Category: C & C++

  • C Boolean Type: _Bool, bool, true, and false

    C Boolean Type: _Bool, bool, true, and false

    The C boolean type is based on the built-in _Bool type. A Boolean object stores either 0 or 1, although C conditions can evaluate any integer or pointer value as false or true. The value 0 and a null pointer are false; nonzero integers and non-null pointers are true.

    For C99 through C17 code, stdbool.h supplies the familiar bool, true, and false names. Use either the built-in type or the header-provided names consistently within a project.

    The C boolean type: _Bool stores normalized 0 or 1

    _Bool is a built-in type in C99 and later. When an integer, pointer comparison, or other scalar value is assigned to _Bool, C converts it to a normalized Boolean value: zero becomes 0, and any nonzero value becomes 1.

    This complete program assigns both zero and a nonzero integer to _Bool variables:

    • #include <stdio.h>
    • int main(void) {
    • _Bool enabled = 7;
    • _Bool disabled = 0;
    • printf(“%d %d\n”, (int)enabled, (int)disabled);
    • return 0;
    • }

    The output is 1 0. The explicit casts make the intended integer output clear. A _Bool value also undergoes integer promotion when passed to a variadic function such as printf, but the cast is a useful portable and readable pattern.

    The C bool type: stdbool.h aliases and complete declarations

    The C bool type is available through the standard header stdbool.h. In C99 through C17, that header defines bool as an alias-like macro for _Bool, while true and false represent 1 and 0. The header lets declarations read naturally without requiring a C++-style built-in bool keyword.

    This complete example declares and prints two Boolean values:

    • #include <stdbool.h>
    • #include <stdio.h>
    • int main(void) {
    • bool valid = true;
    • bool complete = false;
    • printf(“%s %s\n”, valid ? “true” : “false”, complete ? “true” : “false”);
    • return 0;
    • }

    Include stdbool.h before using bool, true, or false. Without the header, those names are not portable in traditional C versions that provide them as macros.

    How the bool type in C handles conditions and assignments

    The bool type in C is useful for storing a condition’s result, but an if statement does not require a Boolean object. C evaluates the controlling expression directly: integer zero is false, every nonzero integer is true, a null pointer is false, and every non-null pointer is true.

    For example, if (count) enters its block when count is nonzero. Similarly, if (buffer) enters its block when buffer points to an object. To normalize either value for storage, assign it to bool:

    • bool has_items = count;
    • bool has_buffer = buffer;

    Both assignments store only true or false. Comparisons and logical operators produce an integer result of 0 or 1, which can also be assigned directly to bool.

    Boolean functions, return values, and portable output

    A Boolean function should include stdbool.h and return bool when callers need a true-or-false result. A comparison such as n % 2 == 0 produces 1 or 0, and returning it from a bool function makes the interface explicit.

    • #include <stdbool.h>
    • #include <stdio.h>
    • bool is_even(int n) { return n % 2 == 0; }
    • int main(void) {
    • printf(“%s\n”, is_even(8) ? “true” : “false”);
    • return 0;
    • }

    The conditional operator converts the Boolean result into one of two string literals, making %s a portable way to print the words true and false. For numeric output, use printf(“%d\n”, (int)is_even(8)) to print 1 or 0.

  • Bit Shifting in C: How > Move Bits

    Bit Shifting in C: How << and >> Move Bits

    Bit shifting in C and C++ moves an integer’s bits left or right with the << and >> operators. A C++ bit shift is easiest to verify with a fixed-width unsigned value: left shifts add zero bits on the right, while unsigned right shifts add zero bits on the left. The important boundaries are the promoted type’s width, the shift count, and whether the operand is signed.

    How does bit shifting in C use << and >>?

    The expression value << count moves each bit toward a more significant position. Bits that leave the type are discarded. The expression value >> count moves bits toward less significant positions. For an unsigned operand, zero bits enter from the left.

    For unsigned values, shifting left by n positions is equivalent to multiplying by 2n when the result remains within the available width. Shifting right by n positions is equivalent to dividing by 2n and discarding the remainder. A shift is not a rotation: discarded bits do not reappear at the other end.

    How do binary traces explain a C++ bit shift?

    These examples use 8-bit storage to make the movement visible. The binary notation shows the stored low eight bits.

    Left shift: uint8_t x = 0x2D; starts as 00101101. After x << 2, the trace is:

    00101101 << 2 = 10110100

    The value changes from decimal 45 to decimal 180. The two zeros entering on the right replace the two high bits that fall off the left.

    Unsigned right shift: uint8_t x = 0xB4; starts as 10110100. After x >> 2, the trace is:

    10110100 >> 2 = 00101101

    The result is decimal 45. In actual C and C++ expressions, small integer types undergo integer promotion first. If int can represent every uint8_t value, x is promoted to int; assigning the result back to uint8_t stores only the low eight bits.

    How do shifts create masks, fields, and powers of two?

    A shift creates a single-bit mask efficiently. With a 32-bit unsigned value, UINT32_C(1) << 5 produces 0x00000020, which selects bit 5. This represents 25. The count must stay within the valid range for the operand’s promoted type.

    To create a mask for the lowest four bits, use (UINT32_C(1) << 4) – 1, producing 0x0000000F. To extract an eight-bit field beginning at bit 8, use:

    (word >> 8) & UINT32_C(0xFF)

    The right shift moves the field to the low end, and the mask removes unrelated bits. To insert a bounded field, mask the source value before shifting it, then combine it with the destination using bitwise OR.

    How do signedness, width, and shift counts affect results?

    Both operands undergo integer promotion, and the result type is the promoted type of the left operand. Therefore, the width that controls a shift is not always the declared width. A uint8_t commonly promotes to int, so its shift count is checked against int’s width rather than eight bits. A uint32_t normally remains an unsigned 32-bit type because int cannot represent all its values.

    The shift count must be nonnegative and less than the bit width of the promoted left operand. A count equal to that width, or larger, produces undefined behavior in C and C++. Validate a runtime count before shifting; for a 32-bit value, a signed count must satisfy count >= 0 && count < 32.

    Unsigned left shifts have defined modulo behavior: high bits are discarded. Signed left shifts are riskier because a result that cannot be represented can cause undefined behavior. Convert to an appropriately sized unsigned type when that modulo behavior is intended.

    Right-shifting an unsigned value is logical and fills with zeros. Right-shifting a signed negative value is not a portable C shortcut: C makes that result implementation-defined, and language-version differences matter in C++. Use unsigned operands when the bit pattern, rather than an arithmetic sign, must be preserved.

  • Size of an Array in C++: Count Elements Safely

    Size of an Array in C++: Count Elements Safely

    Use std::size(array) for a built-in array in C++17 and later. You can also divide sizeof(array) by sizeof(array[0]) while the compiler still knows the complete array type. Both methods return the number of elements, not the array’s size in bytes.

    For example, a five-element array has a C++ array length of 5. The key limitation is array-to-pointer decay: after an array becomes a pointer, neither method can recover the original element count. This guide shows how to get an array’s size in C++ safely and when to use .size() instead.

    Size of an Array in C++ with std::size: a five-element example

    std::size is the clearest standard-library option for counting elements in a built-in array. Include <iterator>, then pass the array directly to the function. It returns the array’s element count as a size type, normally std::size_t.

    Example: #include <cstddef>; #include <iterator>; int scores[] = { 12, 18, 24, 31, 40 };; std::size_t count = std::size(scores);. The value of count is 5.

    The compiler determines the bound from the array type, which is int[5] in this example. You do not need to repeat the bound or maintain a separate constant. Unlike a member function, std::size works with a built-in array even though built-in arrays do not have a .size() member.

    This overload preserves the array type by receiving it through a reference. That detail matters because passing the array in the wrong context can change it into a pointer before the count is calculated.

    Use the C++ sizeof operator for an array: division returns 5

    The traditional C++ sizeof for an array formula divides the total number of bytes occupied by the array by the number of bytes occupied by one element:

    Formula: sizeof(array) / sizeof(array[0])

    Applied to the same five-element array, the code is std::size_t count = sizeof(scores) / sizeof(scores[0]);. The result is 5. The numerator represents all five elements, while the denominator represents one int. The calculation therefore produces an element count regardless of the platform’s actual int size.

    sizeof(scores) is not itself the array length. It reports the array’s total storage in bytes. Similarly, sizeof(scores[0]) reports the storage for one element. The division is valid only when scores still has its array type, such as in the same scope where it was declared or in a function that receives an array by reference.

    std::size is usually preferable in modern C++ because it states the intent directly and avoids repeating the element expression. The sizeof formula remains useful in older language standards and in low-level code where the array type is known.

    Why is C++ array size lost after array-to-pointer decay?

    When a built-in array is passed to an ordinary function parameter, it normally changes, or decays, into a pointer to its first element. Bracket notation in a parameter does not prevent this adjustment. These declarations therefore describe the same effective parameter:

    void report(const int values[]); and void report(const int* values);

    Inside report, values is a pointer, not an array. Consequently, sizeof(values) returns the pointer’s storage size, while sizeof(values[0]) returns the size of one integer. Dividing those values produces an unrelated result rather than the original number of elements. The result may vary by platform and pointer type, so never apply the array division formula to a pointer.

    std::size(values) also cannot count the original array in this function. Its array overload requires an actual array, and a pointer does not contain a record of how many elements were allocated or where the array ends. A pointer might refer to one element, a dynamically allocated block, or only a subrange of a larger array.

    Pass the count separately when a function intentionally accepts a pointer:

    void report(const int* values, std::size_t count);

    Alternatively, accept the built-in array by reference so its bound remains available:

    template <typename T, std::size_t N> constexpr std::size_t countOf(const T (&values)[N]) { return N; }

    Calling countOf(scores) returns 5. The reference parameter prevents array-to-pointer decay, and N captures the bound at compile time.

    How do std::array and std::vector report C++ array length? .size() returns the element count

    std::array is a fixed-size container that keeps its element count as part of its type. Its .size() member returns the number of stored elements:

    std::array<int, 5> fixedScores{ 12, 18, 24, 31, 40 };; fixedScores.size() returns 5.

    The value cannot change during the lifetime of that std::array. An std::array<int, 0> is valid, and its .size() returns 0. Use this container when you want array-like storage with standard container interfaces, iterators, and a reliable size operation.

    std::vector stores a variable number of elements. Its .size() member returns the number of elements currently stored, not the amount of memory reserved:

    std::vector<int> dynamicScores{ 12, 18, 24, 31, 40 };; dynamicScores.size() returns 5. After dynamicScores.push_back(47), it returns 6. Use .capacity() only when you need the allocated storage capacity; it is not the C++ array length.