Arithmetic operators

C has the usual arithmetic operators: +, -, *, /, and % for the remainder. There is no operator for powers (^ is bitwise XOR, below); use pow from <math.h> or multiply.

Integer division and remainder

When both operands are integers, / is integer division: the result is an integer, and since C99 the fraction is always cut off toward zero. The remainder a % b is defined so that (a / b) * b + a % b equals a, which gives it the sign of the left operand. As soon as one operand is a floating-point number, the division is a normal one:

#include <stdio.h>

int main(void)
{
    int a = 7, b = 2;

    printf(" 7 /  2 = %d     7 %%  2 = %d\n", a / b, a % b);
    printf("-7 /  2 = %d    -7 %%  2 = %d\n", -a / b, -a % b);
    printf(" 7 / -2 = %d     7 %% -2 = %d\n", a / -b, a % -b);
    printf(" 7 / 2.0 = %.1f\n", a / 2.0);
    printf("(double)7 / 2 = %.1f\n", (double)a / b);
    printf("5 / 2 * 2.0 = %.1f\n", 5 / 2 * 2.0);
    return 0;
}
 7 /  2 = 3     7 %  2 = 1
-7 /  2 = -3    -7 %  2 = -1
 7 / -2 = -3     7 % -2 = 1
 7 / 2.0 = 3.5
(double)7 / 2 = 3.5
5 / 2 * 2.0 = 4.0

(%% inside a printf format prints a single %.) The last line is a typical surprise: 5 / 2 is evaluated first, as integers, giving 2; only then is it multiplied by 2.0. Integer division and the remainder together split quantities into units, for example minutes into hours, or rounding up to whole quarter-hours, a common step with meter data:

#include <stdio.h>

int main(void)
{
    int minutes = 135;
    int hours = minutes / 60;
    int rest = minutes % 60;
    int quarter_hours = (minutes + 14) / 15;     /* round up */

    printf("%d min = %d h %d min, %d quarter-hours\n",
           minutes, hours, rest, quarter_hours);
    return 0;
}
135 min = 2 h 15 min, 9 quarter-hours

Dividing an integer by zero is undefined behavior, and what you see depends on the processor. On x86-64 Linux the program usually dies with "Floating point exception" (the signal SIGFPE, despite the name). On ARM64 the divide instruction simply returns 0: on the test machine, 1000 / zero printed 0 and the program carried on, which is worse, because nothing looks wrong. UndefinedBehaviorSanitizer reports it as runtime error: division by zero. Check the divisor first. Floating-point division by zero gives infinity or NaN, as in IEEE 754.

Increment, decrement and compound assignment

The increment and decrement operators ++ and -- add or subtract 1 and store the result in the variable. They come in two forms with a different value: the postfix form k++ gives the old value, the prefix form ++k the new one. Compound assignments such as +=, -=, *= and /= combine an operation with storing the result.

#include <stdio.h>

int main(void)
{
    int k = 5;
    int r = k++ + 10;     /* uses 5, then k becomes 6 */
    printf("r = %d, k = %d\n", r, k);

    int m = 5;
    int s = ++m + 10;     /* m becomes 6 first, then 16 */
    printf("s = %d, m = %d\n", s, m);

    int total = 100;
    total += 25;          /* total = total + 25 */
    total *= 2;
    printf("total = %d\n", total);
    return 0;
}
r = 15, k = 6
s = 16, m = 6
total = 250

Used on its own, as in i++; or the third part of a for loop, both forms do the same thing. Python has no ++; in Python ++k is just two unary plus signs.

Order of evaluation and sequence points

C does not say in which order the operands of most operators are evaluated: in f() + g(), either function may run first. What C does define are points in the program where all side effects so far must be complete. Such a sequence point lies, among others, at the end of each full expression (the ;), after the left operand of &&, ||, ?: and the comma operator, and before a function is called. Since C11 the standard describes the same rules as "sequenced before" relations. The rule that matters in practice: between two sequence points, do not modify a variable twice, and do not read it apart from computing its new value. Otherwise the behavior is undefined. GCC's -Wall catches the simple cases:

#include <stdio.h>

int main(void)
{
    int i = 1;
    i = i++ + 1;
    printf("%d\n", i);
    return 0;
}
example.c:6:7: error: operation on ‘i’ may be undefined [-Werror=sequence-point]

Other compilers may give 2, 3 or something else for code like this, and all of them are right, because the standard requires nothing. The same goes for printf("%d %d", n++, n); and a[i] = i++;. The fix is always to split the work into separate statements.

Comparison and logical operators

The comparison operators ==, !=, <, <=, > and >= and the logical operators && (and), || (or) and ! (not) produce an int: 1 for true, 0 for false. In a condition, any value that is not zero counts as true. && and || use short-circuit evaluation: the right operand is only evaluated when it can still change the result, which makes guards like "not zero and …" safe:

#include <stdio.h>

int main(void)
{
    int kwh = 350;
    int is_high = kwh > 300;
    int ok = kwh > 0 && kwh < 10000;

    printf("kwh > 300 is %d, in range is %d, !ok is %d\n", is_high, ok, !ok);
    printf("10 == 10.0 is %d\n", 10 == 10.0);

    int readings = 0;
    if (readings != 0 && 1000 / readings > 5) {   /* no division by zero */
        printf("high average\n");
    } else {
        printf("no readings yet\n");
    }
    return 0;
}
kwh > 300 is 1, in range is 1, !ok is 0
10 == 10.0 is 1
no readings yet

10 == 10.0 is true because the int is converted to double before comparing (see usual arithmetic conversions). Comparing results of floating-point calculations with == is unreliable for the reasons on Floating-Point Numbers. Chained comparisons do not work as in Python: 0 < kwh < 10000 compiles, but means (0 < kwh) < 10000, which is always true, since the left part is 0 or 1. Write kwh > 0 && kwh < 10000.

Because an assignment is also an expression, a single = in a condition is valid C and a classic bug: it assigns instead of comparing, and the condition is then the assigned value. -Wall asks for extra parentheses if you really mean it:

#include <stdio.h>

int main(void)
{
    int status = 0;
    if (status = 1) {
        printf("status is one\n");
    }
    return 0;
}
example.c:6:9: error: suggest parentheses around assignment used as truth value [-Werror=parentheses]

Bitwise operators and shifts

The bitwise operators work on the individual bits of integers: & (and), | (or), ^ (exclusive or), ~ (not), and the shifts << and >>. They are everyday tools in firmware, network code and file formats: testing a flag in a status byte, setting a bit in a hardware register, packing values. The theory, with masks, is on Binary Arithmetic and Bitwise Operations; setting, clearing and toggling register bits in C is shown on Firmware and Embedded Programming.

#include <stdio.h>

int main(void)
{
    unsigned a = 0x0F, b = 0x3C;

    printf("a & b  = %u\n", a & b);
    printf("a | b  = %u\n", a | b);
    printf("a ^ b  = %u\n", a ^ b);
    printf("1u << 31 = %u\n", 1u << 31);
    printf("0xF0 >> 4 = %u\n", 0xF0u >> 4);
    int negative = -8;
    printf("-8 >> 1 = %d\n", negative >> 1);
    return 0;
}
a & b  = 12
a | b  = 63
a ^ b  = 51
1u << 31 = 2147483648
0xF0 >> 4 = 15
-8 >> 1 = -4

Three rules keep bit manipulation safe:

  • Use unsigned types. Shifting a negative number right is implementation-defined (GCC copies the sign bit, giving −4 above; another compiler may not), and shifting a 1 into or past the sign bit of a signed type is undefined. Write 1u << 31, not 1 << 31.
  • Never shift by the width of the type or more. 1u << 32 on a 32-bit unsigned is undefined behavior. Shift 1u by 0 to 31 only, or use a 64-bit type such as UINT64_C(1) << n.
  • Mind ~ on small types. The operand is promoted to int first; see integer promotion below.

The compiler warns about a constant shift that is too large, but cannot see a shift count that arrives at run time. The sanitizer can:

#include <stdio.h>

int main(void)
{
    unsigned bits = 32;               /* imagine this came from a config file */
    printf("building a mask of bit %u\n", bits);
    unsigned mask = 1u << bits;       /* only 0..31 are allowed */
    (void)mask;
    return 0;
}
building a mask of bit 32
example.c:7:24: runtime error: shift exponent 32 is too large for 32-bit type 'unsigned int'

Without the sanitizer this program runs without complaint; on the test machine the same shift gave 1 at -O0 and 0 at -O2, a typical sign of undefined behavior (see Undefined Behavior and Safer C).

The conditional operator

The conditional operator condition ? a : b is C's only operator with three operands: it evaluates the condition, then only one of the other two, and gives its value. It is handy for short choices inside an expression; for anything longer, an if statement is easier to read (see Control Flow and Loops).

#include <stdio.h>

int main(void)
{
    int kwh = 350;
    const char *label = kwh > 300 ? "high" : "normal";
    int abs_diff = kwh > 400 ? kwh - 400 : 400 - kwh;

    printf("%d kWh is %s, %d from the limit\n", kwh, label, abs_diff);
    printf("%d reading%s\n", 1, 1 == 1 ? "" : "s");
    return 0;
}
350 kWh is high, 50 from the limit
1 reading

The comma operator a, b evaluates a, then b, and gives the value of b. You see it almost only in for loops with two counters (for (i = 0, j = n - 1; i < j; i++, j--)). The commas between function arguments are not comma operators.

Precedence and associativity

Operator precedence decides how an expression without parentheses is grouped. C has 15 levels; the table shows the most used, from binding most tightly to least. Operators on the same row are grouped from left to right, except the unary operators, ?: and assignment, which group from right to left (so a = b = 0 sets both).

Level Operators
Postfix a[i] f() . -> x++ x--
Unary ++x --x ! ~ unary - *p &x (type) sizeof
Multiplicative * / %
Additive + -
Shift << >>
Relational < <= > >=
Equality == !=
Bitwise &, then ^, then | (three levels)
Logical &&, then || (two levels)
Conditional ?:
Assignment = += -= *= /= %= &= |= ^= <<= >>=
Comma ,

The bitwise operators bind less tightly than the comparisons, a historical accident that catches everyone once. status & ALARM_BIT == 0 means status & (ALARM_BIT == 0), which is always 0. GCC warns:

#include <stdio.h>

#define ALARM_BIT 0x04u

int main(void)
{
    unsigned status = 0x06u;
    if (status & ALARM_BIT == 0) {
        printf("no alarm\n");
    }
    return 0;
}
example.c:8:16: error: suggest parentheses around comparison in operand of ‘&’ [-Werror=parentheses]

Write (status & ALARM_BIT) == 0. As a habit: add parentheses whenever you mix bitwise operators, shifts or && and ||; nobody remembers the whole table, and readers should not have to.

Implicit conversions

C converts values from one type to another automatically in many places: in assignments, when passing arguments, and before most arithmetic. Such an implicit conversion happens without any operator in the code, which is convenient and is the source of many C surprises. Two sets of rules decide what happens in arithmetic.

Integer promotion

C never does arithmetic in types smaller than int. Under integer promotion, a char, short, _Bool or bit field (signed or unsigned) is converted to int before an operator works on it, when an int can hold all its values (otherwise to unsigned int, e.g. unsigned short on 16-bit microcontrollers). The result therefore has type int, and only shrinks again when you store it in a smaller variable:

#include <stdint.h>
#include <stdio.h>

int main(void)
{
    unsigned char a = 200, b = 100;
    int sum = a + b;                  /* both promoted to int: 300 */
    unsigned char stored = a + b;     /* 300 does not fit: 44 */
    printf("sum %d, stored %d\n", sum, stored);

    uint8_t x = 0xF0;
    printf("~x = %d\n", ~x);          /* promoted to int first */
    printf("(uint8_t)~x = %d\n", (uint8_t)~x);
    return 0;
}
sum 300, stored 44
~x = -241
(uint8_t)~x = 15

The sum of two unsigned char values does not wrap at 255: it is computed as int. Storing 300 in an unsigned char keeps the low 8 bits, 44. And ~x flips all 32 bits of the promoted int 0x000000F0, giving 0xFFFFFF0F, which is −241. To get the 8-bit result 0x0F, convert back with a cast. Firmware code that inverts or shifts uint8_t and uint16_t values, as in a CRC calculation, is full of such casts for this reason.

The usual arithmetic conversions

When the two operands of an arithmetic or comparison operator have different types after promotion, the usual arithmetic conversions bring them to one common type. Simplified:

  1. If either operand is a floating-point type, the other is converted to the larger floating-point type (int and double → double).
  2. Otherwise, if both are signed or both unsigned, the smaller type is converted to the larger.
  3. Otherwise, with one signed and one unsigned operand: if the unsigned type is at least as large, the signed value is converted to that unsigned type. If the signed type is larger and can hold every value of the unsigned one, the unsigned value is converted to the signed type.

Rule 3 is the dangerous one. Comparing an int with an unsigned int converts the int to unsigned, so −1 becomes 4,294,967,295. A negative balance is then "not below" a limit of 10. With -Wextra, GCC refuses this comparison:

#include <stdio.h>

int main(void)
{
    int balance = -1;
    unsigned limit = 10;

    if (balance < limit) {
        printf("below limit\n");
    }
    return 0;
}
example.c:8:17: error: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Werror=sign-compare]

The warning only covers comparisons. Arithmetic with mixed types is converted the same way, silently, and a size_t (as returned by strlen) makes it easy to get there:

#include <stdio.h>
#include <string.h>

int main(void)
{
    const char *code = "ab";

    if (strlen(code) - 3 > 0) {
        printf("strlen(code) - 3 = %zu\n", strlen(code) - 3);
        printf("so a 2-character code counts as longer than 3\n");
    }
    if ((long)strlen(code) - 3 > 0) {
        printf("never printed\n");
    } else {
        printf("with a signed type: %ld\n", (long)strlen(code) - 3);
    }
    return 0;
}
strlen(code) - 3 = 18446744073709551615
so a 2-character code counts as longer than 3
with a signed type: -1

strlen(code) - 3 is computed in size_t, which is unsigned, so 2 − 3 wraps around to the largest 64-bit value, and that is certainly greater than 0. Compare lengths directly instead: strlen(code) > 3. Whether a mixed comparison is dangerous can even depend on the platform. On LP64, long is larger than unsigned int, so long c = -1; c < 10u is true (the unsigned value becomes a long); on 64-bit Windows, where long is 32 bits, the same comparison is false.

Casts and explicit conversions

The cast operator (type) value converts a value to another type on purpose. It is how you ask for a floating-point division of two integers, keep an 8-bit result, or tell the compiler that a conversion that may lose information is intended. A conversion to a smaller type, like the Java narrowing conversion, can lose information; C does it without complaint once you write the cast.

#include <stdio.h>

int main(void)
{
    printf("(int)3.99  = %d\n", (int)3.99);
    printf("(int)-3.99 = %d\n", (int)-3.99);
    printf("'7' - '0'  = %d\n", '7' - '0');
    printf("'A' + 1    = %c\n", 'A' + 1);

    int used = 7, days = 2;
    printf("average    = %.2f\n", (double)used / days);
    return 0;
}
(int)3.99  = 3
(int)-3.99 = -3
'7' - '0'  = 7
'A' + 1    = B
average    = 3.50
  • Converting a floating-point value to an integer truncates toward zero; it does not round. Use lround to round. If the value does not fit in the integer type at all, the behavior is undefined.
  • Characters are small integers. Digits '0' to '9' have consecutive codes, so c - '0' turns a digit character into its value, a common step when parsing a meter telegram by hand (see ASCII).
  • The cast binds tightly: (double)used / days converts used first, and the division is then done in double. (double)(used / days) would convert the already truncated result 3.