Skip to content
VLSI Mentor

C++ Part I · Module 3 · Types, Variables, and Initialization

bool and Character Types

Two groups of fundamental types remain, and they are the two least like the rest. One holds a single logical answer and nothing else, and it is genuinely its own type rather than a small integer dressed up — though it converts to one readily enough that people conclude otherwise. The other is the type C++ uses for characters, and it is more interesting than it looks: it is an integer type that happens to be used for text, it comes in three separate flavours that are genuinely distinct types, and one of those three has a property that varies between platforms and quietly breaks code written by people who assumed it did not. This chapter covers both, and it connects a loose end from Chapter 3.4 — the surprise where a std::uint8_t printed as a letter turns out to have its explanation here.

Foundation14 min readC++Part IFundamentalsTypes

Part I · Module 3 · Chapter 3.6 · bool and Character Types

1. Two Groups, One Chapter

bool and the character types are grouped together here for a practical reason rather than a conceptual one: each is small, each is short to teach, and each has exactly one property that causes real trouble.

For bool, the trouble is people treating it as an integer.

For char, the trouble is people assuming it is signed.

2. bool Is a Logical Type

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
bool parity_error = false;
bool link_up      = true;

A bool has exactly two values: true and false. They are keywords, part of the language, and they are the only values a bool can hold.

That is worth saying plainly because of what bool is not:

bool is its own type. It is not a small integer that happens to be restricted to 0 and 1.

The distinction matters because the type is what tells the compiler, and the reader, what kind of question is being answered. parity_error answers a yes/no question. A type that says so is better than an int that holds 0 or 1 by convention, because the convention is not checked and the type is.

Measured on the machine used for this chapter, sizeof(bool) is 1. That is not one bit — Chapter 3.2 §4 explained why there is no type smaller than one char. A bool occupies a whole addressable unit and carries one bit of information.

3. bool Converts to and from Numbers

C++ does convert between bool and the numeric types, and you will see it, so here is what happens:

Azvya Education Pvt. Ltd.VLSI Mentor
boolconv.cpp — complete program
#include <iostream>

int main()
{
    bool link_up = true;
    bool idle    = false;

    std::cout << "link_up = " << link_up << '\n';
    std::cout << "idle    = " << idle << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
link_up = 1
idle    = 0

Going the other way, any non-zero number converts to true and zero converts to false.

Two notes, each with an owner elsewhere:

  • Printing 1 and 0 rather than the words is a formatting default. std::boolalpha changes it, and Chapter 23.2 owns stream formatting. Chapter 2.8 §3 flagged this when you first met it.
  • Where these conversions happen automatically, and the rules governing them, is Chapter 4.10.

The conversions existing does not make bool an integer. An int converts to a double too, and nobody concludes they are the same type. A conversion is a defined relationship between two types, which is the opposite of them being one type.

4. char Is an Integer Type Used for Text

Now the more interesting half.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
char status_letter = 'A';

Single quotes mean one character — Chapter 2.2 §5. What is stored is a number: the code that represents that character in the implementation's character set.

Azvya Education Pvt. Ltd.VLSI Mentor
charnum.cpp — complete program
#include <iostream>

int main()
{
    char status_letter = 'A';

    std::cout << "as a character = " << status_letter << '\n';
    std::cout << "as a number    = " << static_cast<int>(status_letter) << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
as a character = A
as a number    = 65

The same object, read two ways. This is Chapter 3.1 §5 once more: the storage holds a bit pattern, and what you get depends on the type through which you read it. Sent to a stream as a char, it prints the character. Converted to int first, it prints the code.

So char is an integer type. You can do arithmetic on it. What makes it special is that the language and the standard library treat it as the unit of text, and that sizeof(char) is 1 by definition (Chapter 3.2 §3).

5. Three Character Types, Not One

Here is the fact most treatments skip, and without it the next section makes no sense.

C++ has three distinct narrow character types:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
char           c;    // the text type
signed char    sc;   // a signed 8-bit-or-wider integer
unsigned char  uc;   // an unsigned one

They are three separate types. Not two with an alias:

Azvya Education Pvt. Ltd.VLSI Mentor
chardistinct.cpp — complete program
#include <iostream>
#include <type_traits>

int main()
{
    std::cout << "char is the same type as signed char?   "
              << std::is_same<char, signed char>::value << '\n';
    std::cout << "char is the same type as unsigned char? "
              << std::is_same<char, unsigned char>::value << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
char is the same type as signed char?   0
char is the same type as unsigned char? 0

Both zero. std::is_same answers a compile-time question about whether two types are the same type, and the answer is no in both cases. Plain char is a third type, distinct from both of the others, even though it must behave like one of them for storage and range.

All three are the same size — measured here, 1 each. The difference is in how the bits are interpreted, and in the fact that char is a separate type for the purposes of overload resolution and template matching, which you will meet from Module 6 onward.

6. Plain char Has Implementation-Defined Signedness

This is the property that catches people.

Whether plain char is signed or unsigned is implementation-defined. The standard does not say. It must match one of the other two, and which one is your platform's choice.

Measure it:

Azvya Education Pvt. Ltd.VLSI Mentor
charsign.cpp — complete program
#include <climits>
#include <iostream>

int main()
{
    std::cout << "CHAR_MIN = " << CHAR_MIN << '\n';
    std::cout << "plain char is "
              << (CHAR_MIN < 0 ? "signed" : "unsigned")
              << " on this implementation\n";
    return 0;
}

Example output on Apple clang 21, arm64 macOS:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
CHAR_MIN = -128
plain char is signed on this implementation

That is a measurement of one platform, not a C++ fact — the discipline from Chapter 3.2 §4. Plain char is signed on x86 Linux, macOS and Windows, and unsigned on several ARM Linux configurations. Both are fully conforming.

Why it matters: a char holding a byte value above 127 is a positive number on one platform and a negative number on another, from the same source file. Any comparison, any arithmetic, any use as an index then behaves differently — and nothing warns you, because both builds are correct C++.

7. The Loose End from Chapter 3.4

Chapter 3.4 §6 showed a surprise and promised the reason here:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    std::uint8_t error_count = 65;
    std::cout << error_count << '\n';        // prints A, not 65

Now it is explicable. std::uint8_t is required to be exactly 8 bits unsigned, and on mainstream implementations the type that fits is unsigned char — a character type. When you send a character type to a stream, the stream prints the character.

So the behaviour is not a quirk of the fixed-width types at all. It is this chapter's subject arriving one chapter early: std::uint8_t is a character type wearing a numeric name, and the stream treats it accordingly. The fix from 3.4 — convert to unsigned before printing — is the right one, and now you know why it is needed.

8. The Wider Character Types

C++ has more character types than the three narrow ones, for text that does not fit in a byte:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wchar_t   wide;       // width is implementation-defined
char16_t  utf16;      // for UTF-16 code units
char32_t  utf32;      // for UTF-32 code units

That is all this chapter says about them, and the restraint is deliberate. Text encoding is a real subject with real depth — what a code unit is, how multi-byte encodings work, why the number of characters in a string is not the number of bytes. Module 9 covers strings and is where it belongs.

For the work this curriculum targets, the narrow types carry almost everything: char for ASCII text in logs and messages, unsigned char or std::uint8_t for binary data.

9. Mistakes Worth Making Once

Using int for a yes/no answer. Nothing checks that it holds only 0 or 1, and a reader cannot tell whether 1 means "yes" or "one". Use bool.

Concluding bool is an integer because it converts to one. int converts to double too. A conversion is a relationship between two types, not evidence they are one type.

Expecting true to print as the word. It prints 1 by default. std::boolalpha changes that, in Chapter 23.2.

Assuming plain char is signed. It is on this machine and on x86 platforms; it is unsigned on some ARM Linux configurations. The standard does not say.

Storing binary data in a plain char. A payload byte above 127 is positive on one platform and negative on another. Use unsigned char or std::uint8_t.

Thinking char and signed char are the same type. Measured: they are not. Plain char is a third, distinct type.

Printing a std::uint8_t and getting a letter. It is a character type underneath. Convert to unsigned first — Chapter 3.4 §6.

10. What You Should Be Able to Say Now

  1. bool is its own type with exactly two values, true and false — not a small integer.
  2. sizeof(bool) is 1 here, which is one char, not one bit. There is no type smaller than char.
  3. bool converts to and from numbers: true is 1, false is 0, and any non-zero value converts to true. The conversions do not make it an integer.
  4. Printing 1/0 rather than words is a formatting default; std::boolalpha is Chapter 23.2.
  5. Use bool for a yes/no answer. An int holding 0 or 1 relies on an unchecked convention.
  6. char is an integer type used for text. Measured: 'A' prints as A through a char and as 65 through an int.
  7. There are three distinct narrow character types: char, signed char and unsigned char. Verified with std::is_same — plain char is a third type, not an alias for either.
  8. Whether plain char is signed is implementation-defined. Measured CHAR_MIN = -128, so it is signed here; it is unsigned on some ARM Linux builds. Both conform.
  9. The consequence: a byte above 127 in a char is positive on one platform and negative on another, with no warning.
  10. Use char for text, unsigned char (or std::uint8_t) for raw bytes, signed char rarely.
  11. std::uint8_t prints as a letter because it is unsigned char on mainstream implementations — a character type.
  12. wchar_t, char16_t and char32_t exist for wider text; Module 9 covers encoding.

11. Exercises

Exercise 1 — predict the output. Say what each line prints and why:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    bool link_up = true;
    std::cout << link_up << '\n';
    std::cout << (link_up + 1) << '\n';

Exercise 2 — choose the type. For each, pick bool, char, unsigned char or std::uint8_t, with one phrase of justification: (a) whether a parity check failed; (b) a letter in a status message; (c) one byte of packet payload; (d) a register's 8-bit value; (e) the number of errors found.

Exercise 3 — the platform split. A program stores packet bytes in a char array and compares each against 200. Describe what happens on a platform where char is signed versus one where it is unsigned, say whether either compiler is wrong, and give the fix.

Exercise 4 — three types. std::is_same<char, signed char>::value is 0. Explain what that tells you, and why it is surprising given that char must behave like one of the other two.

Exercise 5 — explain the letter. Without re-reading Chapter 3.4, explain why std::cout << std::uint8_t{65} prints A. Your answer should name the underlying type.

Exercise 6 — argue for bool. A colleague uses int ok = 1; and says it is clearer because it can later hold an error code. Give the specific objection, and say what they should write if they genuinely need more than two outcomes.

Exercise 7 — guarantee or measurement? Classify each: (a) sizeof(char) == 1; (b) CHAR_MIN == -128; (c) char, signed char and unsigned char are three distinct types; (d) true converts to 1.

12. Summary

  • bool is a logical type with two values. It converts to and from numbers; that does not make it an integer.
  • sizeof(bool) is 1 — one char, not one bit.
  • Use bool for yes/no. An int holding 0 or 1 is an unchecked convention.
  • char is an integer type used for text. The same object prints as A or 65 depending on the type you read it through.
  • Three distinct narrow character types, verified: plain char is neither signed char nor unsigned char.
  • Plain char's signedness is implementation-defined. Measured signed here; unsigned on some ARM Linux builds. Both conform.
  • char for text, unsigned char or std::uint8_t for raw bytes, signed char rarely. Follow this and the signedness question never arises.
  • std::uint8_t prints as a letter because it is unsigned char underneath — Chapter 3.4's surprise, explained.
  • Wider character types exist; encoding belongs to Module 9.

Every type in this module has now been introduced, and every example has quietly done the same thing: given the object a value on the line that declared it. That looks like one operation with one obvious spelling, and it is not. C++ has several distinct ways to give an object its initial value, they do not all mean the same thing, and one of them will reject a conversion that the others accept without a word. Chapter 3.7 takes them apart — and it is the chapter in this module whose habits will show up in every line of C++ you write afterwards.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.