Search

2.1 — Fundamental variable definition, initialization, and assignment

Addressing memory

This lesson builds directly on the material in the section “1.3 -- A first look at variables“.

In the previous lesson on variables, we talked about the fact that variables are names for a piece of memory that can be used to store information. To recap briefly, computers have random access memory (RAM) that is available for programs to use. When a variable is defined, a piece of that memory is set aside for that variable.

The smallest unit of memory is a binary digit (bit), which can hold a value of 0 or 1. You can think of a bit as being like a traditional light switch -- either the light is off (0), or it is on (1). There is no in-between. If you were to look at a random segment of memory, all you would see is …011010100101010… or some combination thereof. Memory is organized into sequential units called memory addresses (or addresses for short). Similar to how a street address can be used to find a given house on a street, the memory address allows us to find and access the contents of memory at a particular location. Perhaps surprisingly, in modern computers, each bit does not get its own address. The smallest addressable unit of memory is known as a byte. The modern standard is that a byte is comprised of 8 sequential bits. Note that some older or non-standard machines may have bytes of a different size -- however, we generally need not worry about these. For these tutorials, we’ll assume a byte is 8 bits.

The following picture shows some sequential memory addresses, along with the corresponding byte of data:

Memory Addressing

Because all data on a computer is just a sequence of bits, we use a data type (often called a “type” for short) to tell us how to interpret the contents of memory in some meaningful way. You have already seen one example of a data type: the integer. When we declare a variable as an integer, we are telling the compiler “the piece of memory that this variable addresses is going to be interpreted as a non-fractional number”.

When you assign a value to a data type, the compiler and CPU take care of the details of encoding your value into the appropriate sequence of bits for that data type. When you ask for your value back, your number is “reconstituted” from the sequence of bits in memory.

There are many other data types in C++ besides the integer, most of which we will cover shortly. As shorthand, we typically refer to a variable’s “data type” as its “type”.

Fundamental data types

C++ comes with built-in support for certain data types. These are called fundamental data types (in the C++ specification), but are often informally called basic types, primitive types, or built-in types.

Here is a list of the fundamental data types, some of which you have already seen:

Category Types Meaning Example Notes
boolean bool true or false true
character char, wchar_t, char16_t, char32_t a single ASCII character ‘c’ char16_t, char32_t introduced in C++11
floating point float, double, long double a number with a decimal 3.14159
integer short, int, long, long long a whole number 64 long long introduced in C99/C++11
void no type void n/a

This chapter is dedicated to exploring these basic data types in detail.

Defining a variable

In the “basic C++” section, you already learned how to define an integer variable:

To define variables of other data types, the idea is exactly the same:

In the following example, we define 5 different variables of 5 different types.

Note that void has special rules about how it can be used, so the following won’t work:

Variable initialization

When a variable is defined, you can immediately give that variable a value. This is called variable initialization (or initialization for short).

C++ supports three basic ways to initialize a variable. First, we can do copy initialization by using an equals sign:

(Note for advanced users: The equals sign used here for copy initialization is part of the initialization syntax, and is not considered a use of the assignment operator that gets invoked when doing copy assignment)

Second, we can do a direct initialization by using parenthesis.

Even though direct initialization form looks a lot like a function call, the compiler keeps track of which names are variables and which are functions so that they can be resolved properly.

Direct initialization can perform better than copy initialization for some data types, and comes with some other benefits once we start talking about classes. It also helps differentiate initialization from assignment. Consequently, we recommend using direct initialization over copy initialization.

Rule: Favor direct initialization over copy initialization

Uniform initialization in C++11

Because C++ grew organically, the copy initialization and direct initialization forms only work for some types of variables (for example, you can’t use either of these forms to initialize a list of values).

In an attempt to provide a single initialization mechanism that will work with all data types, C++11 adds a new form of initialization called uniform initialization (also called brace initialization):

Initializing a variable with an empty brace indicates default initialization. Default initialization initializes the variable to zero (or empty, if that’s more appropriate for a given type).

Uniform initialization has the added benefit of disallowing “narrowing” type conversions. This means that if you try to use uniform initialization to initialize a variable with a value it can not safely hold, the compiler will throw an warning or error. For example:

Rule: If you’re using a C++11 compatible compiler, favor uniform initialization

Variable assignment

When a variable is given a value after it has been defined, it is called a copy assignment (or assignment for short).

C++ does not provide any built-in way to do a direct or uniform assignment.

Uninitialized variables

A variable that is not initialized is called an uninitialized variable. In C++, a fundamental variable that is uninitialized will have a garbage value until you assign a valid one. We discuss this in previous lesson A first look at variables, initialization, and assignment.

Side note: C++ also has other non-fundamental types, such as pointers, structs, and classes. Some of these do not initialize by default, and some of them do. We’ll explore these types in future lessons. For now, it’s safer to assume all types do not initialize by default.

Rule: Always initialize your fundamental variables, or assign a value to them as soon as possible after defining them.

Defining multiple variables

It is possible to define multiple variables of the same type in a single statement by separating the names with a comma. The following 2 snippets of code are effectively the same:

You can also initialize multiple variables defined on the same line:

There are three mistakes that new programmers tend to make when defining multiple variables in the same statement.

The first mistake is giving each variable a type when defining variables in sequence. This is not a bad mistake because the compiler will complain and ask you to fix it.

The second error is to try to define variables of different types on the same line, which is not allowed. Variables of different types must be defined in separate statements. This is also not a bad mistake because the compiler will complain and ask you to fix it.

The last mistake is the dangerous case. In this case, the programmer mistakenly tries to initialize both variables by using one initialization statement:

In the top statement, variable “a” will be left uninitialized, and the compiler may or may not complain. If it doesn’t, this is a great way to have your program intermittently crash and produce sporadic results.

The best way to remember that this is wrong is to consider the case of direct initialization or uniform initialization:

This makes it seem a little more clear that the value 5 is only being assigned to variable b.

Because defining multiple variables on a single line AND initializing them is a recipe for mistakes, we recommend that you only define multiple variables on a line if you’re not initializing any of them.

Rule: Avoid defining multiple variables on a single line if initializing any of them.

Where to define variables

Older C compilers forced users to define all of the variables in a function at the top of the function:

This style is now obsolete. C++ compilers do not require all variables to be defined at the top of a function. The proper C++ style is to define variables as close to the first use of that variable as you reasonably can:

This has quite a few advantages.

First, variables that are defined only when needed are given context by the statements around them. If x were defined at the top of the function, we would have no idea what it was used for until we scanned the function and found where it was used. Defining x amongst a bunch of input/output statements helps make it obvious that this variable is being used for input and/or output.

Second, defining a variable only where it is needed tells us that this variable does not affect anything above it, making our program easier to understand and requiring less scrolling.

Finally, it reduces the likelihood of inadvertently leaving a variable uninitialized, because we can define and then immediately initialize it with the value we want it to have.

Most of the time, you’ll be able to declare a variable on the line immediately preceding the first use of that variable. However, you will occasionally encounter a case where this is either not desirable (due to performance reasons), or not possible (because the variable will get destroyed and you need it later). We’ll see examples of these cases in future chapters.

Rule: Define variables as close to their first use as you reasonably can.

2.2 -- Void
Index
1.12 -- Chapter 1 comprehensive quiz

186 comments to 2.1 — Fundamental variable definition, initialization, and assignment

  • Jon

    It appears that uniform initialization still does not work in Eclipse (I'm using it on OS X if that matters)? Someone noted the same problem in 2016. I copied

    into my code just as they did and received the message "expected ';' at the end of declaration". The place it is pointing to put the semicolon at is at

    which clearly just causes more errors. Any help?

    • Neither your IDE nor OS matter, your compiler is what makes the difference.
      Check your settings to figure out which compiler you're using, then look up how to tell the compiler to use C++17 or later and change the settings accordingly. If your compiler doesn't support C++14 or later there's no reason to use it, get a new one.

      • Jon

        It says it is using the GCC C++ Compiler. Not sure how to check what version of C++ it is using. I tried changing the compiler to cygwin, but that didn't help.

        • Run
          g++ -v
          in your command line to see which version of g++ you're using (last line). Without checking I'd say anything after version 6 should support C++14.
          Then add -std=c++1z (or -std=c++17 or 14 if you're using a version earlier than gcc 8) to the compiler settings.

          • Jon

            It is an old version for some reason: 4.2.1. Been trying to figure out how to update it? Sadly, I'm not knowledgeable about doing unix command line stuff or how to update these types of things.

            • Jon

              OK, I am downloading gcc8 now.

              • Jon

                So, I think I've updated. Now when I do "g++ -v", I get the following:

                Using built-in specs.
                COLLECT_GCC=g++
                COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin17/8.0.0/lto-wrapper
                Target: x86_64-apple-darwin17
                Configured with: /opt/local/var/macports/build/_opt_bblocal_var_buildworker_ports_build_ports_lang_gcc8/gcc8/work/gcc-8-20170604/configure --prefix=/opt/local --build=x86_64-apple-darwin17 --enable-languages=c,c++,objc,obj-c++,lto,fortran --libdir=/opt/local/lib/gcc8 --includedir=/opt/local/include/gcc8 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-8 --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-8 --with-gxx-include-dir=/opt/local/include/gcc8/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --with-isl=/opt/local --enable-stage1-checking --disable-multilib --enable-lto --enable-libstdcxx-time --with-build-config=bootstrap-debug --with-as=/opt/local/bin/as --with-ld=/opt/local/bin/ld --with-ar=/opt/local/bin/ar --with-bugurl=https://trac.macports.org/newticket --disable-tls --with-pkgversion='MacPorts gcc8 8-20170604_2'
                Thread model: posix
                gcc version 8.0.0 20170604 (experimental) (MacPorts gcc8 8-20170604_2)

                It still does not compile correctly and I am unsure of how to add what you said to the compiler settings in Eclipse.

                • I don't have eclipse installed, some things might be called different.
                  Right click on your project
                  Project properties
                  C/C++ Build
                  Settings
                  Cross G++ Compiler -> Miscellaneous
                  In the "Other flags" textbox add -std=c++1z

                  Here's an image of the settings, the contents of the textbox might look different for you, that doesn't matter, just add what I said to the end http://blog.fellstat.com/wp-content/uploads/2012/04/EclipseScreenSnapz013.png

                  While you're at it, you can also enable some more warnings to prevent you from writing bad code, add

                • Jon

                  It worked! Thanks! I added all of the things you said to.

  • yugin

    Is it merely a coincidence that "int" is used for both initialising integers and functions, or is there some deeper meaning or reason? Also, is it correct to say that when the compiler sees

    , it knows we're talking about a variable, while when it sees

    , it instead recognises it as a function?

    • Alex

      > Is it merely a coincidence that "int" is used for both initialising integers and functions, or is there some deeper meaning or reason?

      int has nothing to do with initialization. int defines a variable as an integer type, or in the case of a function, that the function either returns or accepts an integer type.

      > Also, is it correct to say that when the compiler sees

      , it knows we're talking about a variable, while when it sees

      , it instead recognises it as a function?

      You have the right idea, though your syntax isn't correct. int something() would definitely be a variable that is being initialized to . something() would be a function call. int something() would be a function declaration.

  • Ahmed Abbas sd

    hello everyone
    so i wanted to see the difference between the floating points
    but once i run the program
    i don't get decimals, all i get is integers
    only time i got decimal was from the last line of code in main()

    these are my results

    int = 3
    double = 25
    long double = 3
    input a number
    99
    input a number again
    28
    I devided them together, their result is = 3
    I devided thier result with long double & the answer is: 1
    I devided double by long double & the answer is: 8.33333
    Press any key to continue . . .

    • nascardriver

      Hi Ahmed!

      @devide divides int by int, which results in an int which is then implicitly casted to a long double when you return it. If you want a floating point result, at least one of the parts of the division has to be a floating point number.
      Same thing in line 13 and 14. 50, 2, 10 and 3 are integers. If you want doubles you need to use 50.0, 2.0, 10.0 and 3.0.

  • roman

    Hi there, Thank you for your attention. In the Variables instantiation section, the following statement is confusing to me:

    "Note that the equals sign here is just part of the syntax, and is not the same equals sign used to assign a value once the variable has been created."

    Are there two different  = signs? .....

    • Alex

      No, it's really poor wording on my part.

      I'm trying the get the point across that when you're doing an assignment, operator= (the assignment operator) is invoked. When you're doing an initialization, it isn't -- the equals sign is just considered part of the initialization syntax.

      This is most likely not that interesting now, but it does come up in programmer interviews, and becomes more relevant later once you learn about classes.

      I've reworded the sentence to try and better get the point across. Thanks for the feedback!

  • Michael Centeno

    "When we declare a variable as an integer, we are telling the compiler “the piece of memory that this variable addresses is going to be interpreted as a whole number”." A negative integer isn't a whole number, so this should be corrected 🙂

  • Brian Gaucher

    In your table with the primitive data types. You have a notes section. Nice, but It might be worth adding C++17 to the notes for integer types
    long long is C99/C++11 only
    to
    long long is C99/C++11 and upwards only
    unless it doesn't exist in C++17. I'm just guessing you forgot to add it.

  • Joe

    I am at my wits end.

    For academic purposes I want to specifically print out the garbage at a location.  I have looked up how to do this and on multiple web pages it states the same, declare an uninitialized variable and then cout the result but I keep getting a compile error.

    On multiple occasions I have written a function that returned garbage completely by accident, and now for the life of me I can not remember how I did it.

    so, my dilemma, how do I make the compiler print the value for an uninitialized variable?

    This is what I did, and what I have observed on other web pages.  
    Namely here:  
    https://stackoverflow.com/questions/29704710/why-are-memory-locations-assigned-garbage-values

    • nascardriver

      Hi Joe!

      There are no errors in your code, all you should get is a compile time warning. Which errors are you getting?

      Your code will most likely print 0, but then again, it's undefined, so you might get lucky. Make sure to build in release mode as some compilers initialize memory to a certain value automatically in debug mode.

      • joe

        Its just a compile time warning.

        There were times though in which it would print out like i said with a garbage value when i goofed on returning a value from a function.  I just can not remember how i did it.  Now that I'm trying to goof it's not working.  Lol.

        Ultimately i was trying to use a random garbage value as a seed for an rng to see how that would work but since i can't, I'll guess i try something else.

        • Alex

          Here's one way to do so using pointers:

          When I ran this in release mode a bunch of times, I got a bunch of 0's, and then some random large numbers.

  • Dear Teacher, please let me this question: In section "Uniform initialization in C++11" you suggest: "Rule: If you’re using a C++11 compatible compiler, favor uniform initialization". Do you suggest it for C++17 compatible compiler? Regards.

  • Linyuan

    Hello!
    i am very confused with this rule in the text:

    "Rule: Always initialize your fundamental variables, or assign a value to them as soon as possible after defining them."

    For a function, declaration is of cause different from definition, Declaration has actually the same meaning with the so called "function prototype". We just give out the return type and "head" of function in XXX.h. Definition of a function means that we write the body of this function in XXX.cpp.

    However for a variable, Declaration of variable is some form like

    in the header file XXX.h.

    but when I try to do some definition (initialization) about this value in the XXX.cpp like this:

    Error occurs. It says "value_from_user" has been defined twice.

    so here is my question: why would this happen?
    according to the rule, is seems that i have to do the both declaration(definition) and initialization in xxx.h such as:

    but yeah, that works when i do this in XXX.h

    What these three words "declaration", "definition" and "initialization" for a variable exactly mean???

    my opinion: for variable, declaration and definition are similar(almost the same in fact):

    and initialization is:

    so should i put this initialization in XXX.h or in a XXX.cpp???

    • Alex

      > However for a variable, Declaration of variable is some form like: int value_from_user;

      This is a definition (and a declaration). In order for it to be just a declaration, you need to add the keyword extern.

      > It says "value_from_user" has been defined twice.

      That's because of the definition of the variable in the header (that you thought was a declaration).

      Declaration: Tells the compiler about the name and the type of a variable or function, but does not tell the compiler where or how it's implemented.
      Definition: Tells the linker about the full specification of a variable or function, so it can be instantiated (for variables) or converted into code (functions)
      Initialization: Assigning an initial value to a variable at the point of creation/instantiation.

      Your statements under "my opinion" are correct.

      Non-const variables should generally be defined in XXX.cpp.
      Const variables can be defined in either XXX.cpp or XXX.h.

      I talk a lot more about these topics in chapter 4, particularly lessons 4.1 to 4.3.

  • Sirius

    Hi,
    Is it good practice to be initialising variables just before they are assigned a value from cin? For example is

    preferable over

    Or are they given values so soon afterwards that it's irrelevant? Thanks

    • nascardriver

      Hi Sirius!
      Yes please, having uninitialized variables can cause undefined behavior.

    • Alex

      Most often, if I'm going to immediately ask the user to enter a value for a variable defined immediately preceding the input, I don't initialize the variable. It seems unnecessary.

      Prior to C++11, a failed extraction from std::cin could leave the variable uninitialized. If you then tried to use that variable, you'd be accessing an uninitialized value, and who knows what you'd get. As of C++11, a failed extraction will zero the variable, so initializing beforehand now seems unnecessary. But some people prefer to do so anyway, out of habit.

Leave a Comment

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