(Bối cảnh: Tôi có một số kinh nghiệm triển khai trình biên dịch C và C ++.)
Các mảng có chiều dài thay đổi trong C99 về cơ bản là một sai lầm. Để hỗ trợ VLAs, C99 đã phải đưa ra những nhượng bộ sau đây theo lẽ thường:
sizeof x
không còn luôn là hằng số thời gian biên dịch; trình biên dịch đôi khi phải tạo mã để đánh giá một sizeof
biểu hiện khi chạy.
Việc cho phép các VLA hai chiều ( int A[x][y]
) yêu cầu một cú pháp mới để khai báo các hàm lấy các VLAs 2D làm tham số : void foo(int n, int A[][*])
.
Ít quan trọng hơn trong thế giới C ++, nhưng cực kỳ quan trọng đối với đối tượng mục tiêu của các lập trình viên hệ thống nhúng C, tuyên bố một VLA có nghĩa là nhai một khối lớn tùy ý trong ngăn xếp của bạn. Đây là một ngăn xếp tràn và đảm bảo sự cố. (Bất cứ lúc nào bạn khai báo int A[n]
, bạn đang ngầm khẳng định rằng bạn có 2GB ngăn xếp để phụ tùng. Xét cho cùng, nếu bạn biết n
"sau đó bạn chỉ có thể tuyên bố chắc chắn ít hơn 1000 ở đây là" int A[1000]
. Thay các số nguyên 32-bit n
cho 1000
là một sự thừa nhận rằng bạn không biết hành vi của chương trình của bạn phải là gì.)
Được rồi, vậy bây giờ hãy chuyển sang nói về C ++. Trong C ++, chúng tôi có sự phân biệt mạnh mẽ giống nhau giữa "hệ thống loại" và "hệ thống giá trị" mà C89 thực hiện nhưng chúng tôi thực sự bắt đầu dựa vào nó theo cách mà C không có. Ví dụ:
template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s; // equivalently, S<int[n]> s;
Nếu n
không phải là hằng số thời gian biên dịch (nghĩa là, nếu A
thuộc loại biến đổi thay đổi), thì cái gì trên trái đất sẽ là loại S
? Would S
's loại cũng được xác định duy nhất trong thời gian chạy?
Cái này thì sao:
template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);
Trình biên dịch phải tạo mã cho một số khởi tạo của myfunc
. Mã đó trông như thế nào? Làm thế nào chúng ta có thể tạo mã đó một cách tĩnh, nếu chúng ta không biết loại A1
tại thời điểm biên dịch?
Tệ hơn, nếu nó bật ra trong thời gian chạy đó n1 != n2
, thì sao !std::is_same<decltype(A1), decltype(A2)>()
? Trong trường hợp đó, lệnh gọi myfunc
thậm chí không nên biên dịch , vì việc khấu trừ kiểu mẫu sẽ thất bại! Làm thế nào chúng ta có thể mô phỏng hành vi đó trong thời gian chạy?
Về cơ bản, C ++ đang đi theo hướng đẩy ngày càng nhiều quyết định vào thời gian biên dịch : tạo mã mẫu, constexpr
đánh giá hàm, v.v. Trong khi đó, C99 đang bận rộn đẩy các quyết định biên dịch theo thời gian truyền thống (ví dụ sizeof
) vào thời gian chạy . Với suy nghĩ này, nó thực sự có ý nghĩa khi dành bất kỳ nỗ lực nào để cố gắng tích hợp các VLA kiểu C99 vào C ++?
Như mọi người trả lời khác đã chỉ ra, C ++ cung cấp rất nhiều cơ chế phân bổ heap ( std::unique_ptr<int[]> A = new int[n];
hoặc std::vector<int> A(n);
là cơ chế rõ ràng) khi bạn thực sự muốn truyền đạt ý tưởng "Tôi không biết tôi có thể cần bao nhiêu RAM." Và C ++ cung cấp một mô hình xử lý ngoại lệ tiện lợi để xử lý tình huống không thể tránh khỏi rằng dung lượng RAM bạn cần lớn hơn dung lượng RAM bạn có. Nhưng hy vọng câu trả lời này cung cấp cho bạn một ý tưởng tốt về lý do tại sao VLAs kiểu C99 không phù hợp với C ++ - và thậm chí không thực sự phù hợp với C99. ;)
Để biết thêm về chủ đề này, hãy xem N3810 "Giải pháp thay thế cho phần mở rộng mảng" , bài viết của Bjarne Stroustrup vào tháng 10 năm 2013 về VLAs. POV của Bjarne rất khác với POV của tôi; N3810 tập trung nhiều hơn vào việc tìm kiếm một cú pháp ish C ++ tốt cho mọi thứ và không khuyến khích việc sử dụng các mảng thô trong C ++, trong khi tôi tập trung nhiều hơn vào ý nghĩa của siêu lập trình và hệ thống kiểu. Tôi không biết liệu anh ta xem xét hàm ý siêu hệ thống / kiểu hệ thống đã được giải quyết, có thể giải quyết được hay chỉ đơn giản là không quan tâm.
Một bài đăng blog hay đạt được nhiều điểm giống nhau là "Sử dụng hợp pháp các mảng có chiều dài thay đổi" (Chris Wellons, 2019-10-27).