Article by Ayman Alheraki on February 11 2026 06:13 AM
One of the most common questions among C and C++ developers is:
Are variable types truly “the same” between the two languages? And can we consider them fully compatible at the type-system level?
The precise answer is:
There is significant overlap in fundamental types and their representations. However, there is no complete equivalence in language rules, type semantics, and behavior—especially when we examine implicit conversions, enums, pointers, atomics, and modern alignment features.
This article compares C23 and C++23, focusing strictly on the type system—where they match and where they differ.
bool Milestone)Until C17, C supported Boolean types via a header:
bool x = true;With C23, this changed fundamentally:
bool is now a reserved keyword
true and false are keywords
<stdbool.h> is no longer required
So this is now valid:
bool ready = true;This brings C syntactically closer to C++. But syntactic similarity does not imply full semantic equivalence.
The following fundamental types exist in both C23 and C++23:
char, signed char, unsigned char
short, int, long, long long
float, double, long double
On most platforms, their memory representations are identical.
However, neither standard mandates exact bit sizes (except ordering relationships like sizeof(short) <= sizeof(int) <= sizeof(long)).
So practical compatibility depends on platform ABI, not just language specification.
bool: Similar Now — But Not Identical in ContextC23 and C++23 both define bool.
The difference is not usually size—but how the language treats the type.
bool is an integral type
Values normalize to 0 or 1
Implicit conversions are permissive
bool is also integral
Participates in overload resolution
Affects template specialization
Interacts with implicit conversion rules differently in some contexts
The type itself may look identical—but C++ surrounds it with a richer type system.
For ABI-stable interfaces, both languages provide:
int32_t, uint64_t
intptr_t, uintptr_t
size_t, ptrdiff_t
In C23 via <stdint.h> and <stddef.h>
In C++23 via <cstdint> and <cstddef> (typically under std::)
Here, compatibility is excellent. These are the recommended types for portable, low-level API design.
Pointer representation at the machine level is typically identical.
However, conversion rules differ significantly.
void* to T*In C23:
void* p = malloc(sizeof(int));int* x = p; // Allowed without castIn C++23:
void* p = std::malloc(sizeof(int));int* x = (int*)p; // Explicit cast requiredThis is not cosmetic. C++ enforces stricter type safety and requires explicit conversions.
NULL vs nullptr: A Subtle but Important DifferenceIn C23:
int* p = NULL;In C++23:
int* p = nullptr;nullptr has a dedicated type (std::nullptr_t) and prevents a classic C++ issue:
When functions are overloaded between int and pointer types, NULL may be treated as integer 0, leading to incorrect overload selection.
nullptr resolves this cleanly.
C23 does not introduce nullptr.
enum: The Largest Type-System DivergenceIn C23:
enum Color { RED, GREEN };Implicitly convertible to int
Weakly typed
In C++23, the same is possible—but modern C++ adds:
enum class Color : int { RED, GREEN };This introduces:
Strong typing
No implicit conversion to integer
Explicit control over underlying type
For large-scale systems, enum class dramatically improves type safety.
struct: Same Data Model, Different Object ModelIn C23, a struct is fundamentally a data aggregate.
In C++23, a struct may include:
Constructors and destructors
Member functions
Access control
Inheritance (if used)
However, there is a crucial compatibility zone:
If a C++ struct is:
Standard-layout
Trivially copyable
Without inheritance or virtual functions
Then it will typically match a C struct in memory layout.
This is the golden rule when designing C-compatible APIs in C++.
In C23:
_Atomic int counter;In C++23:
std::atomic<int> counter;Both rely on modern memory models. The difference lies in structure:
C: language-level keyword with procedural API
C++: template class with member functions and stronger abstraction
C++ provides a richer type-oriented interface around atomic operations.
_Generic vs TemplatesC23 includes _Generic for compile-time type selection:
C++23 offers templates and concepts:
More powerful abstraction
Stronger compile-time enforcement
Richer type relationships
This reflects a deep philosophical divergence in type-system design.
We can analyze compatibility at three levels:
Fundamental types and fixed-width integers are largely compatible.
C23 has moved closer to C++ (notably with bool, true, false, and alignment features).
C++ remains significantly richer and stricter due to:
Overloading
Templates and concepts
enum class
References
Object model rules
nullptr
Stronger type constraints
If you are building cross-language libraries or stable APIs:
Prefer int32_t, uint64_t over int and long
Avoid exposing C++-specific constructs across ABI boundaries
Use extern "C" in C++ for public interfaces
Keep shared structs standard-layout and trivial
Avoid mixing object models across language boundaries
This approach gives you the best of both worlds:
C++ power internally
C compatibility externally