unsigned / signed / const / volatile
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
// Declares an unsigned integer type (non-negative values only). 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
#include <stdio.h>
int main(void) {
// unsigned int holds non-negative integers.
unsigned int population = 1280000000u; // The 'u' suffix denotes an unsigned constant.
printf("Population: %u\n", population); // Outputs: Population: 1280000000
// unsigned char is useful for processing byte data.
unsigned char byte = 255;
printf("Byte value: %u\n", byte); // Outputs: Byte value: 255
// Observe the difference in overflow behavior.
unsigned int u = 0;
u = u - 1; // For unsigned types, 0 - 1 wraps around to the maximum value.
printf("unsigned wraparound: %u\n", u); // Outputs: 4294967295
// Declare constants with const.
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; // This would cause a compile error.
// volatile is used for hardware registers and interrupt variables.
volatile int sensor_value = 0;
// The compiler will not optimize away accesses to this variable.
printf("Sensor value: %d\n", sensor_value);
// Combining const and volatile (e.g., a read-only hardware register).
// const volatile int *reg = (int *)0x40000000;
return 0;
}
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.