C11 UTF-16 and UTF-32 Character Support
Prior to the C11 standard, the C language lacked native, portable
data types and literal prefixes for fixed-width Unicode encodings,
relying instead on the implementation-defined wchar_t. The
C11 revision directly addressed this limitation by introducing the
char16_t and char32_t types, dedicated string
and character literal prefixes (u and U), and
the <uchar.h> header. This article examines how C11
formalized explicit support for UTF-16 and UTF-32 to deliver reliable,
cross-platform Unicode manipulation.
The Limitations of
wchar_t
Before C11, wide characters were handled through
wchar_t. While flexible, the C standard does not dictate
the size or encoding of wchar_t. On Windows platforms,
wchar_t is typically 16 bits wide and encoded using UTF-16,
whereas on most Unix-like systems, it is 32 bits wide and encoded using
UTF-32. This discrepancy made it difficult to write portable code that
required a specific, predictable encoding size.
New Types:
char16_t and char32_t
To resolve this ambiguity, C11 introduced two distinct typedefs via
the new standard library header <uchar.h>:
char16_t: An unsigned integer type used for 16-bit character representation, sized to hold at least 16 bits and designed specifically for UTF-16 code units.char32_t: An unsigned integer type used for 32-bit character representation, sized to hold at least 32 bits and designed specifically for UTF-32 code units.
Both types are defined as typedefs over existing unsigned integer
types (such as uint_least16_t and
uint_least32_t).
Dedicated Character and String Literals
C11 introduced dedicated prefixes that allow developers to create literals explicitly encoded in UTF-16 and UTF-32:
UTF-16 Literals: Using the lowercase
uprefix generates a literal of typechar16_tfor a character or an array ofchar16_tfor a string:char16_t single_unit = u'A'; char16_t text[] = u"Hello, World!";UTF-32 Literals: Using the uppercase
Uprefix generates a literal of typechar32_tfor a character or an array ofchar32_tfor a string:char32_t single_cp = U'A'; char32_t text32[] = U"Hello, World!";
Additionally, C11 defined universal character names
(\uXXXX for 16-bit values and \UXXXXXXXX for
32-bit values) within these literals, guaranteeing that the specified
Unicode code point is encoded as expected.
Standard Conversion
Functions in <uchar.h>
Along with new types and literals, C11 added standard library
functions to convert between multibyte characters (such as UTF-8 stored
in char arrays) and the new 16-bit and 32-bit character
types:
mbrtoc16(): Converts a multibyte character to achar16_trepresentation.c16rtomb(): Converts achar16_tvalue to its corresponding multibyte character representation.mbrtoc32(): Converts a multibyte character to achar32_trepresentation.c32rtomb(): Converts achar32_tvalue to its corresponding multibyte character representation.
These functions utilize a state parameter (mbstate_t) to
correctly handle variable-length encodings like UTF-16 surrogate
pairs.
Verification Macros
To ensure that implementations adhere strictly to the Unicode standard, C11 defined two predefined feature-test macros:
__STDC_UTF_16__: Defined if values of typechar16_tare guaranteed to use UTF-16 encoding.__STDC_UTF_32__: Defined if values of typechar32_tare guaranteed to use UTF-32 encoding.
By introducing explicit types, dedicated literal prefixes, conversion routines, and validation macros, C11 established an unambiguous framework for portable UTF-16 and UTF-32 programming in C.