unsigned / signed / const / volatile
| Since: | C89(1989) |
|---|
Type qualifiers are keywords added to basic types to change the behavior of a variable. They include unsigned/signed for controlling whether a value can be negative, const for preventing a value from being changed, and volatile for controlling compiler optimization.
Syntax
unsigned int variableName; unsigned char variableName; // Declares a signed integer type (can hold negative values). This is the default. signed int variableName; // Declares a constant whose value cannot be changed. const type variableName = value; // Suppresses compiler optimization for this variable. volatile type variableName; // You can also combine multiple qualifiers. const unsigned int variableName = value;
Type Qualifier List
| Qualifier | Description |
|---|---|
| unsigned | Makes the integer type unsigned. It cannot hold negative values, but the upper limit for positive values is roughly doubled. For example, unsigned int can hold values from 0 to 4294967295. |
| signed | Makes the integer type signed. This is the default behavior and is usually omitted, but it can be specified explicitly for char, whose signedness varies by implementation. |
| const | Declares a variable as a constant. It must be initialized at the time of declaration, and its value cannot be assigned or changed afterward. |
| volatile | Suppresses compiler optimization. Use this for variables whose values may change from outside the program, such as interrupt handlers or hardware registers. |
Sample Code
sample_unsigned_const.c
#include <stdio.h>
int main(void) {
/* The 'u' suffix marks an unsigned integer constant. */
unsigned int population = 1280000000u;
printf("Population: %u\n", population);
unsigned char byte = 255;
printf("Byte value: %u\n", byte);
/* Unsigned types wrap around: 0 - 1 becomes the maximum value. */
unsigned int u = 0;
u = u - 1;
printf("unsigned wraparound: %u\n", u);
const double PI = 3.14159265358979;
const int MAX_SIZE = 100;
printf("Pi: %f\n", PI);
printf("Max size: %d\n", MAX_SIZE);
/* PI = 3.0; */ /* Compile error — const cannot be reassigned. */
/* volatile prevents the compiler from caching this variable. */
volatile int sensor_value = 0;
printf("Sensor value: %d\n", sensor_value);
/* const volatile is used for read-only hardware registers. */
/* const volatile int *reg = (int *)0x40000000; */
return 0;
}
Run the following command:
gcc unsigned_const.c -o unsigned_const ./unsigned_const Population: 1280000000 Byte value: 255 unsigned wraparound: 4294967295 Pi: 3.141593 Max size: 100 Sensor value: 0
const Pointer and Buffer Protection
Combining const with a pointer tells the compiler that the pointed-to data will not be modified — a common pattern for read-only function parameters.
const_pointer.c
#include <stdio.h>
/* const char *: the string pointed to will not be modified. */
int count_char(const char *s, char c) {
int n = 0;
for (; *s; s++) {
if (*s == c) n++;
}
return n;
}
int main(void) {
const char *msg = "Son Goku is the strongest.";
printf("Count of 'o': %d\n", count_char(msg, 'o'));
printf("Count of 's': %d\n", count_char(msg, 's'));
/* const array elements cannot be assigned. */
const int scores[] = {95, 88, 72, 100};
/* scores[0] = 0; */ /* Compile error */
int total = 0;
for (int i = 0; i < 4; i++) {
total += scores[i];
}
printf("Total: %d\n", total);
return 0;
}
Run the following command:
gcc const_pointer.c -o const_pointer ./const_pointer Count of 'o': 3 Count of 's': 2 Total: 355
Common Mistakes
Common Mistake: Mixing Signed and Unsigned Types
When a signed value is compared or computed with an unsigned value, the signed side is converted to unsigned, producing unexpected results.
unsigned_signed_ng.c
#include <stdio.h>
int main(void) {
unsigned int u = 1;
int s = -1;
/* OK: cast to the same type first */
if (s < (int)u) {
printf("OK: compared after casting\n");
}
/* Show what happens without casting */
if ((unsigned int)s > u) {
/* (unsigned int)(-1) == 4294967295, which is > 1 */
printf("As unsigned, -1 becomes: %u\n", (unsigned int)s);
}
/* OK: compare using the same type */
int ui = (int)u;
if (s < ui) {
printf("OK: same-type comparison (%d < %d)\n", s, ui);
}
return 0;
}
Run the following command:
gcc unsigned_signed_ng.c -o unsigned_signed_ng ./unsigned_signed_ng OK: compared after casting As unsigned, -1 becomes: 4294967295 OK: same-type comparison (-1 < 1)
Notes
The unsigned type extends the range of positive values by not supporting negative numbers. Mixing signed and unsigned types in arithmetic can produce unexpected results. Unsigned types also take precedence in comparison operations, so extra care is required.
const tells the compiler that a value will not be changed, allowing invalid assignments to be caught at compile time. It provides better type checking than macro-based constants using #define, so using const is recommended in C99 and later. For more on macros, see #include / #define (Constants).
volatile is important in hardware control and embedded programming. Compilers may apply optimizations such as register caching, but these optimizations can cause incorrect behavior for variables whose values may change externally. You can check the actual size of each type using sizeof.
If you find any errors or copyright issues, please contact us.