Developer Solutions
In-depth answers to today's top programming questions.
Is It Safe to Cast unsigned char to char in C?
May 31, 2026 Source Question
When working with C standard library functions like getc() or fgetc(), you often receive an int that represents either an unsigned character value (typically 0 to 255) or EOF. Once you have checked for EOF, you are left with an unsigned char value stored in an int. But what happens when you want to convert this back into a standard char (which is signed on many systems)? Is a direct cast safe, or does it trigger undefined behavior?
The Strict ISO C Standard Perspective
According to the ISO C standard (specifically C99, C11, and C23), converting an out-of-range value to a signed integer type is implementation-defined. Section 6.3.1.3 of the C11 standard states:
"Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised."
Because a signed 8-bit char typically has a range of -128 to 127, any value from 128 to 255 cannot be represented. Therefore, technically speaking, casting an unsigned char greater than 127 to a signed char is implementation-defined and could theoretically trigger a signal (though virtually no modern compiler does this).
The POSIX Guarantee
If you are compiling for a POSIX-compliant system (like Linux or macOS), the rules are much stricter and safer. POSIX requires that signed integers use two's complement representation. Under POSIX, casting an out-of-range value to a signed integer simply wraps around using modular arithmetic, meaning it is entirely safe and well-defined.
How to Safely Convert unsigned char to char
Depending on how strict your portability requirements are, you have a few ways to handle this conversion.
1. The Pragmatic Approach (Implicit/Explicit Cast)
In 99.9% of real-world applications, you can simply assign the value directly. Modern compilers like GCC, Clang, and MSVC define this implementation-defined behavior to behave as a two's complement wrap-around.
int c = fgetc(fp);
if (c != EOF) {
char ch = (char)c; // Safe on virtually all modern platforms
}
2. The Pedantic, Pure ISO C Approach
If you are writing highly portable code for esoteric systems where you cannot guarantee the compiler's implementation-defined behavior, you can use a safe conversion function that avoids out-of-range assignments altogether:
#include <limits.h>
static inline char uchar_to_char(unsigned char uch) {
if (CHAR_MIN < 0 && uch > CHAR_MAX) {
return (char)(((int)uch) - ((int)UCHAR_MAX + 1));
}
return (char)uch;
}
On any optimizing compiler, this function will be compiled down to a simple no-operation (zero assembly instructions), while technically remaining 100% compliant with the strict letter of the ISO C standard.
3. The Bit Reinterpretation Approach
Another standard-compliant way to copy the bit pattern without triggering signed conversion rules is using memcpy. Modern compilers optimize this into a direct register move:
#include <string.h>
char safe_cast(unsigned char uch) {
char c;
memcpy(&c, &uch, 1);
return c;
}
Conclusion
For almost all modern applications—especially those targeting POSIX systems—a simple cast or implicit assignment from unsigned char to char is completely safe. However, if you are targeting highly specialized embedded systems and require absolute compliance with the ISO C standard without relying on implementation-defined behavior, using a safe wrapper or memcpy is the most robust way to go.