SimplifyC++ Article
C23 vs C++23: Are Variable Types Really Identical?
C23 vs C++23: Are Variable Types Really Identical?
A Practical Comparison of the Type Systems in Modern C and Modern C++
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.
1) What Changed in C23? (The bool Milestone)
Until C17, C supported Boolean types via a header:
bool x = true;With C23, this changed fundamentally:
boolis now a reserved keywordtrueandfalseare 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.
2) Fundamental Types: Where True Compatibility Exists
The following fundamental types exist in both C23 and C++23:
char,signed char,unsigned charshort,int,long,long longfloat,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.
3) bool: Similar Now — But Not Identical in Context
C23 and C++23 both define bool.
The difference is not usually size—but how the language treats the type.
In C23:
boolis an integral typeValues normalize to
0or1Implicit conversions are permissive
In C++23:
boolis also integralParticipates 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.
4) Fixed-Width Integer Types: The Engineering Foundation
For ABI-stable interfaces, both languages provide:
int32_t,uint64_tintptr_t,uintptr_tsize_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.
5) Pointers: Identical Representation, Different Rules
Pointer representation at the machine level is typically identical.
However, conversion rules differ significantly.
Conversion from 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.
6) NULL vs nullptr: A Subtle but Important Difference
In 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.
7) enum: The Largest Type-System Divergence
In C23:
enum Color { RED, GREEN };Implicitly convertible to
intWeakly 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.
8) struct: Same Data Model, Different Object Model
In 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++.
9) Atomics: Keyword vs Template Type
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.
10) _Generic vs Templates
C23 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.
Final Engineering Summary
We can analyze compatibility at three levels:
1) Representation & ABI Level
Fundamental types and fixed-width integers are largely compatible.
2) Syntax Level
C23 has moved closer to C++ (notably with bool, true, false, and alignment features).
3) Semantic & Type-System Level
C++ remains significantly richer and stricter due to:
Overloading
Templates and concepts
enum classReferences
Object model rules
nullptrStronger type constraints
Practical Advice for C and C++ Developers
If you are building cross-language libraries or stable APIs:
Prefer
int32_t,uint64_toverintandlongAvoid exposing C++-specific constructs across ABI boundaries
Use
extern "C"in C++ for public interfacesKeep 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