C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
128: Unicode Character Support with uchar.h
I was working on a small piece of middleware last week that needed to handle user nicknames from a global player base. I started off the old-fashioned way, using standard char arrays, assuming UTF-8 encoding would just "work" because that's what most modern terminals use. But as soon as I tried to calculate the length of a name to truncate it for a UI element, everything broke.
The Byte Trap
Let's look at what happened. I tried to process the Japanese greeting "こんにちは" (Konnichiwa). In my head, that's five characters. But look at what C tells me:
#include <stdio.h>
#include <string.h>
int main() {
char *greeting = "こんにちは";
printf("String: %s\n", greeting);
printf("Length: %zu\n", strlen(greeting));
return 0;
}
When I ran this, the output for the length wasn't 5. It was 15. Why? Because strlen doesn't count characters; it counts bytes until it hits a null terminator. In UTF-8, those Japanese characters take up three bytes each. If I had tried to truncate this string at index 5 to "save space," I would have sliced a multi-byte character right down the middle, leaving the terminal to print a weird replacement character (usually a black diamond with a question mark) because the resulting byte sequence was invalid.
Bringing in uchar.h
I realized I needed a way to treat characters as fixed-width units regardless of how they're encoded for transmission. That's where <uchar.h> comes in. It was introduced in C11 to give us types that actually map to Unicode standards: char16_t for UTF-16 and char32_t for UTF-32.
I decided to pivot to char16_t. To do this, I have to use a specific prefix for my string literals. If I just use double quotes, I'm back to the char byte-array mess. I need the u prefix.
#include <stdio.h>
#include <uchar.h>
int main() {
// The 'u' prefix tells the compiler this is a UTF-16 string
char16_t *u_greeting = u"こんにちは";
// Since this is an array of char16_t,
// the size is now predictable.
size_t len = sizeof(u_greeting[0]) * 5; // Simplified for this example
printf("Unicode characters are now handled as 16-bit units.\n");
return 0;
}
Now, each element in u_greeting is a char16_t. I'm no longer guessing how many bytes a character takes; I know each one is exactly 2 bytes. This makes indexing and slicing significantly safer. If I need even more headroom—say, for some obscure ancient script or a massive library of emojis—I can use char32_t with the U prefix.
The Printing Problem
Here is where I hit a wall. I tried to printf("%s", u_greeting) and the compiler started screaming at me. printf expects a char *, and char16_t * is a completely different type. C's standard library is, frankly, pretty stunted when it comes to printing Unicode directly to the console. It doesn't have a printf equivalent for char16_t.
To actually see my work, I had to realize that uchar.h provides the types, but it doesn't provide the I/O. To print these, you typically have to convert them back to UTF-8 (which the terminal understands) or use OS-specific APIs (like WriteConsoleW on Windows). For my debugging, I wrote a quick loop to print the hexadecimal values of the characters just to verify they were stored correctly:
#include <stdio.h>
#include <uchar.h>
int main() {
char16_t *u_greeting = u"こんにちは";
// Manually iterating through the UTF-16 string
for (int i = 0; i < 5; i++) {
printf("Char %d: U+%04X\n", i, (unsigned short)u_greeting[i]);
}
return 0;
}
This approach confirms that the characters are stored as their actual Unicode code points. It's a bit of a chore to get them onto the screen, but having char16_t and char32_t means my internal logic—sorting, searching, or truncating—is finally based on actual characters rather than random byte offsets.
📋 Practical Task
Exercise: Unicode Character Code Point Extractor
Build a program that allows a user to define a string containing a mix of standard ASCII, emojis, and non-Latin characters using char32_t. Your program should not attempt to print the string itself (as printf cannot handle char32_t), but instead must iterate through the string and print the hexadecimal Unicode code point for every character present.
- Use the
Uprefix for achar32_tstring literal. - Include a string that contains at least one emoji (e.g.,
U"Hello 🌍"). - Use a loop to print each character in the format:
Index [X]: U+XXXXX. - Ensure you correctly identify the end of the string by looking for the null terminator
\0.
There are no comments for now.