91

std::array is vastly superior to the C arrays. And even if I want to interoperate with legacy code, I can just use std::array::data(). Is there any reason I would ever want an old-school array?

Jarod42
  • 190,553
  • 13
  • 166
  • 271
R. Martinho Fernandes
  • 219,040
  • 71
  • 423
  • 503
  • Note that `` is a heavy standard library header which can increase compilation time a lot (varies between std lib implementations), whereas a C-style array does not require any includes/imports as it is part of the core language/syntax. – Matthias Aug 14 '21 at 18:03

7 Answers7

61

Unless I've missed something (I've not followed the most recent changes in the standard too closely), most of the uses of C style arrays still remain. std::array does allow static initialization, but it still won't count the initializers for you. And since the only real use of C style arrays before std::array was for statically initialized tables along the lines of:

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

using the usual begin and end template functions (adopted in C++11) to iterate over them. Without ever mentionning the size, which the compiler determines from the number of initializers.

EDIT: Another thing I forgot: string literals are still C style arrays; i.e. with type char[]. I don't think that anyone would exclude using string literals just because we have std::array.

Nils von Barth
  • 2,809
  • 1
  • 23
  • 24
James Kanze
  • 146,674
  • 16
  • 175
  • 326
  • 7
    You can write a variadic function template which constructs arrays without you having to specify the length. –  Apr 22 '15 at 12:37
  • 2
    With C++17 Class Template Deduction automatic deduction of the number of initializers is supported. For example, "auto a = std::array{1, 2, 3};" – Ricky65 May 10 '17 at 16:54
  • Nitpick: the type of string literals is `const char[]` – Bulletmagnet Jul 10 '19 at 14:13
33

No. To, uh, put it bluntly. And in 30 characters.

Of course, you need C arrays to implement std::array, but that's not really a reason that a user would ever want C arrays. In addition, no, std::array is not less performant than a C array, and has an option for a bounds-checked access. And finally, it is completely reasonable for any C++ program to depend on the Standard library- that's kind of the point of it being Standard- and if you don't have access to a Standard library, then your compiler is non-conformant and the question is tagged "C++", not "C++ and those not-C++ things that miss out half the specification because they felt it inappropriate.".

Puppy
  • 141,834
  • 35
  • 244
  • 454
  • 1
    Hm. What if you're writing C++ code that gets called from another language and needs something to be passed as a parameter? – asveikau May 24 '11 at 16:00
  • 3
    Freestanding implementations are allowed to leave out almost all of the standard library and still be compliant. I would have serious doubts about a compiler that couldn't implement `std::array` in a freestanding C++11 implementation, though. – Dennis Zickefoose May 24 '11 at 16:54
  • 11
    C++0x Final Draft (Document N3092) § 1.4.7 "Two kinds of implementations are defined: hosted and freestanding. For a hosted implementation, this International Standard defines the set of available libraries. A freestanding implementation is one in which execution may take place without the benefit of an operating system, and has an implementation-defined set of libraries that includes certain language-support libraries" .. The STL is not included as a "language-support" library in a freestanding compiler – Earlz May 24 '11 at 18:03
24

Seems like using multi-dimensional arrays is easier with C arrays than std::array. For instance,

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

as opposed to

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

Also due to the automatic decay property of C arrays, c_arr[i] in the above example will decay to a pointer and you just need to pass the remaining dimensions as two more parameters. My point is it c_arr is not expensive to copy. However, cpp_arr[i] will be very costly to copy.

Michael Mrozek
  • 161,243
  • 28
  • 165
  • 171
Sumant
  • 4,206
  • 1
  • 22
  • 30
  • 1
    However, you could pass a multidimensional `array` to a function without loosing dimensions. And if you pass it to a function template, then that function could deduce both the dimension and the size of each dimension, or just one of them two. This might be interesting for scientific template libraries that principally work on arbitrary dimensions. – Sebastian Mach Sep 01 '11 at 09:55
  • 32
    a simple `template using array2d = std::array, M>;` should solve any of those issues. – Miles Rout Mar 16 '13 at 23:54
  • also if have atomic in the mix :) – NoSenseEtAl May 21 '13 at 20:30
  • 8
    Your example `c_arr` is _very_ expensive to copy! You have to provide the code to do so yourself. The pointer it will decay to is a closer equivalent to a reference than a copy and you can use `std::array` to pass a reference if that is what you want. – ChetS May 21 '14 at 18:10
  • 1
    @MilesRout *technically*, shouldn't that be `std::size_t` instead of `int`? sorry for nitpicking, but this would make it universal. – robbie Feb 12 '17 at 21:09
  • 1
    @robbie0630 Yeah you can make it `size_t` if you want, although I can't imagine there are many scenarios where arrays with more than 4 billion rows or columns are necessary. – Miles Rout Feb 12 '17 at 21:15
  • @MilesRout, int can be 16 bits on some architecture. And yes you can have an array of more than 32768 elements. size_t is generally 32 bits on the same architecture and it's unsigned. – xryl669 Mar 15 '22 at 18:03
14

As Sumant said, multi-dimensional arrays are a lot easier to use with built in C-arrays than with std::array.

When nested, std::array can become very hard to read and unnecessarily verbose.

For example:

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

compared to

char c_arr[3][3]; 

Also, note that begin(), end() and size() all return meaningless values when you nest std::array.

For these reasons I've created my own fixed size multidimensional array containers, array_2d and array_3d. They are analogous to std::array but for multidimensional arrays of 2 and 3 dimensions. They are safer and have no worse performance than built-in multidimensional arrays. I didn't include a container for multidimensional arrays with dimensions greater than 3 as they are uncommon. In C++0x a variadic template version could be made which supports an arbitrary number of dimensions.

An example of the two-dimensional variant:

//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 }
}};

Full documentation is available here:

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

You can download the library here:

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

Jarod42
  • 190,553
  • 13
  • 166
  • 271
Ricky65
  • 1,627
  • 18
  • 22
  • 4
    Fixed-size C-style arrays are easy, but if you want to vary the dimensions things get complicated. For example, given `arr[x][y]`, you can't tell whether `arr` is an array of arrays, an array of pointers, a pointer to an array, or a pointer to a pointer; all for implementations are legitimate, depending on your needs. And probably most real-world use cases for multidimensional arrays require the size to be determined at run time. – Keith Thompson Aug 11 '11 at 20:13
  • I would love to see that variadic-template-implementation of n-dimensional arrays! Meta-programming at its best! – steffen Oct 22 '14 at 11:25
  • 3
    @steffen I did make an attempt a few years ago. You can view it here: https://code.google.com/p/fsma/source/browse/trunk/variadic_template_array.hpp. Test case here: https://code.google.com/p/fsma/source/browse/trunk/variadic_template_array_test.cpp. I'm sure it can be done a lot better though. – Ricky65 Nov 02 '14 at 15:14
5

The C-style arrays that are available in C++ are actually much less versatile than the real C-arrays. The difference is, that in C, array types can have runtime sizes. The following is valid C code, but it can neither be expressed with C++ C-style arrays nor with the C++ array<> types:

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

In C++, you would have to allocate the temporary array on the heap:

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

This cannot be achieved with std::array<>, because bar is not known at compile time, it requires the use of either C-style arrays in C++ or of std::vector<>.


While the first example could relatively easily be expressed in C++ (albeit requiring new[] and delete[]), the following cannot be achieved in C++ without 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);
}

The point is, that the pointers to the line arrays int (*)[width] cannot use a runtime width in C++, which makes any image manipulation code much more complicated in C++ than it is in C. A typical C++ implementation of the image manipulation example would look like this:

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;
}

This code does precisely the same calculations as the C code above, but it needs to perform the index computation by hand wherever the indices are used. For the 2D case, this is still feasible (even though it comes with a lot of opportunities to get the index calculation wrong). It gets really nasty in the 3D case, though.

I like writing code in C++. But whenever I need to manipulate multidimensional data, I really ask myself whether I should move that part of the code to C.

cmaster - reinstate monica
  • 36,366
  • 8
  • 54
  • 104
  • 7
    It should be noted that at least Clang and GCC support VLA's in C++. – Janus Troelsen Jun 06 '14 at 13:38
  • @JanusTroelsen and also that they are horribly limited in which element types they support. –  Apr 22 '15 at 12:39
  • Doesn’t C11 make VLA optional? If so then I think your answer is misleading. It would be correct when C99 was the standard but not C11. – Z boson Jan 28 '16 at 09:05
  • 1
    @Zboson C99 is a C standard, and there are compilers that implement its VLA features (`gcc` for instance). C11 has made quite a bit of interesting stuff optional, and I don't think that's because they want to outlaw the feature. I tend to see it as a sign that they wanted to lower the level for writing a fully standard compliant compiler: VLA's are quite a difficult beast to implement, and much code can do without, so it makes sense for a new compiler on some new platform to not have to implement VLA's right away. – cmaster - reinstate monica Jan 29 '16 at 17:24
-1

May be the std::array is not slow. But I did some benchmarking using simple store and read from the std::array; See the below benchmark results (on W8.1, VS2013 Update 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

According to the negative marks, the code I used is in the pastebin (link)

The benchmark class code is here;

I don't know a lot about benchmarkings... My code may be flawed

K'Prime
  • 98
  • 4
  • 6
    Benchmark results without benchmark code, or compilation flags? Come on, you can do better. – R. Martinho Fernandes Apr 22 '15 at 12:10
  • FWIW, just that small bit of code already shows the benchmark is severely flawed. A smart enough compiler will just turn the whole thing into `long test_arr_without_init() { return ARR_SIZE; }` – R. Martinho Fernandes Apr 22 '15 at 12:13
  • That was just an example. I thought it was not big deal. I changed the code to return void, used release build in VS 2013, with /O2 /Ot /Gl. – K'Prime Apr 22 '15 at 13:59
  • Removing the return value means the compiler can turn the whole thing into `void test_arr_without_init() {}` now. You really need to jump through hoops to make sure the code you are measuring is the code you want to measure. – R. Martinho Fernandes Apr 22 '15 at 14:17
-5
  1. to implement something like std::array
  2. if you don't want to use the STL or can't
  3. For performance
Lou Franco
  • 85,695
  • 14
  • 129
  • 186
  • I accept the first one, but can you please explain the others? What would prevent you from using the standard library? – Björn Pollex May 24 '11 at 14:01
  • 27
    Tell me how `std::array` will be less performant than a C array. – Xeo May 24 '11 at 14:01
  • @Xeo: Bounds checking is one example. Bounds checking is usually good, and I'll personally try to use std::array any time I can. But if you ask for element 5 in a 3-element std::array, there must be a check. This will slow down your program a little. The is a performance cost to the following check: ["If that position is invalid, the function throws an object of class out_of_range."](http://msdn.microsoft.com/en-us/library/bb982619.aspx) – Aaron McDaid May 24 '11 at 14:05
  • 2
    [From wikipedia](http://en.wikipedia.org/wiki/Std::array): "The array implementation is not required to do bound check. However the implementation in boost does that for operator[], but not for iterators." -- so operator[] is slower. I haven't looked at implementations, but any code in the implementation could get in the way of the optimizer. – Lou Franco May 24 '11 at 14:07
  • In some environments, the STL is not available. For instance, when working with AVR8 microcontrollers(ie, Arduino). The STL would increase code size too much, so it was never attempted to be ported. – Earlz May 24 '11 at 14:08
  • @Aaron, I am not sure the standard defines bounds checking. It doesn't for other containers with `operator []` so I can't see why it would for `std::array` – juanchopanza May 24 '11 at 14:11
  • 1
    @Space_C0wb0y: 1. need to write code that compiles in older compilers 2. Need to link to a library that uses an old version of STL on Windows and loads it dynamically 3. Implementing a competing data-structure library and don't want the STL dependency – Lou Franco May 24 '11 at 14:12
  • 19
    @Aaron McDaid: That's only in `at()`, it's not in `operator[]`, just like `std::vector`. There's no performance decrease or code bloat to `std::array`, the compiler is designed to optimize this kind of thing. And, of course, the addition of the checked function is an excellent debug tool and a large advantage. @Lou Franco: All C++ code may depend on the Standard library- that's kind of what it's for. @Earlz: If you don't have STL available, then it's not C++, and that's the end of that. – Puppy May 24 '11 at 14:20
  • @DeadMG.. no? Technically, it's still C++, complete with virtual functions and OOP. Just the STL won't fit in 4kbytes of code memory – Earlz May 24 '11 at 14:24
  • 6
    @Earlz: The C++ Standard contains the Standard library. If you can't use the library, it's not conformant. And secondly, you must have one hell of a shitty compiler for the use of `std::array` to be larger than equivalent C array usage. – Puppy May 24 '11 at 14:47
  • @McDaid It's perfectly legal for `operator[]` to do bounds checking, and any good implementation will have options to do so. Of course, they'll also have options to turn it off when the performance matters. – James Kanze May 24 '11 at 15:47
  • @DeadMG for the record, most C++ compilers aren't standard conforming. – Earlz May 24 '11 at 16:48
  • 5
    @Earlz: There's a big difference between "not quite conforming" and "missing features which are hundreds of pages in specification". – Puppy May 24 '11 at 17:11
  • 2
    @DeadMG: Principally agree. But don't forget that C++ implementations might be freestanding, with only a minimal set of guaranteed headers ("Freestanding implementations [compliance]"), and what is called STL sometimes is not part of the minimal set of headers – Sebastian Mach Sep 01 '11 at 10:10
  • Adding to @Lou's scenarios of not using STL: implementing an operating system. If somebody decides to write a operating system in C++ (we know it will not be Linus Torvalds knowing his opinion of C++), then not wanting or even being able to rely on standard libraries seem likely. However it could still make sense to use the suitable selection C++11 language features (and maybe reimplement std:array equivalent just like Linux reimplementing many libc functions). – FooF Jun 29 '12 at 05:29
  • 1
    @DeadMG and Earlz: I am perhaps nitpicking (but this is SO so it should be okay)... Essentially (with little rewording) you are both right but are talking of different things. There is difference between language implementation compliance and restrictions placed by target platform. If your platform restricts what language features you can use, it does not change the language conformance. (Maybe there is another platform targeted by the same compiler where you can use fuller or full set of features provided by that same language implementation.) – FooF Jun 29 '12 at 05:39
  • ahh, `operator[]` doesn't do bounds checking. `.at` does. – Miles Rout Mar 16 '13 at 23:55
  • @Xeo Simply including the `array` header copy-pastes 1000+ lines of code. If the usage is very trivial, there isn't any need to shy away from C-style arrays –  Nov 11 '20 at 05:28