Tại sao mảng không có độ dài thay đổi là một phần của tiêu chuẩn C ++?


326

Tôi đã không sử dụng C rất nhiều trong vài năm qua. Khi tôi đọc câu hỏi này ngày hôm nay, tôi đã bắt gặp một số cú pháp C mà tôi không quen thuộc.

Rõ ràng trong C99 , cú pháp sau đây là hợp lệ:

void foo(int n) {
    int values[n]; //Declare a variable length array
}

Đây có vẻ là một tính năng khá hữu ích. Đã bao giờ có một cuộc thảo luận về việc thêm nó vào tiêu chuẩn C ++, và nếu vậy, tại sao nó lại bị bỏ qua?

Một số lý do tiềm năng:

  • Lông cho các nhà cung cấp trình biên dịch để thực hiện
  • Không tương thích với một số phần khác của tiêu chuẩn
  • Chức năng có thể được mô phỏng với các cấu trúc C ++ khác

Tiêu chuẩn C ++ nói rằng kích thước mảng phải là biểu thức không đổi (8.3.4.1).

Vâng, tất nhiên tôi nhận ra rằng trong ví dụ đồ chơi người ta có thể sử dụng std::vector<int> values(m);, nhưng điều này phân bổ bộ nhớ từ heap chứ không phải stack. Và nếu tôi muốn một mảng nhiều chiều như:

void foo(int x, int y, int z) {
    int values[x][y][z]; // Declare a variable length array
}

các vectorphiên bản trở nên vụng về khá:

void foo(int x, int y, int z) {
    vector< vector< vector<int> > > values( /* Really painful expression here. */);
}

Các lát, hàng và cột cũng có khả năng được trải đều trên bộ nhớ.

Nhìn vào cuộc thảo luận comp.std.c++rõ ràng rằng câu hỏi này gây tranh cãi khá nhiều với một số tên rất nặng ở cả hai phía của cuộc tranh luận. Rõ ràng là không phải std::vectorlà một giải pháp tốt hơn.


3
Vì tò mò, tại sao nó cần phải được phân bổ trên ngăn xếp? Bạn có sợ các vấn đề hiệu suất phân bổ heap?
Dimitri C.

32
@Dimitri Không thực sự, nhưng không thể phủ nhận rằng phân bổ ngăn xếp sẽ nhanh hơn phân bổ heap. Và trong một số trường hợp điều này có thể quan trọng.
Andreas Brinck

11
Ưu điểm chính của các mảng có độ dài thay đổi là tất cả các dữ liệu gần nhau, vì vậy khi bạn lặp qua mảng này, bạn đọc và ghi các byte cạnh nhau. Dữ liệu của bạn được tìm nạp vào bộ đệm và cpu có thể hoạt động trên nó mà không cần tìm nạp và gửi các byte đến / từ bộ nhớ.
Calmarius

4
Các mảng có chiều dài thay đổi cũng có thể được sử dụng để thay thế các hằng tiền xử lý bằng các biến const tĩnh. Ngoài ra, trong C, bạn không có các tùy chọn khác cho VLA và đôi khi cần phải viết mã C / C ++ di động (tương thích với cả hai trình biên dịch).
Yury

2
như một bên, nó xuất hiện clang ++ cho phép VLAs.
dùng3426763

Câu trả lời:


204

Gần đây có một cuộc thảo luận về điều này đã khởi động trong usenet: Tại sao không có VLAs trong C ++ 0x .

Tôi đồng ý với những người dường như đồng ý rằng việc phải tạo ra một mảng lớn tiềm năng trên ngăn xếp, thường chỉ có ít không gian có sẵn, là không tốt. Đối số là, nếu bạn biết kích thước trước, bạn có thể sử dụng một mảng tĩnh. Và nếu bạn không biết kích thước trước đó, bạn sẽ viết mã không an toàn.

Các VLA C99 có thể mang lại một lợi ích nhỏ là có thể tạo các mảng nhỏ mà không lãng phí không gian hoặc gọi các hàm tạo cho các phần tử không được sử dụng, nhưng chúng sẽ đưa ra các thay đổi khá lớn cho hệ thống loại (bạn cần có thể chỉ định các loại tùy thuộc vào giá trị thời gian chạy - điều này chưa tồn tại trong C ++ hiện tại, ngoại trừ các newbộ chỉ định loại toán tử, nhưng chúng được xử lý đặc biệt, do đó thời gian chạy không thoát khỏi phạm vi của newtoán tử).

Bạn có thể sử dụng std::vector, nhưng nó không hoàn toàn giống nhau, vì nó sử dụng bộ nhớ động và khiến nó sử dụng bộ cấp phát ngăn xếp của chính mình không dễ dàng (căn chỉnh cũng là một vấn đề). Nó cũng không giải quyết được vấn đề tương tự, bởi vì một vectơ là một thùng chứa có thể thay đổi kích thước, trong khi các VLA có kích thước cố định. Đề xuất C ++ Dynamic Array nhằm giới thiệu một giải pháp dựa trên thư viện, thay thế cho VLA dựa trên ngôn ngữ. Tuy nhiên, nó sẽ không phải là một phần của C ++ 0x, theo như tôi biết.


22
+1 và được chấp nhận. Mặc dù vậy, một nhận xét, tôi nghĩ rằng đối số an toàn là một chút yếu vì có rất nhiều cách khác để gây ra tràn ngăn xếp. Đối số an toàn có thể được sử dụng để hỗ trợ vị trí mà bạn không bao giờ nên sử dụng đệ quy và bạn nên phân bổ tất cả các đối tượng từ heap.
Andreas Brinck

17
Vì vậy, bạn đang nói rằng bởi vì có nhiều cách khác để gây ra tràn ngăn xếp, chúng tôi cũng có thể khuyến khích nhiều người trong số họ?
jalf

3
@Andreas, đồng ý về điểm yếu. Nhưng để đệ quy, phải mất một số lượng lớn các cuộc gọi cho đến khi stack bị ăn hết và nếu đó có thể là trường hợp, mọi người sẽ sử dụng phép lặp. Tuy nhiên, như một số người trong chủ đề usenet nói, đây không phải là một đối số chống lại VLAs trong mọi trường hợp, vì đôi khi bạn chắc chắn có thể biết giới hạn trên. Nhưng trong những trường hợp đó, từ những gì tôi thấy một mảng tĩnh có thể là đủ, vì dù sao nó cũng sẽ không lãng phí nhiều không gian (nếu , thì bạn thực sự sẽ phải hỏi liệu diện tích ngăn xếp có đủ lớn nữa không).
Julian Schaub - litb

10
Cũng xem câu trả lời của Matt Austern trong chủ đề đó: Đặc tả ngôn ngữ của VLAs có thể phức tạp hơn đáng kể đối với C ++, vì loại khớp chặt chẽ hơn trong C ++ (ví dụ: C cho phép gán T(*)[]a T(*)[N]- trong C ++, điều này không được phép, vì C ++ không biết về "khả năng tương thích loại" - nó yêu cầu khớp chính xác), tham số loại, ngoại lệ, cấu hình và hàm hủy và nội dung. Tôi không chắc liệu các lợi ích của VLAs có thực sự trả hết cho công việc đó hay không. Nhưng sau đó, tôi chưa bao giờ sử dụng VLAs trong cuộc sống thực, vì vậy tôi có thể không biết các trường hợp sử dụng tốt cho chúng.
Julian Schaub - litb

1
@AHelps: Có lẽ thứ tốt nhất cho loại đó sẽ là loại hoạt động hơi giống vectornhưng yêu cầu mẫu sử dụng LIFO cố định và duy trì một hoặc nhiều bộ đệm được phân bổ tĩnh cho mỗi luồng thường có kích thước theo tổng phân bổ lớn nhất mà luồng có từng được sử dụng, nhưng có thể được cắt tỉa rõ ràng. Một "phân bổ" bình thường sẽ trong trường hợp phổ biến không đòi hỏi gì nhiều hơn một bản sao con trỏ, phép trừ con trỏ từ con trỏ, so sánh số nguyên và thêm con trỏ; phân bổ đơn giản sẽ yêu cầu một bản sao con trỏ. Không chậm hơn nhiều so với VLA.
supercat

216

(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 xkhô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 sizeofbiể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 ncho 1000là 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 nkhông phải là hằng số thời gian biên dịch (nghĩa là, nếu Athuộ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 A1tạ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).


15
Tôi đồng ý VLAs chỉ sai. Thay vào đó, việc triển khai rộng rãi hơn và hữu ích hơn nhiều, alloca()đã được chuẩn hóa trong C99. VLAs là những gì xảy ra khi một ủy ban tiêu chuẩn nhảy ra trước các triển khai, thay vì cách khác.
MadSellectist

10
Hệ thống loại được sửa đổi thay đổi là một IMO bổ sung tuyệt vời và không có điểm đạn nào của bạn vi phạm lẽ thường. (1) tiêu chuẩn C không phân biệt giữa "thời gian biên dịch" và "thời gian chạy" vì vậy đây không phải là vấn đề; (2) Đây *là tùy chọn, bạn có thể (và nên) viết int A[][n]; (3) Bạn có thể sử dụng hệ thống loại mà không thực sự khai báo bất kỳ VLA nào. Ví dụ, một hàm có thể chấp nhận mảng có kiểu biến đổi thay đổi và nó có thể được gọi với các mảng 2-D không có VLA có kích thước khác nhau. Tuy nhiên, bạn thực hiện các điểm hợp lệ trong phần sau của bài viết của bạn.
MM

3
"khai báo một VLA có nghĩa là nhai một khối lớn tùy ý của ngăn xếp của bạn. Đây là một ngăn xếp và sự cố ngăn xếp được bảo đảm. sai. Tôi vừa chạy một chương trình VLA với ngăn xếp ít hơn 2GB mà không bị tràn ngăn xếp.
Jeff

3
@Jeff: Giá trị tối đa ntrong trường hợp thử nghiệm của bạn là bao nhiêu và kích thước của ngăn xếp của bạn là bao nhiêu? Tôi khuyên bạn nên thử nhập một giá trị nít nhất bằng kích thước của ngăn xếp của bạn. (Và nếu không có cách nào để người dùng kiểm soát giá trị ntrong chương trình của bạn, thì tôi khuyên bạn chỉ nên truyền giá trị tối đa nthẳng vào khai báo: khai báo int A[1000]hoặc bất cứ điều gì bạn cần. VLAs chỉ cần thiết và chỉ nguy hiểm, khi giá trị tối đa nkhông bị giới hạn bởi bất kỳ hằng số thời gian biên dịch nhỏ nào.)
Quuxplusone

2
Vì alloca () có thể được triển khai bằng cách sử dụng nội tại như vậy, theo định nghĩa đúng là alloca () có thể được thực hiện trên bất kỳ nền tảng nào, như là một hàm chuẩn của trình biên dịch. Không có lý do gì mà trình biên dịch không thể phát hiện phiên bản đầu tiên của alloca () và sắp xếp các loại dấu và bản phát hành được nhúng trong mã, và không có lý do gì trình biên dịch không thể thực hiện alloca () bằng cách sử dụng heap nếu nó không thể được thực hiện với ngăn xếp. Cái gì cứng / không di động là alloca () được triển khai trên trình biên dịch C, để nó hoạt động trên một loạt các trình biên dịch và hệ điều hành.
MadSellectist

26

Bạn luôn có thể sử dụng alloca () để phân bổ bộ nhớ trên ngăn xếp khi chạy, nếu bạn muốn:

void foo (int n)
{
    int *values = (int *)alloca(sizeof(int) * n);
}

Được phân bổ trên ngăn xếp ngụ ý rằng nó sẽ tự động được giải phóng khi ngăn xếp thư giãn.

Lưu ý nhanh: Như đã đề cập trong trang man Mac OS X cho alloca (3), "Hàm alloca () phụ thuộc vào máy và trình biên dịch; việc sử dụng nó không được khuyến khích." Chỉ để bạn biết.


4
Ngoài ra, phạm vi cho alloca () là toàn bộ hàm, không chỉ là khối mã chứa biến. Vì vậy, sử dụng nó bên trong một vòng lặp, nó sẽ liên tục tăng ngăn xếp. Một VLA không có vấn đề này.
sashoalm

3
Tuy nhiên, các VLA có phạm vi của khối kèm theo có nghĩa là chúng ít hữu ích hơn so với alloca () với phạm vi của toàn bộ hàm. Xem xét: if (!p) { p = alloca(strlen(foo)+1); strcpy(p, foo); } Điều này không thể được thực hiện với VLAs, chính xác là do phạm vi khối của chúng.
MadSellectist

1
Điều đó không trả lời câu hỏi tại sao của OP . Hơn nữa, đây là một Cgiải pháp giống như, và không thực sự C++-ish.
Adrian W

13

Trong công việc của mình, tôi đã nhận ra rằng mỗi khi tôi muốn một cái gì đó như mảng tự động có độ dài thay đổi hoặc alloca (), tôi không thực sự quan tâm rằng bộ nhớ nằm trên vật lý cpu, chỉ là nó đến từ một số phân bổ ngăn xếp không phát sinh các chuyến đi chậm đến đống chung. Vì vậy, tôi có một đối tượng trên mỗi luồng sở hữu một số bộ nhớ mà từ đó nó có thể đẩy / bật bộ đệm có kích thước biến. Trên một số nền tảng tôi cho phép điều này phát triển thông qua mmu. Các nền tảng khác có kích thước cố định (thường đi kèm với ngăn xếp cpu kích thước cố định cũng vì không có mmu). Một nền tảng tôi làm việc với (một máy chơi game cầm tay) dù sao cũng có ngăn xếp cpu nhỏ quý giá vì nó nằm trong bộ nhớ nhanh, khan hiếm.

Tôi không nói rằng việc đẩy bộ đệm có kích thước thay đổi lên ngăn xếp cpu là không bao giờ cần thiết. Thành thật tôi đã ngạc nhiên trở lại khi tôi phát hiện ra điều này không chuẩn, vì có vẻ như khái niệm này phù hợp với ngôn ngữ đủ tốt. Đối với tôi, các yêu cầu "kích thước thay đổi" và "phải được định vị vật lý trên ngăn xếp cpu" chưa bao giờ xuất hiện cùng nhau. Đó là về tốc độ, vì vậy tôi đã tạo ra "ngăn xếp song song cho bộ đệm dữ liệu" của riêng mình.


12

Có những tình huống phân bổ bộ nhớ heap rất tốn kém so với các hoạt động được thực hiện. Một ví dụ là toán ma trận. Nếu bạn làm việc với các ma trận nhỏ, hãy nói 5 đến 10 yếu tố và thực hiện nhiều phép thuật, việc sử dụng malloc sẽ rất quan trọng. Đồng thời làm cho kích thước một hằng số thời gian biên dịch có vẻ rất lãng phí và không linh hoạt.

Tôi nghĩ rằng bản thân C ++ không an toàn đến mức đối số "cố gắng không thêm nhiều tính năng không an toàn" không mạnh lắm. Mặt khác, vì C ++ được cho là tính năng ngôn ngữ lập trình hiệu quả nhất thời gian chạy, điều này làm cho nó luôn hữu dụng hơn: Những người viết các chương trình quan trọng về hiệu suất sẽ sử dụng C ++ ở mức độ lớn và họ cần càng nhiều hiệu suất càng tốt. Di chuyển công cụ từ đống sang đống là một khả năng như vậy. Giảm số khối heap là một số khác. Cho phép các VLA là thành viên đối tượng sẽ là một cách để đạt được điều này. Tôi đang làm việc trên một đề nghị như vậy. Nó là một chút phức tạp để thực hiện, thừa nhận, nhưng có vẻ khá khả thi.


12

Có vẻ như nó sẽ có sẵn trong C ++ 14:

https://en.wikipedia.org/wiki/C%2B%2B14#Racer-sized_one_dimensional_arrays

Cập nhật: Nó không được đưa vào C ++ 14.


hấp dẫn. Herb Sutter thảo luận về vấn đề này ở đây theo Mảng động : isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meeting (đây là tài liệu tham khảo cho thông tin wikipedia)
Mặc định

1
"Các mảng và dynarray có kích thước thời gian chạy đã được chuyển sang đặc tả kỹ thuật của Phần mở rộng mảng" đã viết 78.86.152.103 trên Wikipedia vào ngày 18 tháng 1 năm 2014: en.wikipedia.org/w/ (kẻ
strager

10
Wikipedia không phải là tài liệu tham khảo quy phạm :) Đề xuất này không được đưa vào C ++ 14.
MM

2
@ViktorSehr: Trạng thái của bài viết này C ++ 17 là gì?
einpoklum

@einpoklum Không có ý tưởng, sử dụng boost :: container :: static_vector
Viktor Sehr

7

Điều này đã được xem xét để đưa vào C ++ / 1x, nhưng đã bị loại bỏ (đây là một sự điều chỉnh so với những gì tôi đã nói trước đó).

Dù sao nó cũng sẽ ít hữu ích hơn trong C ++ vì chúng ta đã phải std::vectorhoàn thành vai trò này.


42
Không, chúng tôi không, std :: vector không phân bổ dữ liệu trên ngăn xếp. :)
Kos

7
"ngăn xếp" là một chi tiết thực hiện; trình biên dịch có thể phân bổ bộ nhớ từ bất cứ đâu miễn là đảm bảo về tuổi thọ của đối tượng.
MM

1
@MM: Đủ công bằng, nhưng trong thực tế, chúng ta vẫn không thể sử dụng std::vectorthay vì, nói , alloca().
einpoklum

@einpoklum về việc nhận đầu ra chính xác cho chương trình của bạn, bạn có thể. Hiệu suất là vấn đề chất lượng thực hiện
MM

1
@MM chất lượng thực hiện không phải là di động. và nếu bạn không cần hiệu suất, bạn không sử dụng c ++ ở nơi đầu tiên
pal

3

Sử dụng std :: vector cho việc này. Ví dụ:

std::vector<int> values;
values.resize(n);

Bộ nhớ sẽ được phân bổ trên heap, nhưng điều này chỉ giữ một nhược điểm nhỏ về hiệu năng. Hơn nữa, thật khôn ngoan khi không phân bổ các dữ liệu lớn trên ngăn xếp, vì nó khá hạn chế về kích thước.


4
Một ứng dụng chính cho các mảng có chiều dài thay đổi là đánh giá các đa thức bậc tùy ý. Trong trường hợp đó, "nhược điểm hiệu suất nhỏ" của bạn có nghĩa là "mã chạy chậm hơn năm lần trong các trường hợp điển hình". Điều đó không nhỏ.
AHelps

1
Tại sao bạn không đơn giản sử dụng std::vector<int> values(n);? Bằng cách sử dụng resizesau khi xây dựng, bạn đang cấm các loại không di chuyển.
LF

1

C99 cho phép VLA. Và nó đặt ra một số hạn chế về cách khai báo VLA. Để biết chi tiết, tham khảo 6.7.5.2 của tiêu chuẩn. C ++ không cho phép VLA. Nhưng g ++ cho phép nó.


Bạn có thể cung cấp một liên kết đến đoạn tiêu chuẩn mà bạn đang chỉ?
Vincent

0

Mảng như thế này là một phần của C99, nhưng không phải là một phần của C ++ tiêu chuẩn. như những người khác đã nói, một vectơ luôn là một giải pháp tốt hơn nhiều, đó có thể là lý do tại sao các mảng có kích thước thay đổi không nằm trong standatrd C ++ (hoặc trong tiêu chuẩn C ++ 0x được đề xuất).

BTW, đối với các câu hỏi về "tại sao" tiêu chuẩn C ++ là như vậy, nhóm tin Usenet được kiểm duyệt comp.std.c ++ là nơi để đến.


6
-1 Vector không phải lúc nào cũng tốt hơn. Thường thì có. Luôn luôn, không. Nếu bạn chỉ cần một mảng nhỏ, trên một nền tảng có không gian heap chậm và việc triển khai vector của thư viện của bạn sử dụng không gian heap, thì tính năng này có thể sẽ tốt hơn nếu nó tồn tại.
Patrick M

-1

Nếu bạn biết giá trị tại thời điểm biên dịch, bạn có thể làm như sau:

template <int X>
void foo(void)
{
   int values[X];

}

Chỉnh sửa: Bạn có thể tạo một vectơ sử dụng bộ cấp phát ngăn xếp (alloca), vì bộ cấp phát là một tham số mẫu.


18
Nếu bạn biết giá trị tại thời gian biên dịch, bạn hoàn toàn không cần một mẫu. Chỉ cần sử dụng X trực tiếp trong chức năng phi mẫu của bạn.
Rob Kennedy

3
Đôi khi người gọi biết vào thời gian biên dịch và callee thì không, đó là những mẫu nào phù hợp. Tất nhiên, trong trường hợp chung, không ai biết X cho đến khi hết giờ.
Qwertie

Bạn không thể sử dụng alloca trong bộ cấp phát STL - bộ nhớ được cấp phát từ alloca sẽ được giải phóng khi khung ngăn xếp bị hủy - đó là khi phương thức phân bổ bộ nhớ trả về.
Oliver

-5

Tôi có một giải pháp thực sự hiệu quả với tôi. Tôi không muốn phân bổ bộ nhớ vì phân mảnh theo thói quen cần chạy nhiều lần. Câu trả lời là cực kỳ nguy hiểm, vì vậy hãy tự chịu rủi ro khi sử dụng nó, nhưng nó tận dụng lợi thế của việc lắp ráp để dự trữ không gian trên ngăn xếp. Ví dụ của tôi dưới đây sử dụng một mảng ký tự (rõ ràng biến có kích thước khác sẽ cần nhiều bộ nhớ hơn).

void varTest(int iSz)
{
    char *varArray;
    __asm {
        sub esp, iSz       // Create space on the stack for the variable array here
        mov varArray, esp  // save the end of it to our pointer
    }

    // Use the array called varArray here...  

    __asm {
        add esp, iSz       // Variable array is no longer accessible after this point
    } 
}

Các mối nguy hiểm ở đây có rất nhiều nhưng tôi sẽ giải thích một vài điều sau: xây dựng ... cần lắp ráp khác nhau cho cái đó (nhưng một macro có thể giải quyết vấn đề đó). 4. Trình biên dịch cụ thể (có thể gặp khó khăn khi di chuyển giữa các trình biên dịch). Tôi chưa thử nên tôi thực sự không biết.


... Và nếu bạn muốn tự mình thực hiện điều này, có thể sử dụng lớp RAII?
einpoklum

Bạn chỉ có thể sử dụng boost :: container :: static_vector.
Viktor Sehr

Điều này không có tương đương cho các trình biên dịch khác có lắp ráp thô hơn MSVC. VC có thể sẽ hiểu rằng espđã thay đổi và sẽ điều chỉnh các truy cập của nó thành ngăn xếp, nhưng trong ví dụ GCC, bạn sẽ hoàn toàn phá vỡ nó - ít nhất là nếu bạn sử dụng tối ưu hóa và -fomit-frame-pointerđặc biệt.
Ruslan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.