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. 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:

In C++11, the uniform initialization syntax can 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

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).

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)

196 comments to 6.2 — Arrays (Part II)

  • John Halfyard

    I had to put the Animals::Animals::elephant in the called function down below in order to use namespace and enum Animals.  If I took out the namespace I only needed "Animals::elephant" - could someone tell me what's going on?  hahaha

    • Alex

      In the example above, you shouldn't need a double Animals:: prefix. The namespace requires one prefix, but the enum doesn't, so a single prefix should do.

      If you used an enum class inside a namespace, you'd need a double prefix.

      • John Halfyard

        Okay.. I removed the second prefix in the main code and it worked but why do you need to type "std::string getAnimal(Animals::Animals animal)" in the called function getAnimal?

  • The Perplexed Programmer

    Good morning/afternoon/evening !
    Can we type

    instead of

    ?

    • Alex

      No, C++ doesn't support list assignment to fixed arrays (which is annoying).

      Fortunately you can do list assignment with std::array:

      std::array x;
      x = { 1, 2, 3, 4, 5 };

      We talk more about std::array later in this chapter.

  • Hi Alex and commenters, I'm confusing myself with question 1 I was able to answer the question as follows

    so I thought I would write a little program to prove that each element had in fact been set to 0.0 the result I got was not what I expected so to check further I decided to intialize each element to 2.5 just by substituting {2.5} in place of {0.0} and checked again, it appears that only the first element is initialized although I could have coded my little program incorrectly, what do you think, here is the code:-

    • Alex

      Correct. Using an initializer list will initialize the elements of the array to the values in the initializer list. If there aren't enough values in the initializer list, the rest of the values in the array will be zero-initialized.

      • Ah so to fully answer q1 you would need at least line 8 and lines 19 to 22 of the following program in the main() loop:-

        • Alex

          double temperature[365] = { }; should initialize all the elements to 0. You don't need to iterate through all the elements of the array to do zero initialization. If you want to set them to some other value, you do. However, the quiz only asks you to set them to 0.

      • Hi Alex sorry, I was taking the part of the quiz question that states "Initialize the array with a value of 0.0 for each day." literally. i.e. set each day to the value 0.0

  • c++ learner

    why are we using namespace here?

    • Alex

      Without a namespace, the StudentNames would go into the global namespace. That's not a problem for this simple program, but as programs get larger you want to keep your global namespace as clean as possible.

  • Russell

    Sorry if this has already been asked...

    Shouldn't Solution 1 be double temperature[364] = { 0.0 }  (i.e. 365 - 1)?

    • Alex

      No. The number in hard braces is the number of elements you want in your array. Those elements are numbered 0 through #-1.

      In this case, because there are 365 days in a year, we want 365 elements, so we use 365 as the array size. Those elements are indexed from 0 to 364.

  • Said

    So even though this compiles and runs just fine it's big no no because it could corrupt memory?

  • Jeremiah

    Not getting the results I was expecting. It's only printing element 66-364.

    Never mind. I'm assuming the console only allows a certain amount of lines. I was able to fix it like previous tutorials have demonstrated adding...

    if (iii % 10 == 0)
    std::cout << std::endl;

  • James Ray

    Hi Alex,

    Why does the following program print a random 8 digit hexadecimal number?

    • Alex

      Because your fixed array is decaying into a pointer to the first element of the array, and since pointers hold addresses, your compiler is printing the address the pointer is holding as a hex value.

      I discuss all of this in more detail in the upcoming lessons in this chapter.

  • James

    I love that South Park reference by the way.  😉

    What is the difference between vectors and arrays?  I have looked at vectors a little bit.  They look very similar to arrays.

    • Alex

      I talk about vectors at the end of this chapter. But in short:
      * Fixed arrays are a set of sequential elements laid out in stack memory.
      * Dynamic arrays are a set of sequential elements laid out in dynamic memory, where you do all the memory management.
      * Vectors are a set of sequential elements laid out in dynamic memory, where the vector does all the memory management.

      Essentially, vectors provide a nice way to get the dynamism of dynamic arrays without having to deal with all of the memory management.

  • Rohit

    when I use const before int prime[5] in the function definition it shows an error but the tutorial says that it won't change the values of the prime[].

  • nikos-13

    Could we solve quiz #1 like this?

  • Buddy

    Thank you very much for the south park reference in the example.

  • Ratnesh

    Its incorrect
    0 is an integer literal whereas 0.0 is a double precision literal. Its a convention to tell the compiler how to store the value in memory.

    your quiz was to initialize all with 0.0 not 0

    • Alex

      It's not incorrect, though my wording is slightly sloppy.

      Section 8.5.1.7 of the C++ spec says, "If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list".

      An empty initializer list as applies to individual array elements does a value initialization. A value initialization for a class type uses the default constructor, and for fundamental types does a zero initialization. Zero initialization does an implicit conversion of integer constant zero to the element's type.

      So, basically, if we provide at least one initializer, any non-initialized elements in our double array get zero-initialized, which means integer 0 is implicitly converted to 0.0 and used to initialize the element.

      It's trivial to prove that this is true: just write a simple program and initialize a double array with fewer elements than the array size. You'll see the rest of the elements get zero-initialized. Just make sure you do it in release mode, not debug mode, since debug mode will generally zero-initialize all variables.

  • Ratnesh

    Hi Alex Solution for quiz 1 is wrong this will only initialize first element of array not all

    for initializing all element we have to set in loop or you suggest any better solution

    Let me know your answer

    • Alex

      Incorrect. As noted in the lesson, "... if there are less initializers in the list than the array can hold, the remaining elements are initialized to 0".

      Since our array has 365 elements and we initialized the first one to 0.0, the remaining elements will be initialized to 0.

  • Paulo Mourão

    Hey Alex.
    Amazing tutorials, I'm learning a hell of a lot from this, thank you.
    I'm somewhat halfway through this one and I have a doubt. I tried searching on the comments section for this, but I didn't find it (let's hope I didn't miss it, sorry if I did).

    It's just that you said "Note that this “trick” only works if you do not change the enumerator values manually!". Why is that?

    • Alex

      Remember that each enumerator that isn't given a value will take a value one greater than the previous enumerator. So if you don't touch the enumerator values, then the MAX_ENUMERATOR will represent the number of other enumerators (itself excluded). This value can be useful in many different contexts, including setting the size of an array where you need one element for each enumerator.

      If you were to assign your own values to the enumerator, MAX_ENUMERATOR would likely end up with some other value that doesn't represent the number of other enumerators in the array -- thus defeating the point of the trick.

      • Paulo Mourão

        AHH, that trick! I thought you were reffering to the whole thing of using enumerators in the first place. I thought the compiler would complain or something if you used enumerators whose values had been changed. My bad, misunderstood it there.
        Thank you for the quick response!

  • Alexander Kindel

    For the first quiz question, I assumed the idea was ensure that, whenever an element of the array is printed, it will be formatted with one digit to the right of the decimal place. In the given answer, I find that no matter how I enter a value, if there is a zero to the right of the decimal place not followed by a nonzero digit, that zero is omitted when the value is printed. For example, if I set an element to 2.0, it prints as 2. I recognize that this or any other single case could be fixed using setprecision(), but since that deals with total number of digits displayed, on both sides of the decimal point, while here the desired total number of digits varies depending on how many of them are to the left of the decimal place, I don't see how it could be used in general. Is this something that hasn't been covered by this point in the tutorial, or have I missed it?

    • Alex

      I've not covered how to print a fixed number of digits after the decimal point. Here's how you can do that:

  • Brankovich

    Why an out-of-range element can be accessed if I assign it to a value?

    • Alex

      C++ doesn't do range checking for build-in arrays. Making sure your indices are all within range is your responsibility. If you fail to do so, the results are undefined. Your program may work fine, you may get a different result, or it may crash.

  • Matt

    Alex,

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

    In context, I figured out what this sentence meant after reading ahead. But this sentence alone is a little ambiguous due to your use of the word "size" refering to two different things in the same sentence.

    • Alex

      Yeah, I agree, the overloading of the word size makes that hard to parse. I've updated the lesson to use the word "length" when I'm talking about how many elements are in the array, and "size" when I'm talking about how large something is in bytes.

  • HelloWorld

    Is it possible to get the array-length by dividing the sizeof(array) through sizeof(datatype of array)?

    E.g for int..

    int array[] { 1, 2, 3, 4};
    std::cout << "Array length: " << sizeof(array)/sizeof(int) << "n";
    //instead of sizeof(int) you use the sizeof(array[0])..if this is for making sure that the array is not un-initialized, then i get your point there.

    The math behind this is:
    sizeof(array) = 4*4bytes = 16 bytes
    sizeof(int) = 4 bytes
    sizeof(array[0]) = 4 bytes

    Right?

    So sizeof(array)/sizeof(array[0]) or sizeof(array)/sizeof(int) EQUALS (4 * 4 bytes) /4 bytes = 4 . The actual length of the array.
    Let skip the case where ints are 2 bytes..it would be the same math only 2 instead of 4 bytes. Still 4 entrys in the array itself. The last entry (4) is array[3] indexed to [3]. Indexing starts at 0 right.

    So the length of 4 would be great to work with in a for loop because we loop until array index [3] if we set the the loop like this :
    for (int i = 0; i < sizeof(array)/sizeof(int); ++i)
    {
    do somethings
    };

    Now….
    If I made an array like this:

    int array[4] {1, 2, 3, 4}; it would set the 5th array entry array[4] to 0 by default through initializing the array not just defining it

    So If I do the sizeof(array)/sizeof(array[0]) or sizeof(array)/sizeof(int) on this…

    Will this give me the same results as above, or will this happen:

    sizeof(array) = 5 * 4 bytes; //5 not 4 because the last array entry array[4] is initialized = 0.  That makes 5 initialized entrys

    1. array[0] = 1
    2. array[1] = 2
    3. array[2] = 3
    4. array[3] = 4
    5. array[4] = 0 // by default to 0, because the array is initialized(initializer list), not only defined/or declared(i dunno what term is actually right for this, I tend to defined?)

    sizeof(array[0]) = sizeof(int) = 4 bytes

    So this time sizeof(array)/sizeof(array[0]) or sizeof(array)/sizeof(int) EQUALS (5 * 4 bytes)/ 4 bytes = 5.

    It's dangerous to use this length/s unknowingsly in for loops etc. For example calculations like a mean of a dataset need the exact amount of entrys. If the 0 and the length of 5 not 4 get calculated within the mean, the value is not representive for whatever, isn't it?

    • Alex

      Yes, I cover both this trick and its limitations in the "sizeof and arrays" subsection as part of the lesson.

      If you do this:
      int array[4] { 1, 2, 3, 4 }

      there is no 5th element, so sizeof(array)/sizeof(array[0]) will give you the proper length of 4.

      It's a neat trick, but there _are_ better options available (such as std::array) that I discuss later in the chapter.

  • HWANG SEHYUN

    So, [int temperature[365]{}][/code] would initialize all the elements to 0.
    How about [double temperature[365]{}][/code]?? will this initialize all the elements to 0.0.

    In solution 1, I'm curious why you initialized 0.0 only to the first elements. Does [double temperature[365]{}][/code] works same for this?

  • KIRPAL SINGH HARWAN SINGH

    // even though prime is the actual array, within this function it should be treated as a constant
    void passArray(const int prime[5])
    {
        // so each of these lines will cause a compile error!
        prime[0] = 11;
        prime[1] = 7;
        prime[2] = 5;
        prime[3] = 3;
        prime[4] = 2;
    }

    if this will produce an error... what is the right way to make an array constant?

    • Alex

      I don't understand the question. This produces an error because prime is constant, and the function is trying to change the value of the members.

      The problem isn't prime, the problem is the code in the function body.

  • KIRPAL SINGH HARWAN SINGH

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

    int main()
    {
        int testScores[static_cast<int>(StudentNames::MAX_STUDENTS)]; // allocate 6 integers
        testScores[static_cast<int>(StudentNames::STAN)] = 76;
    }

    can you explain a little further on this... i dont understand how this works... i am new to c++

    • Alex

      What part of this are you not understanding? enum classes won't implicitly convert their enumerators to a integer, so we have to explicitly do so via static_cast.

  • KIRPAL SINGH HARWAN SINGH

    int main()
    {
        int testScores[MAX_STUDENTS]; // allocate 5 integers
        testScores[STAN] = 76;

        return 0;
    }

    how will the compiler know where to find max_student since its inside enum studentname and not in main function... shouldnt it be like testScores[studentname.Maxstudent]... i am new to c++

    • Alex

      enums are a type, not an object, so you don't need an object to access the enumerators.

      Also, enums put their enumerators into the surrounding scope (e.g. they pollute the scope they're in). In the above case, the enumerators in StudentNames go into the global namespace, and thus are accessible from within function main().

  • Abdul Sami

    Dear Admin, i have a question about passing array as an argument to a function. when we call PassArray function by passing array then how it works ? Is the base  address passed or the whole Array etc??  

  • Jorge

    I am not using the "namespace Animals" and the code works fine, why?  Thanks, Jorge

    • Alex

      Enums put all of their enumerators in the global namespace (which causes a lot of namespace pollution). This means you don't need to use the scope resolution operator (::) to access them. But it also means you will get naming conflicts if you have multiple enums trying to use the same enumerator (e.g. a Color enum and a Mood enum would have a conflict if enumerator Blue appeared in both).

  • abolfazl

  • KIRPAL SINGH

    this is example from above:
    "StudentNames::STAN"

    what is the meaning of "::" after the enum StudentNames?

    • Alex

      :: is the scope resolution operator. What comes before the :: identifies the scope, and what comes after the :: identifies the identifier within that scope. So StudentNames::STAN means "Look inside the scope StudentNames for an identifier named STAN". The scope could be the name of a namespace, enum, class, etc... If no scope is provided, then the global scope will be used (so ::Foo would refer to identifier Foo in the global namespace).

  • KIRPAL SINGH

    array element 2

    Can you elaborate more on this statement? what did you mean when you said array element 2?

  • subh samal

    Hi Alex,

    I could not understand the reason behind the below differences when using enum inside namespace. Why typecasting is not required in the second case?

    case 1: Without namespace.

    enum class StudentNames
    {
        KENNY, // 0
        KYLE, // 1
        STAN, // 2
        BUTTERS, // 3
        CARTMAN, // 4
        WENDY, // 5
        MAX_STUDENTS // 6
    };

    int main()

    {
        
    int testScores[static_cast<int>(StudentNames::MAX_STUDENTS)]; // allocate 6 integers
        testScores[static_cast<int>(StudentNames::STAN)] = 76;
    }

    case 2: using namespace.

    namespace StudentNames
    {
        enum StudentNames
        {
            KENNY, // 0
            KYLE, // 1
            STAN, // 2
            BUTTERS, // 3
            CARTMAN, // 4
            WENDY, // 5
            MAX_STUDENTS // 6
        };
    }

    int main()

    {
        int testScores[StudentNames::MAX_STUDENTS]; // allocate 6 integers
        testScores[StudentNames::STAN] = 76;
    }

    • Alex

      Interesting question, but the wrong question to ask. 🙂 Remember that enums are placed into the same scope that they are declared in, so in the top case, KENNY, KYLE, STAN, etc... are all placed in the global namespace. It's actually non-standard C++ to access an enum using the scope resolution operator -- Visual Studio lets you, as do some other compilers, but it's non-standard behavior. So instead of StudentNames::STAN, you should just be using STAN in this case.

      In the second case, StudentNames:: is actually resolving to the namespace, with STAN resolving to the enumerator inside of the namespace. This is syntactically valid for all compilers.

  • Nyap

    y doesn't this work

    /home/nyap/Projects/TicTacToe/main.cpp|43|error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]|

Leave a Comment

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