Language[lang]

Data types in C++: int, double, char, bool and more

Section
Language [lang]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
7 min read
In this answer6 clauses
  1. 1What are the basic data types in C++?
  2. 2How do you check the size and range of a type on your machine?
  3. 3Signed or unsigned, and what happens on overflow?
  4. 4When does the exact size matter? (int32_t and friends)
  5. 5What type does auto pick, and what do suffixes like 5u mean?
  6. 5.1Why does int n{3.7} not compile?
  7. 6Which C++ data type should you use?

Abstract

C++ data types say what a variable holds and how much memory it takes: int for whole numbers, double for decimals, char for one character, bool for true or false, and std::string for text. The standard sets only minimum sizes. MSVC, Clang and GCC on 64-bit Windows and Linux make int 4 bytes and double 8, while long is 4 bytes on Windows and 8 on Linux; sizeof and std::numeric_limits print the real size and range.

Four boxes side by side, one, one, four and eight cells wide: a tick in the first, an S-shaped stroke in the second, a blue bar in the third, a black bar broken by a dot in the last.
Figure 1 — Different kinds of value, different sizes.

C++ data types say what kind of value a variable holds and how much memory it takes. The basic ones are int for whole numbers, double for decimals, char for one character and bool for true or false; text goes in a std::string. The standard sets only minimum sizes, so exact sizes depend on the compiler and the platform.

One variable of each, printed with std::cout:

[Example 1:
#include <iostream>
#include <string>

int main() {
    int age = 36;             // a whole number
    double height = 1.72;     // a number with a decimal point
    char grade = 'A';         // one character
    bool is_member = true;    // true or false
    std::string name = "Ada"; // text, from the standard library

    std::cout << std::boolalpha;  // print bool as true/false, not 1/0
    std::cout << "name: " << name << '\n';
    std::cout << "age: " << age << '\n';
    std::cout << "height: " << height << '\n';
    std::cout << "grade: " << grade << '\n';
    std::cout << "member: " << is_member << '\n';
}

— end example]

It printed:

[Example 2:
name: Ada
age: 36
height: 1.72
grade: A
member: true

— end example]

The samples are C++17 (Since When’s C++17 table), built with MSVC 19.44 and Clang 19.1 (MSVC STL) on 64-bit Windows and with GCC 16.2 and Clang 23.1 (libc++) on 64-bit Linux. All four printed the same here.

What are the basic data types in C++?

They are the types built into the language, which the standard calls fundamental types; the ones you will use most are bool, char, the whole-number types short, int, long and long long, and the floating-point types float and double for numbers with a decimal point. The sizes and ranges below are what the four compilers printed on 64-bit desktops, not what the standard promises:

Type Holds Bytes here Range or precision
bool true or false 1 false or true
char one character 1 -128 to 127
short a small whole number 2 -32,768 to 32,767
int a whole number 4 about -2.1 billion to 2.1 billion
long a whole number 4 on Windows, 8 on Linux as int on Windows, as long long on Linux
long long a large whole number 8 about -9.2 quintillion to 9.2 quintillion
float a number with a decimal point 4 6 significant digits
double the same, more precisely 8 15 significant digits
Bars drawn to scale in byte cells, one per type: bool and char 1 byte, short 2, int 4, long as two bars, 4 bytes on Windows and 8 on Linux, long long 8, float 4, double 8.
Figure 2 — Typical sizes on a 64-bit desktop; of these types, only long differs between Windows and Linux.

The standard fixes only minimums: int is at least 16 bits, long at least 32 and long long at least 64, and each is at least as large as the one before it ([basic.fundamental]). That leaves room for long to differ: 64-bit Windows keeps it at 4 bytes (Microsoft’s table of data type ranges), and 64-bit Linux makes it 8.

How do you check the size and range of a type on your machine?

sizeof(T) gives a type’s size in bytes, and std::numeric_limits, from <limits>, gives its range: max() is the largest value and, for a whole-number type, min() the smallest.

[Example 3:
std::cout << "int:         " << sizeof(int) << '\n';
std::cout << "long:        " << sizeof(long) << '\n';
std::cout << "long long:   " << sizeof(long long) << '\n';

— end example]

int printed 4 and long long 8 on all four; long printed 4 on both Windows builds and 8 on both Linux builds.

[Example 4:
std::cout << "int min:          "
          << std::numeric_limits<int>::min() << '\n';
std::cout << "int max:          "
          << std::numeric_limits<int>::max() << '\n';

— end example]

All four printed -2147483648 and 2147483647. For float and double, min() is the smallest positive value held at full precision, not the most negative one; lowest() gives that. Their digits10 printed 6 for float and 15 for double: a float carries 6 significant decimal digits through without change and a double 15, the precision in the table.

Signed or unsigned, and what happens on overflow?

An unsigned type, such as unsigned int, holds no negative numbers and wraps around: one past its largest value is 0, and one below 0 is its largest value.

[Example 5:
unsigned int u = 0;
std::cout << u - 1 << '\n';    // below zero: wraps to the top

unsigned int top = std::numeric_limits<unsigned int>::max();
std::cout << top + 1 << '\n';  // past the top: wraps to zero

unsigned int v = -5;           // not an error: -5 wraps too
std::cout << v << '\n';

— end example]

All four printed 4294967295, 0 and 4294967291, and only MSVC warned about the -5 (C4245 at /W4; Clang and GCC said nothing at -Wall -Wextra). The wrap is the standard’s rule: unsigned arithmetic, and converting a value such as -5 to an unsigned type, is done modulo 2 to the power N, where N is the type’s number of bits.

A signed type such as int has no such rule. A result past its largest value is undefined behavior ([expr.pre]): the standard places no requirement on what the program does next. Never rely on it; pick a type wide enough, such as long long for values past about 2.1 billion.

When does the exact size matter? (int32_t and friends)

It matters when the bytes leave your program, in a file format or a network message. Then use the fixed-width types from <cstdint>, which carry their width in the name; everywhere else, int is the default.

[Example 6:
std::cout << sizeof(std::int32_t) << '\n';  // exactly 32 bits
std::cout << sizeof(std::int64_t) << '\n';  // exactly 64 bits
std::cout << sizeof(std::uint8_t) << '\n';  // exactly 8 bits

std::uint8_t age = 65;
std::cout << age << '\n';                    // printed as a char
std::cout << static_cast<int>(age) << '\n';  // printed as a number

— end example]

All four printed 4, 8 and 1, then A and 65: on these compilers std::uint8_t is another name for unsigned char, which std::cout prints as a character, so cast it to int to see the number. The u stands for unsigned.

What type does auto pick, and what do suffixes like 5u mean?

auto gives a variable the type of the value it starts with, and every literal, a value written in the code such as 5 or 'a', has a type: 5 is an int, 5.0 a double, and a suffix changes it.

[Example 7:
auto n = 5;       // int
auto x = 5.0;     // double
auto f = 5.0f;    // f: float
auto u = 5u;      // u: unsigned int
auto big = 5LL;   // LL: long long
auto c = 'a';     // char
auto s = "text";  // const char*, not std::string

— end example]

Checked with std::is_same_v, every comment held on all four. The last line is the surprise: from a quoted literal auto makes a const char*, a pointer to characters, and s tested false as a std::string. Write the type when you want a string, as std::string name = "Ada"; does in the first program.

Why does int n{3.7} not compile?

Braces refuse a narrowing conversion, one that can lose data, whether or not this value would; double to int is one, and here the .7 would go:

[Example 8:
int n{3.7};  // error: braces refuse to drop the .7

— end example]

All four rejected it. Clang said:

[Example 9:
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]

— end example]

With =, int n = 3.7; compiles and stores 3 on all four; MSVC and Clang warn, and GCC said nothing at -Wall -Wextra. Initialize with braces, and the lost data becomes an error instead of a 3 you may not notice.

Which C++ data type should you use?

Pick by what the value is. std::size_t is the unsigned type of the value sizeof gives, declared in <cstddef>. The rest of the basics are in the Language section.

The value Use
A whole number: a count, an age int
A whole number past about 2.1 billion long long
A number with a decimal point double
One character char
True or false bool
Text std::string
A size, or an index into a container std::size_t
An exact width, for a file or the network std::int32_t and the other <cstdint> types
One of a fixed list of names an enum

Questions

What is the size of int in C++?

The standard requires an int of at least 16 bits. On MSVC and Clang on 64-bit Windows and on GCC and Clang on 64-bit Linux it is 4 bytes (32 bits), with a range of -2,147,483,648 to 2,147,483,647. sizeof(int) prints the size on your own compiler.

What is the difference between float and double?

Size and precision. On MSVC, Clang and GCC on 64-bit desktops a float is 4 bytes and carries 6 significant decimal digits through without change, and a double is 8 bytes and carries 15 (std::numeric_limits digits10). Use double unless memory matters; a literal such as 5.0 is already a double.

Is string a data type in C++?

Not a built-in one. std::string is a class from the standard library, in the header <string>. The built-in char holds one character, and a quoted literal such as "Ada" is an array of chars; use std::string for words and sentences.

What is the difference between primitive and user-defined data types?

Primitive types, which the standard calls fundamental types, are built into the language: int, double, char, bool. User-defined types are the ones you or a library write with struct, class or enum; std::string is one, written by the standard library. Arrays, pointers and references, built from other types, are often called derived types.

What is the largest integer type in C++?

The largest standard integer types are long long and unsigned long long, which the standard requires to be at least 64 bits. On MSVC, Clang and GCC on 64-bit Windows and Linux, long long is 8 bytes and holds up to 9,223,372,036,854,775,807.

Is char signed or unsigned in C++?

The standard leaves it to the implementation: plain char has the range of either signed char or unsigned char, and the three are distinct types. On MSVC, Clang and GCC on x86-64 Windows and Linux, char was signed, -128 to 127. When the sign matters, write signed char or unsigned char.

Sources

  1. Fundamental typescppreference.com
  2. std::numeric_limitscppreference.com
  3. Fixed width integer typescppreference.com
  4. std::size_tcppreference.com
  5. Data type rangesMicrosoft Learn
  6. [basic.fundamental] Fundamental typesC++ standard working draft
  7. [expr.pre] Expressions: preambleC++ standard working draft

Read next

  1. bool in C++: true, false and how to use themLanguage · Miriam Castellanos · written for C++17
  2. Polymorphism in C++: one call, many behaviorsLanguage · Miriam Castellanos · written for C++17
  3. C++ constructor: default, parameterized, copy and moveLanguage · Miriam Castellanos · written for C++11