Beyond the basic operators, C's arithmetic expressions are governed by a precise set of rules that dictate how types are handled, what happens at their limits (overflow), and how expressions with side effects are evaluated.
1. The Usual Arithmetic Conversions in Detail
When an expression mixes different numeric types, the compiler performs implicit conversions to find a common type. This process is highly defined.
1. Integer Promotion: First, any operands of a type "smaller" than int
(like char
and short
int
) are promoted to int
or unsigned int
.
2. Type Hierarchy:
After integer promotion, the compiler finds a common type by moving up a
hierarchy. The general rule is to convert the "lower" type to the
"higher" type. The hierarchy is roughly: long double
> double
> float
> unsigned
long long
> long long
> unsigned long
> long
> unsigned
int
> int
.
The Unsigned Trap
A common logical bug arises when mixing signed and unsigned integers, as the signed value is converted to unsigned.
C
#include <stdio.h>
int main() {
unsigned
int a =
10;
int b =
-20;
// To compare 'a' and 'b', 'b' is converted to unsigned.
// The bit pattern for -20 becomes a very large positive unsigned integer.
if (a > b) {
printf(
"10 > -20, as expected.\n");
}
else {
printf(
"This is unexpected: 10 is NOT greater than -20.\n");
// This prints!
}
return
0;
}
size=2 width="100%" align=center>
2. Overflow and Underflow Behavior 🌊
What happens when a calculation exceeds the maximum value a type can hold is critical.
·
Unsigned
Integer Overflow: This is well-defined.
The value wraps around using modulo arithmetic. For an N-bit integer, the
result is value
% 2^N
.
C
unsigned
char x =
255;
// Max value for unsigned char
x++;
// x is now 0 (wraps around)
· Signed Integer Overflow: This is Undefined Behavior (UB). The C standard places no requirements on what should happen. A program might crash, produce a garbage value, or appear to wrap around (though you cannot rely on this). Compilers use the fact that it's UB to perform aggressive optimizations, which can lead to bizarre results.
·
Floating-Point
Overflow: This is well-defined
by the IEEE 754 standard. A calculation that exceeds the maximum representable
value will result in infinity
(INF
).
C
double big =
1.0e308;
printf(
"%f\n", big *
10.0);
// Prints "inf"
size=2 width="100%" align=center>
3. Expressions with Side Effects and Sequence Points
An expression has
a side effect if it modifies the state of a variable (e.g., x++
, y = 10
). C has sequence points (like the semicolon ;
) where all side effects from the previous statement
are guaranteed to be complete.
The Rule: It is undefined behavior to modify a variable more than once between two sequence points.
Example of Undefined Behavior
C
#include <stdio.h>
int main() {
int i =
5;
// UB: The value of 'i' is both read and modified twice with no
// sequence point. The compiler is free to evaluate this in any order.
// The result is completely unpredictable.
int result = i++ + i++;
printf(
"Result is: %d\n", result);
// Could print 10, 11, 12, or something else
printf(
"Final i is: %d\n", i);
// Will be 7
return
0;
}
size=2 width="100%" align=center>
4. Compound Literals (C99 and later)
A compound literal is an unnamed object (an lvalue) created on-the-fly within an expression. This allows you to create arrays or structs without needing to declare a named variable first.
·
Syntax: (type_name){initializer_list}
This is particularly useful for passing temporary arrays or structs to functions.
Example: Passing an unnamed array
C
#include <stdio.h>
// A function that takes a pointer to an array and its size
void print_array(int *arr, int size) {
for (
int i =
0; i < size; i++) {
printf(
"%d ", arr[i]);
}
printf(
"\n");
}
int main() {
// We can create and pass an array directly in the function call
// using a compound literal.
print_array((
int[]){
10,
20,
30,
40},
4);
return
0;
}
The C preprocessor
is a text-processing tool that scans your source code for special directives
(lines beginning with #
) and
modifies the code before it is passed to the actual compiler.