Operators#

C’s operator surface covers arithmetic, comparison, logical, bitwise, pointer (& *), member access (. ->), array indexing ([]), function call (()), sizeof, ? :, the comma operator, and the assignment family. No operator overloading; types decide what is legal.

For the values these act on, see Types.

Arithmetic#

Operator

Meaning

+, -, *, /

addition, subtraction, multiplication, division. Integer / integer truncates toward zero.

%

modulo (integers only). Sign follows the dividend.

++, --

increment / decrement. Prefix yields the new value; postfix yields the old.

7 / 2       /* 3      (integer truncation) */
7.0 / 2     /* 3.5    (one operand is double) */
-7 % 3      /* -1     (sign of dividend) */

int i = 5;
int a = i++;     /* a = 5, i = 6 */
int b = ++i;     /* i = 7, b = 7 */

Relational#

1 == 1      /* 1 (true)  */
"abc" == "abc"  /* compares POINTERS, not bytes */

strcmp(s1, s2) == 0   /* the operator's actual string compare */

Pointer comparison compares addresses; the operator who wants byte comparison uses memcmp (raw) or strcmp (null terminated).

Logical#

&& and || short-circuit and return 0 or 1; ! is unary.

if (p != NULL && p->ready) {  /* short-circuit avoids NULL deref */
    use(p);
}

Bitwise#

Operator

Meaning

&

AND

|

OR

^

XOR

~

bitwise NOT

<<

left shift

>>

right shift (sign-extending on signed, zero-fill on unsigned)

uint32_t flags = 0;
flags |= READER;             /* set */
flags &= ~READER;            /* clear */
flags ^= READER;             /* toggle */
if (flags & READER) {        /* test */
    /* … */
}

uint32_t hi = (val >> 16) & 0xffff;
uint32_t lo =  val        & 0xffff;

Right shift on a signed negative value is implementation-defined; the operator works on unsigned integers when shifting matters.

Assignment#

= plus the compound forms += -= *= /= %= &= |= ^= <<= >>=.

x = 1;
x += 2;
flags |= READER;

The result of an assignment is the assigned value, which is why while ((c = getchar()) != EOF) works.

Pointer#

  • &x takes the address of x.

  • *p dereferences (reads / writes through the pointer).

  • p->field is (*p).field.

int   x = 10;
int  *p = &x;
*p = 20;                     /* x is now 20 */

struct user *u = malloc(sizeof *u);
u->name = "rk";

C has no managed pointers; every malloc pairs with a free on every exit path.

Array / call#

a[i] is sugar for *(a + i); f(x) calls f.

xs[3]                        /* *(xs + 3) */
strlen("hello")              /* 5 */

sizeof#

sizeof returns the size of a type or an expression in size_t.

sizeof(int)                  /* compile-time constant */
sizeof xs                    /* total bytes of the array (NOT pointer size) */
sizeof *p                    /* size of the pointed-to type */

The classic mistake: sizeof an array parameter inside a function gives the pointer size, not the array size, because arrays decay.

Ternary#

cond ? a : b evaluates cond, then either a or b; returns the chosen value.

int abs = (x < 0) ? -x : x;
const char *plural = (n == 1) ? "" : "s";

Comma#

The comma operator evaluates left then right, returning right. Common only inside for headers.

for (int i = 0, j = 10; i < j; i++, j--) { /* … */ }

Precedence#

A subset, highest to lowest. The operator parenthesises anything ambiguous; the compiler does not warn.

Level

Operator

postfix

a()

postfix

a[]

postfix

a.b

postfix

a->b

postfix

a++

postfix

a--

unary

!

unary

~

unary

+ (prefix)

unary

- (prefix)

unary

* (dereference)

unary

& (address-of)

unary

++ (prefix)

unary

-- (prefix)

unary

sizeof

unary

(T)cast

multiplicative

*

multiplicative

/

multiplicative

%

additive

+

additive

-

shift

<<

shift

>>

relational

<

relational

<=

relational

>

relational

>=

equality

==

equality

!=

bitwise

&

bitwise

^

bitwise

|

logical

&&

logical

||

ternary

a ? b : c

assignment

=

assignment

+=

assignment

-=

assignment

*=

assignment

/=

assignment

%=

assignment

<<=

assignment

>>=

assignment

&=

assignment

^=

assignment

|=

comma

,

References#