Search

6.1 — Arrays (Part I)

Note: This chapter is a bit harder than the previous ones. If you feel a little discouraged, stick with it. The best stuff is yet to come!

In lesson 4.7 -- Structs, you learned that you can use a struct to aggregate many different data types into one identifier. This is great for the case where we want to model a single object that has many different properties. However, this is not so great for the case where we want to track many related instances of something.

Fortunately, structs are not the only aggregate data type in C++. An array is an aggregate data type that lets us access many variables of the same type through a single identifier.

Consider the case where you want to record the test scores for 30 students in a class. Without arrays, you would have to allocate 30 almost-identical variables!

Arrays give us a much easier way to do this. The following array definition is essentially equivalent:

In an array variable declaration, we use square brackets ([]) to tell the compiler both that this is an array variable (instead of a normal variable), as well as how many variables to allocate (called the array length).

In the above example, we declare a fixed array named testScore, with a length of 30. A fixed array (also called a fixed length array or fixed size array) is an array where the length is known at compile time. When testScore is instantiated, the compiler will allocate 30 integers.

Array elements and subscripting

Each of the variables in an array is called an element. Elements do not have their own unique names. Instead, to access individual elements of an array, we use the array name, along with the subscript operator ([]), and a parameter called a subscript (or index) that tells the compiler which element we want. This process is called subscripting or indexing the array.

In the example above, the first element in our array is testScore[0]. The second is testScore[1]. The tenth is testScore[9]. The last element in our testScore array is testScore[29]. This is great because we no longer need to keep track of a bunch of different (but related) names -- we can just vary the subscript to access different elements.

Important: Unlike everyday life, where we typically count starting from 1, in C++, arrays always count starting from 0!

For an array of length N, the array elements are numbered 0 through N-1! This is called the array’s range.

An example array program

Here’s a sample program that puts together the definition and indexing of an array:

This prints:

The lowest prime number is: 2
The sum of the first 5 primes is: 28

Array data types

Arrays can be made from any data type. Consider the following example, where we declare an array of doubles:

This program produces the result:

The average is 3.1

Arrays can also be made from structs. Consider the following example:

To access a struct member of an array element, first pick which array element you want, and then use the member selection operator to select the struct member you want:

Arrays can even be made from arrays, a topic that we’ll cover in a future lesson.

Array subscripts

In C++, array subscripts must always be an integral type. This includes char, short, int, long, long long, etc… and strangely enough, bool (where false gives an index of 0 and true gives an index of 1). An array subscript can be a literal value, a variable (constant or non-constant), or an expression that evaluates to an integral type.

Here are some examples:

Fixed array declarations

When declaring a fixed array, the length of the array (between the square brackets) must be a compile-time constant. This is because the length of a fixed array must be known at compile time. Here are some different ways to declare fixed arrays:

Note that non-const variables or runtime constants cannot be used:

Note that in the last two cases, an error should result because length is not a compile-time constant. Some compilers may allow these kinds of arrays (for C99 compatibility reasons), but they are invalid according to the C++ standard, and should be not be used in C++ programs.

A note on dynamic arrays

Because fixed arrays have memory allocated at compile time, that introduces two limitations:

  • Fixed arrays cannot have a length based on either user input or some other value calculated at runtime.
  • Fixed arrays have a fixed length that can not be changed.

In many cases, these limitations are problematic. Fortunately, C++ supports a second kind of array known as a dynamic array. The length of a dynamic array can be set at runtime, and their length can be changed. However, dynamic arrays are a little more complicated to instantiate, so we’ll cover them later in the chapter.

Summary

Fixed arrays provide an easy way to allocate and use multiple variables of the same type so long as the length of the array is known at compile time.

We’ll look at more topics around fixed arrays in the next lesson.

6.2 -- Arrays (Part II)
Index
5.x -- Chapter 5 comprehensive quiz

139 comments to 6.1 — Arrays (Part I)

  • Boogie

    Where do you talk about compiler time?

  • TanLan

    Hi,

    The line 10 in your lesson is incorrect

  • Nguyen

    Hi,

    How can I can determine these array's length?  Any neat trick?

    Thanks, Have a great day.

  • Moein

    Hi Alex and Nascardriver!

    should i use this:

    instead of:

    ??
    because "constexpr" is used when the constant is compile time.
    Thanks!

    • Hi Moein!

      Yes, constexpr is a const variable, but it allows @arrayLength to be used in other contexpr expressions, which can run at compile-time.
      Also, uniform initialization is better.

  • Ramesh

    If we use g++ compiler, following code works.

    int length;
    std::cin >> length;
    int array[length];

  • Samira Ferdi

    Where can I find all header files, all built-in functions, all built-in constants, all data types, etc?

    • nascardriver

      Hi Samira!

      It depends on your system. On linux they should be located in /usr/include, I don't know about other systems. Your IDE might be able to show they to you when you right click on the #include and select "Show Definition" or similar.

  • Samira Ferdi

    Alex.

    I array data types example, cout must be std::cout.

    By the way, can use enum type for array?

  • Richard Bondi

    Array Subscripts example line 18

    not, ok, index out of bounds

  • Samira Ferdi

    I defined array with a fixed length 5, but why I can assign to array with the index greater than 4?

    • nascardriver

      Hi Samira!

      I assume your code looks similar to this

      Line 5 is accessing and attempting to override an undefined value. The memory address at arr[6] is well defined, however the memory itself could be used by another variable or not even accessible to your program. So you're either
      1. Accessing memory which isn't owned by your array, you can use it until your computer uses it for something else and you'll end up with weird values.
      2. Overriding another variable, this is can be abused by attackers to run a stack overflow exploit.
      3. Accessing inaccessible memory and your program crashes.

      1 and 2 are a pain to debug, 3 is easily found, but your program might run for a hundred times before an error occurs, good luck debugging that.

      Some compilers add run-time protection when building in Debug mode so you can't exceed array boundaries, for example a program compiled with gcc will terminate itself, print "*** stack smashing detected ***: <unknown> terminated" and attempt to create a memory dump.

  • Neo

    Conside the following:

    #include <iostream>

    int main()
    {
        int length_a;
        std::cin >> length_a;
        int array_a[length_a]; // Not ok -- length is not a compile-time constant!

        // using a runtime const variable
        int temp = 5;
        const int length_b = temp; // the value of length isn't known until runtime, so this is a runtime constant, not a compile-time constant!
        int array_b[length_b]; // Not ok

        return 0;
    }

    The lesson says that you can't use run-time constants to allocate memory for fixed arrays but this code compiles just fine on the GDB comiler. Can anyone explain why?

    • nascardriver

      Hi Neo!

      GDB isn't a compiler, it's a debugger. I assume you're using g++.
      What you did is _not_ standard C++, it's a compiler extension and should not be used.
      The proper way of creating arrays of dynamic size is covered in lesson 6.9a (You'll need to read the previous lessons before being able to understand it).

      Adding -pedantic to your compiler options will cause your compiler to show warnings when you're using non-standard features.

  • hi alex, can you please make your comment area in your site folded, because sometimes i read your article it make me panic, but you can imagine my surprised, that on some article page, it's only quarter of scroll..., plzzzz,, and ..... "your photo is awesome"

  • Peter Baum

    In the section "Fixed array declarations" it would be helpful to talk about this variation:

    The original version in the lesson doesn't compile under Visual Studio as would be expected, but the above seems to work fine.  Does const make temp a compile-time constant?  What does the C++ standard have to say about this?

    • Alex

      In the above, temp would be a compile-time const, but length would be a runtime const.

      It's fine to initialize a const variable from a compile-time const, or even a non-const. So your snippet will compile file. What we're talking about in the lesson is that the length of a C-style array must be a compile time const. Thus, temp could be used as the array length, but length could not.

      Edit: My original response was incorrect, see my updated response further down this thread.

      • Peter Baum

        What an interesting example.  Do all compilers make length a runtime const?  I would think that a smart compiler could figure out that a const assigned to the value of another const would have to be constant.

        • nascardriver

          I can't tell you about run-time- and compile-time constants, but I can show you my observations.

          Compiles fine (without compiler extensions).
          Both @temp and @length are treated exactly the same.
          Both values are known at compile-time, stored in a read-only section and substituted when printing their value in @main.

          Tested with g++ 8.0.1. I can't imagine any compiler would behave differently.

          • Peter Baum

            I like what you did here.  Under Visual Studio, no surprises but I don't understand what the reinterpret_cast is all about.  Could you explain?

            • nascardriver

              To print a pointer with @printf the %p formatting is used. %p only works with void* so we need a cast.
              static_cast converts the actual data of the variable we're casting. Say int to double. A double's representation in bits is different from an int's. static_cast changes those bits so if we converted 43 to a double we'd get 43.0.
              reinterpret_cast does not do any conversion on the data. All it does is change the type, the bits stay the same. Since I don't want to convert @temp or @length (I only want their addresses as void*) I used reinterpret_cast.

              More about casts: https://stackoverflow.com/a/332086/9364954

        • Alex

          So I just realized I made a mistake in my original response. Let me provide a code snippet clarifying the correct behavior:

          In example 1, length1 is initialized from a compile-time const, and thus is considered to be a compile-time const itself.
          In example 2, length2 is initialized from a non-const, and thus is a considered a run-time const.
          In example 3, length3 is initialized from a runtime-const, and thus is considered a run-time const.

          Hopefully that helps clarify things.

          • Peter Baum

            Nice.  The only thing that I'm uncertain about is c3.
            Am I right in suggesting that the reason c3 might be considered a run-time const is because foo() could be called using different const parameters?  To treat the array as having fixed length within foo() would mean it would essentially have to create different foo() routines for the different calls.  Technically possible in some cases but I can see how the compiler writer might want to abandon ship at this point.

            • Alex

              foo() could be called with anything -- a non-const, a runtime const, or a compile-time const. The point is that the function call isn't resolved until runtime, which means the const parameter has to be treated as a runtime const.

              Yes, you'd have to create different foo variables for different array sizes, which is a pain. Fortunately, templates and template expression parameters make this significantly easier, so we can write one template function and instantiate as many variants as we use. This is covered in chapter 13.

  • Peter Baum

    In the Array Subscripts section, you might consider replacing

    "-- and strangely enough, bool"

    with

    "and bool (where false gives an index of 0 and true an index of 1)"

  • Hamed O.Khaled

    Hi Alex!
    I have two questions.
    a) why isn't good to use define macros variables ?
    b)

    I think it's not good because the variable x would be resolved in the run time ?? am I right ?
    Thanks!

    • Alex

      1) I cover why macros aren't good to use in previous chapters -- look up macros in the index and have a read.
      2) x needs to be a compile-time constant, and in this case it isn't.

  • AS

    what is a run time constant variable? Why is it that in the last case, the array declaration won't work? Could you please explain this in detail.

    • Alex

      This is a bit hard to explain. When you assign a literal value to a const variable (e.g. const int x = 5), the compiler is smart enough to know that when it sees "x", it can substitute the value 5. Variable x is a constant, so it will never change. So these kinds of constants have values that are known at compile time.

      On the other hand, in the last example (const int length = temp), we see a const variable initialized with the value of a normal variable. The value of this normal variable isn't known until runtime -- therefore, the compiler doesn't know what value the constant will have until runtime either. Therefore, this constant is a runtime constant, not a compile-time constant.

      Fixed arrays must have a compile time constant as a length.

      • roy

        i think compiler is able to know length at that time, it is not run time compile variable. variable from cin input is run time. what do you think?

        • Alex

          I think that if you try to compile both of these cases yourself, you'll see in neither case is length considered a compile-time constant.

  • Hema

    Suppose I have an array of length 50. What is the easiest way to print the sum of all the elements?

    • Alex

      Use a loop to iterate through the array, so you can access each element. Sum of the value of the elements in a separate variable. Print out the sum.

  • Ahmed

    Thank you for awesome site, keep going great job

  • Mr C++

    This works on CODE::BLOCKS 16.01 successfully.
    even though I input 1 or 0 as the value of (n), it print arr[1] without any error or even any warning.

    • Alex

      Certain compilers will sometimes let you do things that aren't officially part of the C++ specification. Apparently Code::Blocks will let you do variable length arrays. That doesn't mean you should use them.

Leave a Comment

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