Search

4.1 — Introduction to fundamental data types

Bits, bytes, and memory addressing

In lesson 1.3 -- Introduction to 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 (also called a 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 computer architectures, each bit does not get its own unique memory address. This is because the number of memory addresses are limited, and the need to access data bit-by-bit is rare. Instead, each memory address holds 1 byte of data. A byte is a group of bits that are operated on as a unit. The modern standard is that a byte is comprised of 8 sequential bits.

Key insight

In C++, we typically work with “byte-sized” chunks of data.

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

Memory Addressing

As an aside...

Some older or non-standard machines may have bytes of a different size (from 1 to 48 bits) -- however, we generally need not worry about these, as the modern de-facto standard is that a byte is 8 bits. For these tutorials, we’ll assume a byte is 8 bits.

Data types

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 the compiler 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 uses is going to be interpreted as an integer value”.

When you give an object a value, the compiler and CPU take care of encoding your value into the appropriate sequence of bits for that data type, which are then stored in memory (remember: memory can only store bits). For example, if you assign an integer object the value 65, that value is converted to the sequence of bits 0100 0001 and stored in the memory assigned to the object.

Conversely, when the object is evaluated to produce a value, that sequence of bits is reconstituted back into the original value. Meaning that 0100 0001 is converted back into the value 65.

Fortunately, the compiler and CPU do all the hard work here, so you generally don’t need to worry about how values gets converted into bit sequences and back.

All you need to do is pick a data type for your object that best matches your desired use.

Fundamental data types

C++ comes with built-in support for many different data types. These are called fundamental data types, 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:

Types Category Meaning Example
float
double
long double
Floating Point a number with a fractional part 3.14159
bool Integral (Boolean) true or false true
char
wchar_t
char8_t (C++20)
char16_t (C++11)
char32_t (C++11)
Integral (Character) a single character of text ‘c’
short
int
long
long long (C++11)
Integral (Integer) positive and negative whole numbers, including 0 64
std::nullptr_t (C++11) Null Pointer a null pointer nullptr
void Void no type n/a

This chapter is dedicated to exploring these fundamental data types in detail (except std::nullptr_t, which we’ll discuss when we talk about pointers). C++ also supports a number of other more complex types, called compound types. We’ll explore compound types in a future chapter.

Author's note

The terms “integer” and “integral” are similar, but have different meanings. Integers are a specific data type that hold positive and negative whole numbers, including 0. The term “integral types” (which means “like an integer”) includes all of the boolean, characters, and integer types (and thus is a bit broader in definition). Integral types are named so because they are stored in memory as integers, even though they behave slightly differently.

The _t suffix

Many of the types defined in newer versions of C++ (e.g. std::nullptr_t) use a _t suffix. This suffix means “type”, and it’s a common nomenclature applied to modern types.

If you see something with a _t suffix, it’s probably a type. But many types don’t have a _t suffix, so this isn’t consistently applied.


4.2 -- Void
Index
3.x -- Chapter 3 summary and quiz

233 comments to 4.1 — Introduction to fundamental data types

  • prince

    Hi Alex,
    i wrote
    int value{7.5};
    and it printed as 7 as expected but without warning or errors in codeblock contradicted to
    but you said it disallows narrowing type conversions and above code-line should  produce  an error.
    plz reply

    2.
    one more doubt sir
    "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."

    in above line i am not able to make out what is the difference plz explain it elaboratly.

    • Alex

      1) Your compiler should be giving you a narrowing conversion error. If it isn't, it's either doing something incorrectly, or you have some parameter enabled that is telling your compiler to ignore this error.

      2) When you have a statement like int x = 5, the = sign is part of the syntax of the statement used to separate the variable name for the initialization. This differs from a statement like x = 5, where = is an operator that calls a function. It's not super important at this point, but becomes more meaningful in chapter 9 when we cover operator overloading.

  • James Smith

  • Muru

    My dear teacher,

    Thank you so much for these tutorials. I had been banging my head against wall to learn C++. You made it accessible for me with your tips and clear examples.

    Thank you.

    Regards,
    muru

  • Zero Cool

    Hey Alex. How are you doing?
    I think you should update the table of fundamental types to say that some types like char16_t or char32_t are C++14 standard too.

    Once again thanks for the great website you have made.

    • Alex

      char16_t and char32_t are part of the C++11 standard, not the C++14 standard. Compilers that are C++11 compliant or newer (including compilers that are C++14 compliant) should be able to use these.

  • Xenel

    I was wondering what the best choice is for initialization of variables. There was another comment saying that list (uniform) initialization was the safest because of conversion but I was wondering what the actual best practice in the real world was as opposed to it simply being safer.

    I've read through a bit more of the chapters and I mostly only see direct initializations used but my understanding was that uniform initializations are the best choice if using at least C++11 and variable conversion isn't planned.

    • Alex

      Uniform initialization is best in the real world. It's the safest and most consistent option, and it doesn't add additional burden.

      Many of these tutorials do copy or direct initialization because they were written pre-C++11 and then adapted. Ideally they should be updated but I've been focused on other things like answering comments. :)

      • Xenel

        Thanks for the reply. It's greatly appreciated. I don't mind that it isn't up to date in this constantly changing world. As long as my question is answered, the majority of these tutorials still give me that basic gist of things. Eventually I could have probably figured that out but I'd like to apply the best practice as soon as possible if I can.

        Thanks again.

  • gary wang

    Hi, thanks for the great tutorial. May I ask what do you mean by "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." Can you give an example? thanks so much

    • Alex

      Although these two lines look similar (both use an equals sign) and perform similar functions, C++ treats them differently. The equals signs are not related. In the top line, the equals sign is part of the copy initialization syntax. In the bottom line, the equals sign is the assignment operator, which is evaluated the same way other operators are.

      It doesn't make a huge amount of difference at this point, but later on we'll see that we can override the assignment operator -- this doesn't have any impact on copy initialization, since it doesn't use the assignment operator.

      • gary wang

        Oh, I see. I always thought they are the same thing.
        So basically, the both equal sign(=) are the same sign but they mean the different thing, just like * operator, one is for the pointer and other is for multiplication.
        Thanks so much for the explanation!

        • Alex

          Exactly! C++ is full of things like this, where symbols are reused but have different meanings in different contexts. & is another one: in some contexts it's the address-of operator. In other contexts it means reference.

  • Rob

    I have a few questions about memory and addresses

    Can I trust the computer to "leave" my variable at the same address or will the OS move it around if it needs to (assuming the variable not getting destroyed in the meantime). Eg. could I get the address of the variable and just refer to it by address, not name? I understand that this might be dangerous, and probably not useful in most situations, so it's mostly just curiosity.

    Secondly, can I ask for specific memory addresses for my variable (or maybe just for consecutive ones). Could there be a performance benefit or is reading four addresses at random just as fast as reading four consecutive ones?

    • Alex

      1) Once a variable has been given a memory address, the OS won't randomly change it -- but only so long as that variable is still in scope. Once that variable is destroyed, the program may reuse that memory address for something else.

      Variables may have different address with each execution of the program, and function parameters may be given different addresses each time the function is called.

      So yes, you can refer to a variable by address if you want. And yes, this is dangerous because the variable may go out of scope and get destroyed, you may end up referencing a memory location that's been reused for something else.

      2) No, you can't ask for a specific memory address. There may be some a performance benefit from having data that's accessed together be close in memory together (due to caching), but in most cases we tend to get that for free anyway via using arrays and classes, both of which allow us to aggregate related data into sequential memory.

  • Theo

    Dear Teacher ,
    can you please better explain whats the diference between (assigning an value to an integer)
    and (initializing it)

    • Alex

      Initialization occurs when you provide an initial value to a variable at the point where it is defined. Assignment happens when you provide a new value to a variable after it is defined.

      In most cases, the end result is the same. However, later on, we'll start seeing some cases where variables must be initialized, or can not be assigned to. So it's worth understanding the distinction now, even though in both cases (when it is allowed) the variable acquires the value.

  • Stan

    Just wanted to comment and say this whole tutorial has been very interesting and helpful, thank you!

  • B1

    hi there Alex sorry for bothering you
    but i wrote a small code on Online Compiler (codechef.com)
    and it gave me an error
    i tried the code with (NetBeans) it also gave me the same error

    when i do this

    it gives me this error "prog.cpp:9:1: error: 'g1' does not name a type"

    but when i do this

    it compiles fine...is assigning forbidden in the global scope or what ?

    • Alex

      Yes. You can define things in the global scope, but you can not execute code in the global scope. So the definition of g1 is okay, but the assignment is not.

      If you want to do this, you should define g1 with an initializer (int g1 = 44;).

      • B1

        oh..right
        execution of the code will begin & end in main()
        my thick head missed this info

        thank you Alex

      • Jim Smith

        But this is new information. In the global scope C++ allows some statements like definitions (int x;) and forbids other statements like assignments (x = 2;). Shouldn't this be included in the tutorial?

        • Alex

          Probably, but we haven't even covered the global scope at this point in the tutorial, so it might be a bit premature to do so before or at this point..

  • dgo

    [code]
    #include <iostream>

    int main()
    {
        int x{3};
        std::cout << "x is initialized as " << x << std::endl;
    }

    Seems like uniform initialization doesn't work..
    It does not compile and complains that
    [code]
    variable1.cpp:5:10: error: expected ';' at end of declaration
        int x{3};

    A version of the compiler is the following:

    Dongwooks-MacBook-Pro:ch2 dgo$ g++ --version
    Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
    Apple LLVM version 8.0.0 (clang-800.0.42.1)
    Target: x86_64-apple-darwin16.3.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin

  • Georges Theodosiou

    My dear c++ Teacher,
    Please let me say, it is not that every compiler will throw an warning or error if I try to use uniform initialization to initialize a variable with a value it can not safely hold. For your example

    https://www.codechef.com/ide >> 4.
    http://cpp.sh/ >> nothing.
    http://rextester.com/l/cpp_online_compiler_gcc >> 4.
    https://www.tutorialspoint.com/cplusplus/index.htm >>
    main.cpp: In function ‘int main()’:
    main.cpp:5:18: warning: narrowing conversion of ‘4.5e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
         int value{4.5};
                      ^
    So of the 4 compilers online only one throw an warning.
    With regards and friendship.

    • Alex

      cpp.sh gives a warning, you can see the yellow triangle symbol at the line of code, and if you click on the "compilation" tab you'll see the warning.

      But you're right, some of the others don't appear to be compliant with the C++ specification in this regard. Note that narrowing conversions in a uniform initialization should be an error, not a warning!

  • Nguyen

    Hi Alex,

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

    When I read about the Memory addresses, I start getting confused.  Here are what I guess "When a variable is defined, a memory address is set aside for that variable"

    I don't know if a variable has anything to do with the memory address when a variable is defined?

    Thank you

    • Alex

      Let's recap:
      1) Your computer has memory.
      2) Memory is used to store values.
      3) Each byte of memory has a unique address.
      4) Memory addresses are hard to work with (because they are just numbers).

      So, when you declare a variable, you're doing a few things:
      A) You're telling the compiler to reserve some memory for you.
      B) You're telling the compiler how to convert the contents of that memory into a specific type of value.
      C) You're telling the compiler what you want to call that memory.

      For example, when you say

      You're telling the compiler that you want to reserve 4 bytes of memory, that the value should be interpreted as an integer, and that you want to name that memory location "x".

  • George

    I'm not too sure I understand with regards to the defining/initialization. Is what you're saying that you can define a variable separately but with initialization you can define and give it a value on one line? And with the assignment is that simply when you want to give it a value at a lower line so you define it and don't initialize? I've read it a couple times but I'm still not sure.

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

    • Alex

      When a variable is defined, memory is allocated for it. We can define a variable with no initial value:

      But this is generally bad practice, because x will be given a garbage value, and that's usually not desirable. So when we define a variable, we can initialize it, which simply means that we're providing it with an initial value:

      A variable can only be initialized at the point of definition.

      Later on, if we want to _change_ the value that a variable has, we can assign a new value to it, via the assignment operator (=). You can do this regardless of whether the variable was initialized or not.

      Make sense? Also, typo fixed. Thanks!

  • Nick

    Correct me if I'm wrong, but under "Defining a variable" the example of all 5 variable types does not include void, but does include both float and double, which are the same thing, and is thus an incorrect example of all 5 variable types?

    • Alex

      The example isn't intended to be an example of how to declare all 5 types, least of all because you can't declare a variable of type void. I've added a note to the lesson about that.

      • McSteven

        Can you enlighten me, I don't understand the difference between float and double types.

        • Alex

          Float and double serve the same purpose (much like a short, int, and long do), but have different ranges and precisions. We talk more about all of these in upcoming lessons in this chapter.

  • Hi Alex,
    I have one doubt related to address concept

    Consider my compiler architecture is 32 bit(4 byte) and size of short is 16 bit and char is 8 bit
    If I have a structure with structure packing is enabled(No padding)
    like

    here both the members are stored in the same address, is it right ?

    then if I read the variable s_var2 like obj.s_var2. variable read the particular memory, so in this case both the member variable have same memory. In this case how we get the correct value of s_var2? or it is impossible ?

    • Alex

      No, if there's no padding then s_var1 will be stored in the first 8 bytes, and s_var2 will be stored in the next 16 bytes.

      You can prove this yourself by implementing the above, and printing the address of obj.s_var1 and obj.s_var2.

      • Hi Alex,
        Thanks for the replay

        How can I check with structure packing in a compiler, is any configuration in compiler. May be am wrong, My idea about address is, a 32 bit architecture can hold 32 bit data in a single address.
        ie, consider 0x0FFE1 is my address and it have 32 bit, and both the members are stored in the single address 0x00FFE1 is it correct?

        • Alex

          Compilers should have an option to set data structure alignment.

          A 32-bit architecture does not hold 32-bits of data in a single address. Most architectures are byte-addressable, which means that one address holds 1 byte of data. A 32-bit architecture means that the CPU can process 32-bits of data at once (4 addresses worth), as well as address 32-bits of memory addresses (4GB).

          In the future, please try and leave comments on the lessons more appropriate to the topic you're asking about. This topic is really better discussed in the lesson 4.7 -- Structs.

  • Dragos

    Now when u left a variable uninitialized, isn't it automatically initialized with 0?

  • Raquib

    I am confused by a portion (copied below) explained in this tutorial-----

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

    int a, b = 5; // wrong (a is uninitialized!)

    int a= 5, b= 5; // correct
    ----------------------------------------------------------------------------------------------------------

    My question is why is this called to be an assignment statement ?? I thought, the '=' sign here was just a part of syntax for copy initialization and should not be confused with assignment '='.

    Am I missing something ??

  • de.rock

    When i Initializing a variable with empty parenthesis i.e int x()  output is 1 and
    Initializing same variable with nothing i.e int x  output is 4291710. Can any please explain why different output for same variable...tnx..

  • Chris

    The line in question (line 30) has a semicolon.

    • Alex

      You're saying the compiler is complaining about line 30 needing a semicolon even though you already have one? It sounds like your compiler either isn't C++11 compatible, or maybe that functionality is not turned on. You may need to add compiler option -std=c++11 to your compiler settings. See this page for more info.

  • Chris

    The following code compiled and ran without a problem before I added the third to last statement, the one that is supposed to list initialize value to 4. The compiler complains that a ; needs to be put at the end of this statement.  

    Why does the compiler not recognize this mode of initialization?

    The version of C++ I am using includes cstdint, so I assume I am using C++11

    I am currently using the Eclipse IDE on my mac if that helps.

  • Rafal

    Can you explain me benefits of using direct initialization of variables ? What's the difference between direct and copy ? I understand them when we are using classes but what about int, float etc?

  • Nyap

    will you update the website when C++17 comes out?
    edit: and btw, why are explicit and implicit initializations called that? is it because explicit is a bit more obvious because of the assignment operator?

    • Alex

      If I'm still around, I will update for C++17.

      I've updated the names of the initialization forms to be more in line with accepted standards. Explicit initialization is more commonly called copy initialization, and implicit initialization is more commonly called direct initialization.

  • Chuck

    In the code snippet

    The line

    Is missing a semicolon. Not sure if it was a typo or not.

  • Heitor

    Hi, in a long and complex program it won't be an hassle to find where it is the declarations of the variables? I have the habit to declare them at the top of the function like in the old C compilers or declare variables in a seperate file, so I can find easily where the declaration of the variables are. Is this a bad way to do it? Just a question that may increase my performance I guess :/

    By the way I am enjoying to read your lessons, you teach very well. More than my old teacher (where she teached me how to use Visual Basic). I like to learn all about the language, not half. You deserve my congratz :D

    • Alex

      Modern IDEs should show you the variable declarations on hover, so there's no need to scroll to find them. Also if you generally declare variables as close to the first use as possible, the declarations are right above the code that uses them.

      Keeping your functions short and modular also helps.

      • Peter Baum

        I'm with Heitor on this one.  While there are advantages for each method, I would point out that:

        1. It is neater to have the declarations in one place; an unnecessary clutter if placed next to its first use.  During debug, one will mostly not be concerned with the declaration.

        2. If checking the declarations during debug, it is handy to have them all in one place so you can easily compare the types of related variables.  That is awkward if hovering.

        3. The use of a variable may take place in several separate sections of code, so an explanation of a variable's use (as a comment) is sometimes more appropriate in one expected location before all these sections.

        4. By placing the declarations at the top, we don't have to have a special rule for things like globals.

        But overall, this tutorial is terrific and I'm learning a great deal.

        • Alex

          You're certainly welcome to do whatever you like -- however, I would point out declaring your variables in as small a scope as possible, and as close to the first use as possible, is a well established best practice in the industry. There is very little debate on this point.

          • Peter Baum

            Hi Alex,

            Thanks so much for taking the time to reply.  

            Perhaps it would be helpful to separate the issue of declaring variables in as small a scope as possible from the issue of declaring variables “as late as possible.”  I don’t think there is any disagreement about the former.

            I hope we have more justification for whatever approach we take than an appeal to authority.

            I can think of one situation where we might wish to declare variables later rather than at the top of a block.  Depending upon the compiler, some objects may require function calls for their creation, and we might therefore want to invoke those expensive calls at run time only if the objects were actually needed.  The last lesson on timing code might be helpful here.

            Regards,
            Peter

            • Alex

              There's really no disagreement about either point.

              Whether instantiating those variables is expensive or not, defining your variables at the top of a block increases complexity by removing context about what the variable is used for. When you declare everything at the top of the function, you're dumping a pile of non-contextual information on the reader, forcing them to look through your code to see what's used where and how.

              One other good reason to declare as close to first use as possible: constants must be initialized on definition. You may not know what the initialization value is until later in the function.

              Also see: http://wiki.c2.com/?DeclareVariablesAtFirstUse

              • Peter Baum

                Regarding “…defining your variables at the top of a block increases complexity by removing context about what the variable is used for.” – You are correct that some code does this, but it is very common to find code where the purpose is distributed among several sections of code and its first use does not fully illuminate this context.

                Regarding “You may not know what the initialization value is until later in the function.” – Of course, but frequently this is not the case.

                Thanks for the link to http://wiki.c2.com/?DeclareVariablesAtFirstUse.  I didn’t see anything new there but it did remind me that both sides of this issue have their champions and favorite examples.  Habit and familiarity also play a role.
                  
                I’ll just leave it at that unless someone can come up with some new pro or con.

                (And finally… again, thank you Alex for the hard work you put into this site.  It is much appreciated.)

  • Lokesh

    Hi Alex,
    I am using -std=c++11 option to compile the following program with g++ along with some other options(-Wall -pedantic) and instead of giving me an error, it just gives me a warning.

    warning: narrowing conversion of '5.5999999999999996e+0' from 'double' to 'int' inside { } [-Wnarrowing]
    int nValue3{5.6}; // Universal initialization.

    The program compiles and runs with the following output:
    1
    2
    5

    Am I missing something?

    • Alex

      Apparently the compiler is not required to generate an error for a narrowing conversion -- just some kind of diagnostic message. Older versions of GCC gave an error, but they changed it to a warning in newer versions to increase compatibility with older code bases.

  • Javad

    Hi Alex,

    I believe it is not accurate to say "Uniform initialization disallows type conversion" as indicated in the lesson under Uniform initialization in C++11. Uniform initialization disallows type conversion if it involves narrowing. However, if type conversion can be performed without narrowing it is allowed. For example the following code compiles fine:

    char c{ 'c' };
    int i{ c };
    bool b{ true };
    int x{ b };
    char z{ 20 };

  • Aimee

    Hi Alex,

    Great tutorials so far but I'm not still not sure if I understand the difference in usage between brackets ' () ' and squiggly brackets ' {} ' during initialization of the variables.

    From what I understand, both bracket types seem to be appropriate for the initialization of variables so long as the variable introduced belongs to that class (ie, int nValue(4) is not different from int nValue{4}, but int nValue{4.5} does not work since 4.5 isn't an integer), and brackets do not work for lists.

    Is this true? Are there any other differences between the two bracket types?

    Thanks :)

    • Alex

      Both types are appropriate for initializing variables. The "squiggly" braces {} should be preferred if you're using a C++11 compatible compiler because they work for all kinds of initialization, whereas the parenthesis braces () work for initializing most (but not all) types. As you correctly note, the squiggly braces also prevent most implicit type conversions, which is generally a good thing.

      • Nick L

        I'm using Visual Studio Community 2015. Should I only use the {} initialization in the future of these tutorials? Also, after the initialization, should I go about as usual and use the = for assignment? so:

        and just ignore the () and = initialization altogether?

  • Sarhad Salam

    If I use uniform initialization, and my compiler supports it. Can I run my app in older computers?. If I follow C++11 rules, will old computers support it? (Say windows xp, or ubuntu 9, or any such)

    Thanks.

    • Alex

      Yes, once your app is compiled into an executable for a certain architecture, the compiler and which set of C++ features you used in our program doesn't matter.

  • yash

    I am using visual studio 2012....
    Can I use c++11 feature in it?
    When I use use uniform initialization it gives me error.......
    Please give me it's solution
    And thanks for making this tutorials.

Leave a Comment

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