Search

6.2 — Arrays (Part II)

This lesson continues the discussion of arrays that began in lesson 6.1 -- Arrays (part I)

Initializing fixed arrays

Array elements are treated just like normal variables, and as such, they are not initialized when created.

One way to initialize an array is to do it element by element:

However, this is a pain, especially as the array gets larger.

Fortunately, C++ provides a more convenient way to initialize entire arrays via use of an initializer list. The following example is equivalent to the one above:

If there are more initializers in the list than the array can hold, the compiler will generate an error.

However, if there are less initializers in the list than the array can hold, the remaining elements are initialized to 0 (or whatever value 0 converts to for a non-integral fundamental type -- e.g. 0.0 for double). This is called zero initialization.

The following example shows this in action:

This prints:

7
4
5
0
0

Consequently, to initialize all the elements of an array to 0, you can do this:

As of C++11, the uniform initialization syntax should be used instead:

Omitted length

If you are initializing a fixed array of elements using an initializer list, the compiler can figure out the length of the array for you, and you can omit explicitly declaring the length of the array.

The following two lines are equivalent:

This not only saves typing, it also means you don’t have to update the array length if you add or remove elements later.

Arrays and enums

One of the big documentation problems with arrays is that integer indices do not provide any information to the programmer about the meaning of the index. Consider a class of 5 students:

Who is represented by testScores[2]? It’s not clear.

This can be solved by setting up an enumeration where one enumerator maps to each of the possible array indices:

In this way, it’s much clearer what each of the array elements represents. Note that an extra enumerator named MAX_STUDENTS has been added. This enumerator is used during the array declaration to ensure the array has the proper length (as the array length should be one greater than the largest index). This is useful both for documentation purposes, and because the array will automatically be resized if another enumerator is added:

Note that this “trick” only works if you do not change the enumerator values manually!

Arrays and enum classes

Enum classes don’t have an implicit conversion to integer, so if you try the following:

You’ll get a compiler error. This can be addressed by using a static_cast to convert the enumerator to an integer:

However, doing this is somewhat of a pain, so it might be better to use a standard enum inside of a namespace:

Passing arrays to functions

Although passing an array to a function at first glance looks just like passing a normal variable, underneath the hood, C++ treats arrays differently.

When a normal variable is passed by value, C++ copies the value of the argument into the function parameter. Because the parameter is a copy, changing the value of the parameter does not change the value of the original argument.

However, because copying large arrays can be very expensive, C++ does not copy an array when an array is passed into a function. Instead, the actual array is passed. This has the side effect of allowing functions to directly change the value of array elements!

The following example illustrates this concept:

before passValue: 1
after passValue: 1
before passArray: 2 3 5 7 11
after passArray: 11 7 5 3 2

In the above example, value is not changed in main() because the parameter value in function passValue() was a copy of variable value in function main(), not the actual variable. However, because the parameter array in function passArray() is the actual array, passArray() is able to directly change the value of the elements!

Why this happens is related to the way arrays are implemented in C++, a topic we’ll revisit once we’ve covered pointers. For now, you can consider this as a quirk of the language.

As a side note, if you want to ensure a function does not modify the array elements passed into it, you can make the array const:

sizeof and arrays

The sizeof operator can be used on arrays, and it will return the total size of the array (array length multiplied by element size). Note that due to the way C++ passes arrays to functions, this will not work properly for arrays that have been passed to functions!

On a machine with 4 byte integers and 4 byte pointers, this printed:

32
4

(You may get a slightly different result if the size of your types are different).

For this reason, be careful about using sizeof() on arrays!

Note: In common usage, the terms “array size” and “array length” are both most often used to refer to the array’s length (the size of the array isn’t useful in most cases, outside of the trick we’ve shown you above). In these tutorials, we’ll try to use the term “length” when we’re talking about the number of elements in the array, and “size” when we’re referring to how large something is in bytes.

Determining the length of a fixed array prior to C++17

One neat trick: we can determine the length of a fixed array by dividing the size of the entire array by the size of an array element:

This prints:

The array has 8 elements

How does this work? First, note that the size of the entire array is equal to the array’s length multiplied by the size of an element. Put more compactly: array size = array length * element size.

Using algebra, we can rearrange this equation: array length = array size / element size. sizeof(array) is the array size, and sizeof(array[0]) is the element size, so our equation becomes array length = sizeof(array) / sizeof(array[0]). We typically use array element 0 for the array element, since it’s the only element guaranteed to exist no matter what the array length is.

Note that this will only work if the array is a fixed-length array, and you’re doing this trick in the same function that array is declared in (we’ll talk more about why this restriction exists in a future lesson in this chapter).

Determining the length of a fixed array in C++17/C++20

In C++17, a better option is to use the std::size() function, which is defined in the <iterator> header.

Here’s an example:

This prints:

The array has 8 elements

std::size() has the advantage of being easier to remember, it will work with other kinds of objects (such as std::array and std::vector), and it will cause a compiler error if you try to use it on a fixed array that has been passed to a function! Note that std::size returns an unsigned value.

In C++20, std::ssize is even more preferable, as it returns a signed value.

Recommendation: Favor std::size (C++17) or std::ssize(C++20 onward) over the array length division trick if your compiler is C++17/C++20 capable.

Indexing an array out of range

Remember that an array of length N has array elements 0 through N-1. So what happens if you try to access an array with a subscript outside of that range?

Consider the following program:

In this program, our array is of length 5, but we’re trying to write a test score into the 6th element (index 5).

C++ does not do any checking to make sure that your indices are valid for the length of your array. So in the above example, the value of 13 will be inserted into memory where the 6th element would have been had it existed. When this happens, you will get undefined behavior -- For example, this could overwrite the value of another variable, or cause your program to crash.

Although it happens less often, C++ will also let you use a negative index, with similarly undesirable results.

Rule: When using arrays, ensure that your indices are valid for the range of your array!.

Quiz

1) Declare an array to hold the high temperature (to the nearest tenth of a degree) for each day of a year (assume 365 days in a year). Initialize the array with a value of 0.0 for each day.

2) Set up an enum with the names of the following animals: chicken, dog, cat, elephant, duck, and snake. Put the enum in a namespace. Define an array with an element for each of these animals, and use an initializer list to initialize each element to hold the number of legs that animal has.

Write a main function that prints the number of legs an elephant has, using the enumerator.

Quiz answers

1) Show Solution

2) Show Solution

6.3 -- Arrays and loops
Index
6.1 -- Arrays (Part I)

290 comments to 6.2 — Arrays (Part II)

  • hellmet

    Whoa whoa there! So you're telling me, std::size can take a array as argument and return a valid size but a function that uses the same argument and uses sizeof doesn't work! That's some sweet mechanics happening in std::size then! Would be nice to point that out! Adddnn, I can't use std:size in a function! So much happening!

    • Yep, `std::size` can't extract any more information from a decayed array than `sizeof` can. C-style arrays should be avoided anyway, use `std::array` and `std::span` (`gsl::span` if pre C++20) instead, they store the size.

  • Parsa

    std::size results in an error for saying std namespace has no member ssize

    • Enable a higher standard in your compiler's settings.
      `std::size` needs at least C++17
      `std::ssize` needs at least C++20 (Probably not supported yet)

    • hellmet

      You can try and fix that by changing some of your compiler flags. For Visual Studio and Code blocks, you can add the flag '/std:c++17' and check if that works.

      For gcc, add '-std=c++17'. So the whole command would like so.

  • Benur21

    How about passing an array as argument to a function, but the function has an array with a different lenght in its parameter?

  • Lawrence

    Why are the namespace and enum names the same? I'm guessing Animals in the main() is referring to the namespace?

    • I don't know why Alex choose to do this. You're right, `Animals` in `main` is the namespace. The enum's name is never used and can be omitted.

      • Alex

        I gave the enum a name here because I hadn't previously covered the fact that enums can be anonymous.

        I've updated the article on enums to note this, and added a comment around the fact that the enum could be made anonymous.

        • daniel

          where did u cover anon enums?

          • Alex

            I mentioned it in the lesson on enumerated types when I said, "Providing a name for an enumeration is optional, but common". I added a note to that lesson to note that these enums are sometimes called "anonymous enums" just in case anybody is searching for that term.

            • Rob

              Still a bit confused about named/anonymous enums. Given that the solution does name the enum, I was expecting to see namespace_name::enum_name::ENUMERATOR, i.e. Animals::Animals::ELEPHANT and that's how I did it in my solution. Clearly it still works if we just do namespace_name::ENUMERATOR, so does that mean that all enums within a namespace can be treated as if they were anonymous, even when we do actually give them a name?

  • Lorenz

    Little thing: you are missing

    in the second solution.

    And a question for you: in the previous chapters you always recommend to use uniform initialization for variables, what about arrays? is it also better or can I use =?

    Thank you

  • Red Lightning

    If const is a run-time constant (And constexpr is compile-time), how does this example work? these are fixed size arrays.

  • Alireza

    Hello there,

    I just tried this piece of code and it gave me an undefined behavior.

    As I have understood, the @aa array can access the other bits which are not in the range of the array.
    So it can change the other bits and can make some bugs not to have security.
    What should we do in these cases ?

  • Dimbo1911

    Good morning, I have a simple question, is there any convention how to break up the initialization of a table (when it gets over 80 columns, for redability on smaller monitors)? I did it like this but do not know if it is correct.

  • MasterOfNothing

  • YTXbaiaLrs

    In the code example for "Determining the length of a fixed array as of C++17",

    is missing. Also, I do not get the same output as you: "The array has 8elements" (missing space after 8).

  • Nathan

    I’m creating a game and have X and Y coords.  Currently, I’m trying to implement walls, should I use an array to hold all of the walls X and Y coordinates (to check agensed current position)?  A struct? Have any suggestions?

    • 1 wall segment is an instance of a struct (Containing wall material, type, etc.).
      Multiple walls are stored in an array.

      • Nathan

        Thanks, that would be pretty interesting

      • Nathan

        Do you have any suggestions on how I would format that?

        • I have little information about your game.

          • Nathan

            So far, the entire game is math based, and in the console.  I did figure it out though.  Thanks!

          • Nathan

            If you had a more efficient way to do this, it would be appreciated

            ect...

  • Okorie Emmanuella

    Hi!
    I tried writing a code where i declared my array as a char type. After which i tried to compare some elements of the array with
    with boolean expression ==  to my surprise, it gave me an error;
    error: ISO C++ forbids comparison between pointer and integer [-fpermissive]

    Please what really is the problem and how can i solve it?
    Here is my code

    Also, i would really appreciate any other corrections or additions you might have.

    Thank you :)

    • * Line 8, 11, 14, 23, 24, 25: Limit your lines to 80 characters in length for better readability on small displays.
      * Use '\n' instead of @std::endl
      * Use the auto-formatting feature of your editor
      * @i never changes
      * Avoid global variables
      * Re-read the lesson on loops
      * Use a bool for binary variables

      Your code looks like you skipped several lessons and/or didn't do the quizzes. You'll run into a lot more problems.

  • hassan magaji

    Hi Alex,
    what about designated initializers and compound literals?

    • Alex

      Designated initializers are proposed for C++20, which hasn't been finalized yet. Compound literals are a C99 concept, not C++, and better options exist in C++ to do the same thing (create an anonymous std::array).

  • Hi Alex!

    The sizeofs should be replaced with @std::size. It's more versatile, up-to-date and easier to understand. This affects all subsequent lessons.

  • Jon

    Hi! Quick question about the last example in the "Arrays and enum classes" section. Do you recommend using a standard enum inside of a namespace, or just using a standard enum without the namespace like you did in the "Arrays and enums" section above that, and why?

    Thank you!

    • > using a standard enum without the namespace
      Don't use a standard enum unless it's encapsulated (eg. in a namespace or class).
      Use one of the ways shown in the "Arrays and enum classes" paragraph.

  • Rai

    I can't get this to work!!

    The error messages are:
    error C2664: 'std::string getAnimalString(int [])': cannot convert argument 1 from 'int' to 'int []'
    note: Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast

    These are for  getAnimalString(animal[die(mersenne)])

    • Rai

      I just fixed it with this.

      One problem is that The number generated of die(Mersenne) for getAnimalString is different to the one for legs[]. This gives something weird like "A cat has 2 legs" or even "a snake has 4 legs." If lucky it will give "A cat has 4 legs." How do I make it so the number generated for both die(Mersenne) is the same.

      Also how could I improve this code.

      • Rai

        Fixed with this

        int generated = die(mersenne)

        std::cout << "A " << getAnimalString(animal[generated)]) << " has " << legs[generated] << " legs.";

        • Benjamin

          You missed the point of using the enum as the index of the legs array.

          Check out lines 54 & 55 of my code.

  • Stefen Zaki

    can you initialize an array to zero by doing the following ?
    int array [arraySize] = {0};

    • That zero-initializes the entire array.
      Your version also does it, but the 0 in the curly brackets only sets the first element to 0, which is 0 anyway.
      If you want to initialize to a non-zero value, you can use @std::fill

  • Shawn

    On quiz 2:

      int legs[Animals::MAX_ANIMALS] = { 2, 4, 4, 4, 2, 0 };

    why can't we just do:

    int legs[] {2, 4, 4, 4, 2, 0};

    • You can do so. The benefit of explicitly specifying the size is that if you changed @MAX_ANIMALS but forgot to update @legs, you'd get a compile-time error or warning.
      Uniform initialization should be used either way.

  • C++guy

    There's something I didn't understand with what is said about the argument being copied when there's a function call.
    When you make a function call, the parameter of the function is created and initialized with this argument right ? (if not, how does it works ?) So even if I assign a new value to the parameter like you did in the example it doesn't affect the argument.
    It's like saying int value = y then writing value = 2, only the value of value changes but not y right ?

    • > the parameter of the function is created and initialized with this argument
      right

      > It's like saying int value = y then writing value = 2, only the value of value changes but not y
      right. Lesson 7.2, 7.3 and 7.4 cover this in more detail. I like your example.

  • Eric

    Maybe I'm missing something but in the passing arrays to functions, why did "before passArray" print out backwards?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]