2016-03-23 32 views
22

Tôi đang đọc Stroustrup'sChuyến tham quan C++. Trên trang 9, anh tuyên bố:Kích thước mảng C++ không có biểu thức hằng số

"Kích thước của mảng phải là biểu thức không đổi".

Tuy nhiên, sau đó, trên trang. 16, ông sử dụng mẫu mã sau đây:

void vector vector_init(Vector& v, int s) 
{ 
    v.elem = new double[s]; // Allocate an array of s doubles 
    v.sz = s; 
} 

Đây s không phải là một biểu hiện thường xuyên, vậy làm thế nào được khởi tạo v.elem để new double[s] quy phạm pháp luật?

+2

biểu thức liên tục là bắt buộc đối với các mảng trên ngăn xếp chứ không phải trên vùng heap. http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/ –

+3

Đó là loại ngụ ý bởi phần còn lại của đoạn văn (nhưng nên được viết ra, theo ý kiến ​​của tôi) rằng trích dẫn từ trang 9 đề cập đến * khai báo * của một mảng. – molbdnilo

+3

@GillBates Nó không có gì để làm với nơi mảng được lưu trữ. Nếu mảng là một thành viên của một lớp và một thể hiện của lớp đó được cấp phát động, khai báo mảng vẫn yêu cầu kích thước biểu thức không đổi, mặc dù mảng thực tế là "trên heap". – molbdnilo

Trả lời

11

Khi tạo mảng có bộ nhớ được trình biên dịch quản lý, kích thước của nó phải là hằng số (biên dịch). Đối với ví dụ:

int a[5]; 
const int sz = 7; 
int b[sz] = {0}; 

(Một số ngôn ngữ cho ví dụ: C (C99 trở đi) hỗ trợ kích thước mảng động)

Nếu bạn muốn có một mảng động có kích thước (Ví dụ Đoạn do bạn), bạn cần phải phân bổ bộ nhớ cho chính mình, bạn cũng sẽ cần phải giải phóng nó với delete khi bạn đã hoàn tất. Kích thước của mảng như vậy có thể không phải là const. Hơn nữa, bạn cần phải quản lý bộ nhớ một mình, phân bổ có thể thất bại và các nhà khai thác (ví dụ sizeof) sẽ hoạt động trên con trỏ chứ không phải là mảng.

Trong C++ hiện đại (C++ 11 trở đi), thậm chí vùng chứa stl std::array phải có kích thước không đổi.

39

Có một sự khác biệt giữa phân bổ mảng (tức là những người tạo ra với một biểu thức new[], như new double[s]), mà kiếp phải được quản lý bởi các mã (thông qua delete[]) và tuyên bố mảng, mà kiếp này được quản lý bởi họ phạm vi một mình:

int* p = new int[s]; // allocated array, p has type int* 
int q[10];   // declared array, q has type int[10] 
std::vector<int> u; // has member allocated array 
std::array<int, 5> v; // has member declared array 

Sự khác biệt không dựa trên stack/heap. Một mảng được khai báo có thể được đống phân bổ (ví dụ new array<int,5>) hay không trên stack (ví dụ static double x[100];)

Với một mảng phân bổ, kích thước không phải là một biểu hiện thường xuyên. Kích thước đơn giản sẽ được mã hóa vào khối bộ nhớ được cấp phát bởi bộ cấp phát bằng cách nào đó (ví dụ bốn byte hàng đầu trước khi dữ liệu thực tế bắt đầu) sao cho delete[] tương ứng biết số lượng phần tử cần xóa.

Với tuyên bố mảng (hoặc không được phân bổ mảng, không có/etc new/malloc.), Kích thước phải & dao găm; được mã hóa thành loại, để destructor biết phải làm gì. Các chỉ được phép, khai mảng giữa các ý kiến:

T D[constant-expression_opt]; 

(nơi D là một declarator đó có thể là một tên hoặc khai mảng khác, vv) mảng khai báo không giới hạn vào stack. Lưu ý rằng, để thêm nhầm lẫn, biểu thức hằng số là tùy chọn.

Mảng cung cấp nhiều nguồn gây nhầm lẫn trong C++.Các mảng được phân bổ và khai báo có các quy tắc kích thước khác nhau, các thực hành quản lý khác nhau, nhưng bạn có thể gán một T* cho một trong hai và chúng được lập chỉ mục tương đương. Một mảng được phân bổ một con trỏ (đó là tất cả những gì bạn nhận được), nhưng một mảng được khai báo phân rã thành con trỏ (nhưng một mảng!).


& dagger; Lưu ý rằng có một khái niệm về một mảng chiều dài biến (VLA). Ví dụ: gcc, hỗ trợ chúng dưới dạng tiện ích, nhưng chúng không phải là C++ chuẩn. Nó được đề xuất định kỳ mặc dù, và bạn có thể xem this question để biết thêm thông tin về chúng.

+1

Câu trả lời tốt hơn nhiều so với câu trả lời hay nhất hiện nay. – SergeyA

+0

Có thể bạn cũng có thể thêm thông tin về hành vi của toán tử 'sizeof' (ví dụ' sizeof (p) == sizeof (int *) 'vs' sizeof (q) == 10 * sizeof (int) ') –

+0

@king_nak Tôi không thấy điều đó khi thêm thông tin bổ sung, do các loại đã được liệt kê. Đó là những loại quan trọng. – Barry

4

Các quote

Kích thước của một mảng phải là một biểu hiện thường xuyên.

đang nói về một tuyên bố mảng, chẳng hạn như

double a[EXPR]; 

nơi EXPR phải thực sự là một hằng số hoặc constexpr (C có mảng chiều dài thay đổi, nhưng họ không phải là một phần của chuẩn C++).

Biểu thức bạn đề cập đến như một phản ví dụ,

new double[s] 

không một mảng, mặc dù []. Nó là new-expression và cho ra con trỏ chứ không phải mảng. Bạn không hiển thị định nghĩa của v.elem, nhưng tôi có thể cho biết đó là một con trỏ đến gấp đôi.

Lưu ý từ các cuộc thảo luận liên quan về biểu thức mới mà

Nếu loại là một loại mảng, tất cả các không gian khác hơn là người đầu tiên phải được quy định như dương {một cái gì đó giống như một hằng số tích phân - Chi tiết elided}.

Vì vậy, loại được gọi ở trên là double[s], được cho phép rõ ràng.

Phải thừa nhận rằng sự khác biệt giữa một mảng, và loại mảng truyền cho một biểu thức mới là một chút tinh tế, nhưng bạn không thể conflate họ chỉ vì [], bất kỳ hơn bạn có thể tuyên bố rằng

map["key"] 

vi phạm điều gì đó bằng cách khai báo một mảng có độ dài "key".

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