2012-01-14 32 views
8

Điều này đã gây phiền hà cho tôi một thời gian. Nó đi vào trung tâm của sự hiểu biết của tôi (thiếu) về sự khác biệt giữa phân bổ bộ nhớ tĩnh và động. Mảng sau đây là một mảng tĩnh bình thường, có nghĩa là bộ nhớ được cấp phát trong thời gian biên dịch, đúng không? Tuy nhiên, tôi đã thiết lập nó để người dùng nhập kích thước mảng vào thời gian chạy.Mảng là tĩnh, nhưng kích thước mảng không biết cho đến khi chạy. Sao có thể như thế được?

#include <iostream> 
using namespace std; 

int main() { 
    cout << "how many elements should the array hold? "; 
    int arraySize; 
    cin >> arraySize; 

    int arr[arraySize]; 

    for (int i = 0; i < arraySize; ++i) 
    arr[i] = i * 2; 

    return 0; 
} 

Lưu ý rằng không có nhà điều hành new hoặc delete nào trong chương trình này. Nó hoạt động tốt trong Xcode 4.2 (trình biên dịch Clang mặc định) cũng như máy chủ UNIX của trường tôi (GCC 4.4.5). Trình biên dịch biết bao nhiêu bộ nhớ để phân bổ cho arr khi mảng được tạo tại thời gian biên dịch? Đây có phải chỉ là một fluke của trình biên dịch của tôi, mã nguy hiểm có thể làm hỏng bộ nhớ khác, hoặc là legit này?

+1

này sử dụng một tính năng gọi là _variable dài arrays_, được ra mắt tại C99. –

+3

Thử biên dịch bằng 'g ++ -Wall -Wextra -pedantic -std = C++ 98' –

Trả lời

8

Đây là phần mở rộng không chuẩn của trình biên dịch C++ của bạn. Lưu ý rằng trong C, không giống như trong C++, điều này được hỗ trợ chính thức (tức là hành vi bắt buộc chuẩn) kể từ C99. Trong C++, nó không được hỗ trợ vì đã có giải pháp cho vấn đề: Sử dụng std::vector thay vì mảng.

Tuy nhiên, mảng không phải là không sử dụng phân bổ bộ nhớ tĩnh (cũng như cấp phát bộ nhớ động), nhưng phân bổ bộ nhớ tự động. Các biến tự động được tự động deallocated ở phần cuối của hàm (vùng bộ nhớ mà chúng được cấp phát được gọi là stack, bởi vì các phân bổ và deallocations trên nó có chứa các ngữ nghĩa). Để có mảng sử dụng phân bổ bộ nhớ tĩnh, bạn sẽ phải đặt static trước định nghĩa (lưu ý rằng các biến trong phạm vi toàn cầu hoặc không gian tên luôn sử dụng phân bổ bộ nhớ tĩnh). Tuy nhiên, nếu bạn tạo biến tĩnh, bạn sẽ thấy rằng trình biên dịch không cho phép sử dụng kích thước mảng không liên tục nữa.

Lưu ý rằng std::vector lưu trữ dữ liệu của nó bằng phân bổ bộ nhớ động thay thế. Vì lý do đó, bạn cũng có thể sử dụng kích thước không cố định ngay cả đối với static std::vector s.

+0

Lý do chính mà nó không được phép không phải là có sự thay thế của vector. Có những lý do sâu sắc hơn, vì thực tế là kích thước của một mảng là một phần của loại và nó phải được biết tại thời gian biên dịch. –

+0

@ DavidRodríguez-dribeas: Họ có thể đã đưa ra ngoại lệ đối với các quy tắc đó, cũng như các mảng chiều dài biến C cần các ngoại lệ đối với các quy tắc của C. – celtschk

0

Đây là một Variable Length Array (chỉ được hỗ trợ trong C99 chứ không phải trong C++). Nó được cấp phát trên stack khi chạy.

+0

Đây có phải là C++ hợp lệ không? IIRC Visual Studio đã có một số erros về việc này. – stativ

+0

stativ là chính xác; đây không phải là C++. –

+0

Không, nó không phải là cấp phát bộ nhớ động. Đó là phân bổ bộ nhớ tự động. @stativ: Không, nó không hợp lệ C++ (nó sẽ là hợp lệ C nếu không được bao quanh bởi mã C++ cụ thể, tuy nhiên). Đối với C++, đó là phần mở rộng không chuẩn của một số trình biên dịch. – celtschk

1

Mã được tạo phân bổ arraySize byte trên ngăn xếp khi chạy. Khi hàm trả về, ngăn xếp thư giãn, bao gồm "trả lại" các byte được phân bổ trên nó cho mảng.

Sử dụng mới và xóa là để phân bổ dung lượng trên heap. Thời gian bộ nhớ được cấp phát trên heap độc lập với bất kỳ phạm vi hàm hoặc phương thức nào - Nếu bạn phân bổ không gian trên nó trong một hàm và hàm trả về, bộ nhớ vẫn được cấp phát và hợp lệ.

4

Đối với một mảng (hoặc bất kỳ đối tượng nào) được khai báo bên trong một hàm, bộ nhớ được cấp phát khi nhập vào hàm (thường là trên ngăn xếp) và deallocated khi hàm trả về. Thực tế là chức năng xảy ra là main trong trường hợp này không ảnh hưởng đến điều đó.

này:

cin >> arraySize; 
int arr[arraySize]; 

là một "mảng chiều dài thay đổi" (VLA). Vấn đề là, C++ không hỗ trợ VLAs. C, bắt đầu với tiêu chuẩn ISO C 1999 (C99), nhưng nó không phải là một tính năng mà C++ đã áp dụng.

Trình biên dịch của bạn hỗ trợ VLAs trong C++ dưới dạng tiện ích mở rộng. Sử dụng chúng làm cho mã của bạn không thể di chuyển được.

(Một vấn đề với VLAs là không có cơ chế phát hiện lỗi phân bổ; nếu arraySize quá lớn, hành vi của chương trình không xác định).

Đối với gcc, biên soạn với -pedantic sẽ tạo ra một cảnh báo:

warning: ISO C++ forbids variable length array ‘arr’ 
Các vấn đề liên quan