Skip to content

96 Advanced The Hundred

The bool That Was Both

a flag that answers yes to every question

unsigned char packet[] = {0x02};   // one byte, straight off the wire

int main() {
    bool encrypted;
    std::memcpy(&encrypted, packet, 1);

    if (encrypted)
        std::cout << "packet is encrypted\n";
    if (!encrypted)
        std::cout << "packet is not encrypted\n";

    std::cout << "encrypted        = " << encrypted << "\n";
    std::cout << "with boolalpha   = " << std::boolalpha << encrypted << "\n";
    std::cout << "static_cast<int> = " << static_cast<int>(encrypted) << "\n";
}

Build it and run it. How many of those two messages print?

Answer

Both. Typical output (GCC 11 on x86-64, no optimization):

packet is encrypted
packet is not encrypted
encrypted        = 2
with boolalpha   = true
static_cast<int> = 2

At -O1 and above the second message disappears — and encrypted still prints as 2.

Why

A bool may hold only true or false, and the compiler generates code that counts on it. GCC tests if (encrypted) with testb %al, %al — nonzero, so true — but negates with xorl $1, %eax: flipping bit 0 is all "not" needs to mean when the byte is 0 or 1. Yours is 2, and 2 ^ 1 == 3 is nonzero too, so both branches run. Any other byte pattern in a bool is undefined behavior, so neither build is wrong: from -O1 on GCC turns the negation into a compare against zero and only the first message survives, while the raw 2 still sails into operator<<. -Wall -Wextra is silent; -fsanitize=undefined catches it:

main.cpp:10:5: runtime error: load of value 2, which is not a valid value for type 'bool'

The fix

A byte from a file, a socket, or an uninitialized struct is not a bool — never memcpy or reinterpret_cast one into place. Ask a question, and let the compiler build the answer:

bool encrypted = packet[0] != 0;   // 0 or 1, always

Takeaway: undefined behavior is not a wrong value — it is the compiler and the machine disagreeing about what your program says.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo — then again with -O2.

Open in Compiler Explorer ↗ Quiz this entry