Bây giờ chúng ta có std :: array, những gì còn lại sử dụng cho các mảng kiểu C?


88

std::arrayvượt trội hơn hẳn so với mảng C. Và ngay cả khi tôi muốn tương tác với mã kế thừa, tôi chỉ có thể sử dụng std::array::data(). Có lý do gì tôi muốn một mảng trường học cũ không?

Câu trả lời:


60

Trừ khi tôi đã bỏ lỡ điều gì đó (tôi đã không theo dõi quá kỹ những thay đổi gần đây nhất trong tiêu chuẩn), hầu hết các công dụng của mảng kiểu C vẫn còn. std::arraycho phép khởi tạo tĩnh, nhưng nó vẫn không tính các bộ khởi tạo cho bạn. Và vì việc sử dụng thực sự duy nhất của mảng kiểu C trước đây std::arraylà cho các bảng được khởi tạo tĩnh dọc theo các dòng:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

sử dụng các hàm mẫu beginvà thông thường end(được sử dụng trong C ++ 11) để lặp lại chúng. Không bao giờ đề cập đến kích thước mà trình biên dịch xác định từ số lượng trình khởi tạo.

CHỈNH SỬA: Một điều khác mà tôi quên mất: chuỗi ký tự vẫn là mảng kiểu C; tức là với loại char[]. Tôi không nghĩ rằng bất cứ ai sẽ loại trừ việc sử dụng các ký tự chuỗi chỉ vì chúng tôi có std::array.


7
Bạn có thể viết một mẫu hàm đa dạng để tạo mảng mà không cần phải chỉ định độ dài.
sang phải

2
Với C ++ 17 Class Template Deduction tự động khấu trừ số lượng bộ khởi tạo được hỗ trợ. Ví dụ: "auto a = std :: array {1, 2, 3};"
Ricky65

Nitpick: các loại xâu làconst char[]
Bulletmagnet

30

Không. Nói thẳng ra. Và trong 30 ký tự.

Tất nhiên, bạn cần mảng C để triển khai std::array, nhưng đó không thực sự là lý do mà người dùng muốn mảng C. Ngoài ra, không, std::arrayhiệu suất không kém so với mảng C có tùy chọn cho quyền truy cập được kiểm tra giới hạn. Và cuối cùng, hoàn toàn hợp lý khi bất kỳ chương trình C ++ nào phụ thuộc vào thư viện Tiêu chuẩn - đó là điểm của nó là Tiêu chuẩn- và nếu bạn không có quyền truy cập vào thư viện Tiêu chuẩn, thì trình biên dịch của bạn không tuân thủ và câu hỏi được gắn thẻ "C ++", không phải "C ++ và những thứ không phải C ++ bỏ sót một nửa thông số kỹ thuật vì họ cảm thấy nó không phù hợp.".


1
Hừm. Điều gì sẽ xảy ra nếu bạn đang viết mã C ++ được gọi từ một ngôn ngữ khác và cần một thứ gì đó được truyền dưới dạng tham số?
asveikau

3
Các triển khai tự do được phép loại bỏ hầu hết tất cả thư viện tiêu chuẩn và vẫn tuân thủ. Tuy nhiên, tôi sẽ nghi ngờ nghiêm trọng về một trình biên dịch không thể triển khai std::arraytrong việc triển khai C ++ 11 tự do.
Dennis Zickefoose

11
Bản nháp cuối cùng của C ++ 0x (Tài liệu N3092) § 1.4.7 "Hai loại triển khai được định nghĩa: được lưu trữ và đích tự do. Đối với triển khai được lưu trữ, Tiêu chuẩn quốc tế này xác định tập hợp các thư viện có sẵn. Triển khai tự do là một trong đó thực thi có thể diễn ra mà không có lợi ích của hệ điều hành và có một tập hợp thư viện được xác định bằng cách triển khai bao gồm một số thư viện hỗ trợ ngôn ngữ nhất định ".. STL không được bao gồm như một thư viện" hỗ trợ ngôn ngữ "trong trình biên dịch độc lập
Earlz

24

Có vẻ như sử dụng mảng đa chiều dễ dàng hơn với mảng C hơn std::array. Ví dụ,

char c_arr[5][6][7];

như trái ngược với

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Cũng do thuộc tính phân rã tự động của mảng C, c_arr[i]trong ví dụ trên sẽ phân rã thành một con trỏ và bạn chỉ cần chuyển các kích thước còn lại dưới dạng thêm hai tham số. Quan điểm của tôi là nó c_arrkhông đắt tiền để sao chép. Tuy nhiên, cpp_arr[i]sẽ rất tốn kém để sao chép.


1
Tuy nhiên, bạn có thể chuyển nhiều chiều arraycho một hàm mà không làm mất thứ nguyên. Và nếu bạn chuyển nó vào một mẫu hàm, thì hàm đó có thể suy ra cả thứ nguyên và kích thước của mỗi thứ nguyên hoặc chỉ một trong hai thứ nguyên. Điều này có thể thú vị đối với các thư viện mẫu khoa học chủ yếu hoạt động trên các kích thước tùy ý.
Sebastian Mach

29
một đơn giản template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;sẽ giải quyết bất kỳ vấn đề nào trong số đó.
Miles Rout

6
Ví dụ của bạn c_arrrất đắt để sao chép! Bạn phải tự cung cấp mã để làm như vậy. Con trỏ mà nó sẽ phân rã gần tương đương với một tham chiếu hơn là một bản sao và bạn có thể sử dụng std::arrayđể chuyển một tham chiếu nếu đó là những gì bạn muốn.
ChetS

1
@MilesRout về mặt kỹ thuật , không nên std::size_tthay thế int? xin lỗi vì nitpicking, nhưng điều này sẽ làm cho nó phổ biến.
robbie

1
@ robbie0630 Vâng, bạn có thể thực hiện size_tnếu bạn muốn, mặc dù tôi không thể tưởng tượng được có nhiều trường hợp mà mảng với hơn 4 tỷ hàng hoặc cột là cần thiết.
Miles Rout

13

Như Sumant đã nói, mảng đa chiều dễ sử dụng hơn rất nhiều với mảng C tích hợp sẵn std::array.

Khi được lồng vào nhau, std::arraycó thể trở nên rất khó đọc và dài dòng không cần thiết.

Ví dụ:

std::array<std::array<int, 3>, 3> arr1; 

so với

char c_arr[3][3]; 

Ngoài ra, hãy lưu ý rằng begin(), end()size()tất cả đều trả về giá trị vô nghĩa khi bạn lồng vào nhau std::array.

Vì những lý do này, tôi đã tạo vùng chứa mảng đa chiều có kích thước cố định của riêng mình array_2darray_3d. Chúng tương tự std::arraynhưng đối với các mảng đa chiều gồm 2 và 3 chiều. Chúng an toàn hơn và không có hiệu suất kém hơn so với mảng đa chiều tích hợp sẵn. Tôi đã không bao gồm một vùng chứa cho các mảng nhiều chiều có kích thước lớn hơn 3 vì chúng không phổ biến. Trong C ++ 0x có thể tạo một phiên bản mẫu đa dạng hỗ trợ một số kích thước tùy ý.

Một ví dụ về biến thể hai chiều:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

Tài liệu đầy đủ có sẵn tại đây:

http://fsma.googlecode.com/files/fsma.html

Bạn có thể tải thư viện tại đây:

http://fsma.googlecode.com/files/fsma.zip


4
Mảng kiểu C có kích thước cố định rất dễ dàng, nhưng nếu bạn muốn thay đổi kích thước thì mọi thứ sẽ trở nên phức tạp. Ví dụ, đã cho arr[x][y], bạn không thể biết liệu arrlà một mảng mảng, một mảng con trỏ, một con trỏ tới một mảng hay một con trỏ tới một con trỏ; tất cả để triển khai đều hợp pháp, tùy thuộc vào nhu cầu của bạn. Và có lẽ hầu hết các trường hợp sử dụng trong thế giới thực cho mảng đa chiều đều yêu cầu kích thước được xác định tại thời điểm chạy.
Keith Thompson

Tôi rất thích thấy sự triển khai mô hình đa dạng của mảng n-chiều! Siêu lập trình tốt nhất của nó!
steffen

3
@steffen Tôi đã cố gắng một vài năm trước. Bạn có thể xem tại đây: code.google.com/p/fsma/source/browse/trunk/… . Trường hợp thử nghiệm tại đây: code.google.com/p/fsma/source/browse/trunk/… . Tôi chắc chắn rằng nó có thể được thực hiện tốt hơn nhiều.
Ricky65

5

Mảng kiểu C có sẵn trong C ++ thực sự kém linh hoạt hơn nhiều so với mảng C thực. Sự khác biệt là trong C, các kiểu mảng có thể có kích thước thời gian chạy . Sau đây là mã C hợp lệ, nhưng nó không thể được thể hiện với các mảng kiểu C ++ hoặc với các kiểu C ++ array<>:

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

Trong C ++, bạn sẽ phải cấp phát mảng tạm thời trên heap:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Điều này không thể đạt được với std::array<>, vì barkhông được biết tại thời điểm biên dịch, nó yêu cầu sử dụng mảng kiểu C trong C ++ hoặc của std::vector<>.


Trong khi ví dụ đầu tiên có thể được diễn đạt tương đối dễ dàng bằng C ++ (mặc dù yêu cầu new[]delete[]), nhưng điều sau không thể đạt được trong C ++ mà không có std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Vấn đề là, các con trỏ đến các mảng dòng int (*)[width]không thể sử dụng độ rộng thời gian chạy trong C ++, điều này làm cho bất kỳ mã thao tác hình ảnh nào trong C ++ phức tạp hơn nhiều so với trong C. Một ví dụ về thao tác hình ảnh trong C ++ điển hình sẽ trông như thế này:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Mã này thực hiện chính xác các tính toán giống như mã C ở trên, nhưng nó cần thực hiện tính toán chỉ mục bằng tay ở bất kỳ nơi nào các chỉ số được sử dụng . Đối với trường hợp 2D, điều này vẫn khả thi (mặc dù nó đi kèm với rất nhiều cơ hội để tính chỉ số sai). Tuy nhiên, nó thực sự khó chịu trong trường hợp 3D.

Tôi thích viết mã bằng C ++. Nhưng bất cứ khi nào tôi cần thao tác dữ liệu đa chiều, tôi thực sự tự hỏi mình có nên chuyển phần mã đó sang C.


7
Cần lưu ý rằng ít nhất Clang và GCC hỗ trợ VLA trong C ++.
Janus Troelsen

@JanusTroelsen và họ cũng bị hạn chế khủng khiếp trong việc hỗ trợ loại phần tử nào.
sang phải

C11 không làm cho VLA tùy chọn? Nếu vậy thì tôi nghĩ câu trả lời của bạn là sai lệch. Sẽ đúng khi C99 là tiêu chuẩn nhưng không phải C11.
Z boson

1
@Zboson C99 là một tiêu chuẩn C và có các trình biên dịch triển khai các tính năng VLA của nó (gcc ví dụ). C11 đã tạo ra khá nhiều thứ thú vị tùy chọn và tôi không nghĩ đó là vì họ muốn loại bỏ tính năng này. Tôi có xu hướng coi đó là một dấu hiệu cho thấy họ muốn hạ thấp trình độ để viết một trình biên dịch tuân thủ tiêu chuẩn hoàn toàn: VLA là một thứ khá khó thực hiện và nhiều mã có thể làm mà không có, vì vậy nó có ý nghĩa đối với một trình biên dịch mới trên một số nền tảng để không phải triển khai VLA ngay lập tức.
cmaster - Khôi phục monica

-1

Có thể std::arraylà không chậm. Nhưng tôi đã thực hiện một số điểm chuẩn bằng cách sử dụng cửa hàng đơn giản và đọc từ std :: array; Xem kết quả điểm chuẩn bên dưới (trên W8.1, VS2013 Cập nhật 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Theo các dấu âm, mã tôi đã sử dụng nằm trong pastebin ( liên kết )

Mã lớp điểm chuẩn ở đây ;

Tôi không biết nhiều về điểm chuẩn ... Mã của tôi có thể thiếu sót


6
Kết quả điểm chuẩn không có mã điểm chuẩn hoặc cờ tổng hợp? Cố lên, bạn có thể làm tốt hơn.
R. Martinho Fernandes

FWIW, chỉ một đoạn mã nhỏ đó đã cho thấy điểm chuẩn bị thiếu sót nghiêm trọng. Một trình biên dịch đủ thông minh sẽ chỉ cần bật toàn bộ điều vàolong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes

Đó chỉ là một ví dụ. Tôi nghĩ nó không có gì to tát. Tôi đã thay đổi mã để trả về void, đã sử dụng bản dựng phát hành trong VS 2013, với / O2 / Ot / Gl.
K'Prime

Loại bỏ giá trị trả về có nghĩa là trình biên dịch có thể biến toàn bộ thành void test_arr_without_init() {}bây giờ. Bạn thực sự cần phải nhảy qua các vòng để đảm bảo mã bạn đang đo là mã bạn muốn đo.
R. Martinho Fernandes

-6
  1. để thực hiện một cái gì đó như std::array
  2. nếu bạn không muốn sử dụng STL hoặc không thể
  3. Đối với hiệu suất

27
Hãy cho tôi biết làm thế nào std::arraysẽ kém hiệu suất hơn một mảng C.
Xeo

2
Từ wikipedia : "Việc triển khai mảng không bắt buộc phải kiểm tra ràng buộc. Tuy nhiên, việc triển khai trong boost thực hiện điều đó đối với toán tử [], nhưng không phải đối với trình vòng lặp." - vì vậy toán tử [] chậm hơn. Tôi chưa xem xét triển khai, nhưng bất kỳ mã nào trong quá trình triển khai đều có thể cản trở trình tối ưu hóa.
Lou Franco

19
@Aaron McDaid: Đó là chỉ trong at(), nó không phải operator[], giống như std::vector. Không có sự sụt giảm hiệu suất hoặc mã bị phình lên std::array, trình biên dịch được thiết kế để tối ưu hóa loại thứ này. Và, tất nhiên, việc bổ sung chức năng đã kiểm tra là một công cụ gỡ lỗi tuyệt vời và là một lợi thế lớn. @Lou Franco: Tất cả mã C ++ có thể phụ thuộc vào thư viện Chuẩn - đó là loại thư viện dùng để làm. @Earlz: Nếu bạn không có sẵn STL, thì đó không phải là C ++, và đó là kết thúc của việc đó.
Puppy

6
@Earlz: Chuẩn C ++ chứa thư viện Chuẩn. Nếu bạn không thể sử dụng thư viện, nó không phù hợp. Và thứ hai, bạn phải có một trình biên dịch khốn nạn để sử dụng std::arraylớn hơn mức sử dụng mảng C tương đương.
Chó con

5
@Earlz: Có một sự khác biệt lớn giữa "không hoàn toàn phù hợp" và "thiếu các tính năng có hàng trăm trang trong đặc điểm kỹ thuật".
Puppy
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.