2013-09-02 23 views
7

Nếu tôi xác định một mảng trong câu lệnh if thì bộ nhớ sẽ được cấp phát trong thời gian biên dịch ví dụ.Nếu tôi xác định một mảng trong câu lệnh if thì bộ nhớ có được cấp phát không?

if(1) 
{ 
    int a[1000]; 
} 
else 
{ 
    float b[1000]; 
} 

Sau đó, một ký ức về 2 * 1000 cho ints + 4 * 1000 cho nổi được phân bổ?

+3

Tại sao không xem mã trình biên dịch của bạn tạo ra để tìm hiểu? –

+4

@CarlNorum có thể vì không phải ai cũng biết lắp ráp? – Kolyunya

+0

Trong trường hợp này đó không thực sự là một câu trả lời hay. Bạn sẽ phải học nếu bạn muốn biết những gì đang xảy ra. Hành vi ở đây không thể được xác định bằng cách đoán dựa trên một tiêu chuẩn ngôn ngữ, vì ngôn ngữ không chỉ định nó đủ tốt.Ít nhất, phá vỡ ABI và/hoặc tiêu chuẩn gọi thủ tục là cần thiết. –

Trả lời

4

Nó được dành riêng trên ngăn xếp tại thời gian chạy (giả sử một điều kiện không tầm thường - trong trường hợp của bạn, trình biên dịch sẽ loại trừ phần else). Điều đó có nghĩa là nó chỉ tồn tại bên trong khối phạm vi (giữa {}).

+1

Có thể là ** thời gian chạy ** hoặc ** thời gian biên dịch ** không? Phân bổ bộ nhớ tĩnh được thực hiện tại thời gian biên dịch. –

+3

@nishant: bộ nhớ cần thiết được tính toán tại thời gian biên dịch, nhưng nó thực sự được cấp phát khi chạy, ở đầu hàm. – DCoder

+0

@DCoder: Cảm ơn bạn đã giải quyết sự nhầm lẫn của tôi. Ngoài ra tôi đã sửa chữa câu trả lời của tôi. –

0

Như DCoder & lúa chỉnh tôi, bộ nhớ sẽ được tính tại thời gian biên dịch nhưng được phân bổ tại thời gian chạy trong phân khúc ngăn xếp bộ nhớ, nhưng với phạm vi & đời của khối, trong đó mảng được định nghĩa. Kích thước bộ nhớ được cấp phụ thuộc vào kích thước của int & float trong hệ thống của bạn. Đọc this for an overview on C memory map

+0

Lưu ý rằng không có thứ gì như "bản đồ bộ nhớ C". Có lẽ họ có nghĩa là "bản đồ bộ nhớ máy tính" nhưng quên 'P'. – Lundin

0

Bộ nhớ cho mảng trong khối if sẽ được cấp phát trên ngăn xếp trong thời gian chạy. else phần sẽ được tối ưu hóa (gỡ bỏ) bởi trình biên dịch. Để biết thêm về nơi các biến sẽ được cấp phát bộ nhớ, hãy xem Segmentation Fault when writing to a string

1

Trong ví dụ của bạn, chỉ bộ nhớ cho các int được phân bổ trên ngăn xếp (1000 * sizeof (int)).

Như bạn có thể đoán, điều này đang diễn ra vào thời gian chạy. Mã được tạo ra có các hướng dẫn để phân bổ không gian trên ngăn xếp khi mã khối tương ứng được nhập vào.

Hãy nhớ rằng điều này đang xảy ra do ngữ nghĩa của ngôn ngữ. Cấu trúc khối giới thiệu một phạm vi mới và bất kỳ biến tự động nào được phân bổ trong phạm vi đó đều có thời lượng tồn tại miễn là phạm vi thực hiện. Trong C, điều này được thực hiện bằng cách phân bổ nó trên stack, mà sụp đổ khi phạm vi biến mất.

Chỉ cần lái xe về nhà điểm, lưu ý rằng phân bổ sẽ khác nhau có các biến có tính chất khác nhau.

if(1) 
{ 
    static int a[1000]; 
} 
else 
{ 
    static float b[1000]; 
} 

Trong trường hợp này, khoảng trống được phân bổ cho cả ints và phao. Tuổi thọ của các biến này là chương trình. Nhưng khả năng hiển thị là trong phạm vi khối họ được bố trí trong.

+0

Một số ABI yêu cầu con trỏ ngăn xếp chỉ bị giảm một lần cho mỗi lần gọi hàm để hỗ trợ mã mở thư ngoại lệ. –

+0

Điểm tốt. Nhưng đó là những chi tiết cụ thể. Đối với những vấn đề như thế này, người ta sẽ đi theo ngữ nghĩa của ngôn ngữ, bạn không nghĩ sao? – Ziffusion

+0

Vâng, bạn không thể trả lời câu hỏi có hành vi được xác định bằng cách nói về những gì tiêu chuẩn nói. –

1

Phạm vi

Biến khai báo trong phạm vi của một cặp { } là trên stack. Điều này áp dụng cho các biến được khai báo ở đầu hàm hoặc trong bất kỳ cặp nào { } trong hàm.

int myfunc() 
{ 
    int i = 0; // On the stack, scoped: myfunc 

    printf("%i\n"); 

    if (1) 
    { 
     int j = 1; // On the stack, scope: this if statement 

     printf("%i %i\n",i,j); 
    } 

    printf("%i %i\n",i,j); // Won't work, no j 
} 

Những ngày này phạm vi của các biến được giới hạn ở xung quanh { }. Tôi nhớ lại rằng một số trình biên dịch Microsoft cũ hơn không giới hạn phạm vi, và trong ví dụ trên, printf() cuối cùng sẽ biên dịch.

Vậy nó ở đâu trong bộ nhớ?

Bộ nhớ ij chỉ được đặt trước trên ngăn xếp. Điều này không giống như phân bổ bộ nhớ được thực hiện với malloc(). Điều đó rất quan trọng, bởi vì gọi số malloc() rất chậm so sánh. Ngoài ra với bộ nhớ được phân bổ động bằng cách sử dụng malloc() bạn phải gọi free().

Có hiệu lực trình biên dịch biết trước thời gian cần thiết cho các biến của hàm và sẽ tạo mã liên quan đến bộ nhớ liên quan đến bất kỳ con trỏ ngăn xếp nào khi được gọi là myfunc(). Vì vậy, miễn là ngăn xếp là đủ lớn (2MBytes bình thường, phụ thuộc vào hệ điều hành), tất cả là tốt.

Tràn ngăn xảy ra trong trường hợp myfunc() được gọi với con trỏ ngăn xếp đã gần cuối ngăn xếp (tức là myfunc() được gọi bởi một hàm mà người khác gọi là , vv Mỗi lớp của các cuộc gọi lồng nhau đến các hàm di chuyển con trỏ ngăn xếp nhiều hơn một chút và chỉ được chuyển trở lại khi các hàm trả về).

Nếu khoảng cách giữa ngăn xếp ngăn xếp và cuối ngăn xếp không đủ lớn để giữ tất cả các biến được khai báo trong myfunc(), mã cho myfunc() sẽ chỉ đơn giản cố gắng sử dụng các vị trí ngoài cuối ngăn xếp. Điều đó gần như luôn luôn là một điều xấu, và chính xác như thế nào xấu và khó khăn như thế nào để nhận thấy rằng một cái gì đó đã đi sai phụ thuộc vào hệ điều hành. Trên bộ điều khiển nhúng nhỏ, nó có thể là một cơn ác mộng vì nó thường có nghĩa là một phần khác của dữ liệu của chương trình (ví dụ như biến toàn cục) bị ghi đè âm thầm và có thể rất khó gỡ lỗi. Trên các hệ thống lớn hơn (Linux, Windows), hệ điều hành sẽ cho bạn biết những gì đã xảy ra hoặc sẽ chỉ làm cho ngăn xếp lớn hơn.

cân nhắc Runtime Hiệu quả

Trong ví dụ trên tôi gán giá trị cho ij. Điều này thực sự mất một số lượng nhỏ thời gian chạy. j được chỉ định 1 sau khi đánh giá tuyên bố if và chi nhánh tiếp theo vào nơi j được khai báo.

Nói ví dụ câu lệnh if đã không được đánh giá là đúng; trong trường hợp đó j không bao giờ được gán 1. Nếu j được khai báo ở đầu myfunc() thì nó sẽ luôn được gán giá trị là 1 bất kể câu lệnh if có đúng hay không - một phần nhỏ thời gian. Nhưng hãy xem xét một ví dụ ít tầm thường hơn khi một mảng lớn được khai báo là đã khởi tạo; sẽ mất nhiều thời gian hơn.

int myfunc() 
{ 
    int i = 0;   // On the stack, scoped: myfunc 
    int k[10000] = {0} // On the stack, scoped: myfunc. A complete waste of time 
         // when the if statement evaluates to false. 

    printf("%i\n"); 

    if (0) 
    { 
     int j = 1; // On the stack, scope: this if statement 

     // It would be better to move the declaration of k to here 
     // so that it is initialised only when the if evaluates to true. 

     printf("%i %i %i\n",i,j,k[500]); 

    } 

    printf("%i %i\n",i,j); // Won't work, no j 
} 

Đặt tuyên bố k ở đầu myfunc() có nghĩa là một vòng lặp 10.000 dài được thực hiện để khởi k mỗi lần myfunc() được gọi. Tuy nhiên nó không bao giờ được sử dụng, vì vậy vòng lặp đó là một sự lãng phí thời gian hoàn toàn. Tất nhiên, trong những trình biên dịch ví dụ tầm thường này sẽ tối ưu hóa mã không cần thiết, vv. Trong mã thực mà trình biên dịch không thể dự đoán trước thời gian thực hiện dòng chảy thì mọi thứ sẽ được đặt đúng vị trí.

Các vấn đề liên quan