Fundamental C - Expressions
Written by Harry Fairhead   
Tuesday, 24 May 2022
Article Index
Fundamental C - Expressions
Operators
The Rules
C99

Operators in C

C has a range of operators that needs 16 levels of precedence to control! This bewildering array of operators is one of the reasons why C is intimating to the beginner and can look cryptic if written using lots of operators. You can always use parentheses to make the order of evaluation clear. A sub-expression in parentheses is always fully evaluated before it is used in the full expression.

Once you start to extend the range of operators that a language has then you have to worry about associativity as well as precedence. Associativity is simply the order that operators of the same priority will be evaluated. If you restrict your attention to simple arithmetic operators then the associativity isn't a problem because (A+B)+C is the same as A+(B+C). However, when you move on to consider more general operators it does matter. For example, in C the right shift operator A>>B shifts A right by B bits. (A>>B)>>C, which means shift A right by B+C isn't the same as A>>(B>>C), which shifts A right by the result of the right shift of B by C bits.

To make the result of such operations unambiguous, C defines each operator as associating left to right or right to left, i.e. right or left associative. The >> operator associates left to right because this means that A>>B>>C is (A>>B)>>C and this is the same as A>>(B+C).

Every operator in C has a priority and an associativity and you need to know both to be able to figure out what an expression evaluates to. You can see all of the standard C operators in the table on the next page.

Precedence
Operator
Description
Associativity

1

++

Postfix increment a++

Left-to-right

--

Postfix decrement a--

()

Function call a()

[]

Array subscripting a[b]

.

Element selection by reference a.b

->

Element selection through pointer a->b

2

++

Prefix increment ++a

Right-to-left

--

Prefix decrement --a

+

Unary plus +a

-

Unary minus -a

!

Logical NOT !a

~

Bitwise NOT ~a

(type)

Type cast (int) a

*

Indirection (dereference) *a

&

Address-of &a

sizeof

Size-of sizeof a

3

*

Multiplication a*b

Left-to-right

/

Division a/b

%

Modulo (remainder) a%b

4

+

Addition a+b

Left-to-right

-

Subtraction a-b

5

<<

Bitwise left shift a<<b

Left-to-right

>>

Bitwise right shift a>>b

6

<

Less than a<b

Left-to-right

<=

Less than or equal to a<=b

>

Greater than a>b

>=

Greater than or equal to a>=b

7

==

Equal to a==b

Left-to-right

!=

Not equal to a!=b

8

&

Bitwise AND a&b

Left-to-right

9

^

Bitwise XOR (exclusive or) a^b

Left-to-right

10

|

Bitwise OR (inclusive or) a|b

Left-to-right

11

&&

Logical AND a&&b

Left-to-right

12

||

Logical OR a||b

Left-to-right

13

?:

Ternary conditional (see?: later)

Right-to-left

14

=

Direct assignment a=b

Right-to-left

+=

Assignment by sum a+=b

-=

Assignment by difference a-=b

*=

Assignment by product a*=b

/=

Assignment by quotient a/=b

%=

Assignment by remainder a%=b

<<=

Assignment by bitwise left shift a<<=b

>>=

Assignment by bitwise right shift a>>=b

&=

Assignment by bitwise AND a&=b

^=

Assignment by bitwise XOR a^=b

|=

Assignment by bitwise OR

15

,

Comma

Left-to-right

Notice that in C, ^ is exclusive or, not raise to a power as it is in other languages. Most of the other operators you either already know or they are discussed later. There are, however, some additional things to say about operators, in particular, casts and side effects.

Expressions, Type and Casting

There is a great deal of misunderstanding about type and casting in particular. In C it is better to think of a memory location as somewhere that happens to be currently in use to store a bit pattern that has a particular interpretation. For example, as explained in Chapter 5, the bit pattern 01000001 could be the binary representation of 65 or it could be the ASCII code for ‘A’ or it could signify that switches 0 and 6 are closed and the rest are open. The bit pattern stays the same, the interpretation changes.

In C, unlike in other languages, the type assigned to a variable or pointer only changes how the bit pattern is supposed to be interpreted and determines the amount of memory allocated. For example:

char myChar;

allocates a single byte which is interpreted, mostly by the programmer, as a character. So you can initialize it to a character literal:

myChar=’A’;

which stores the bit pattern 01000001 in the byte.

So is myChar a character or a number? If you are expecting a firm answer to this question you haven’t yet understood the very loose way that C treats type. The best answer is that there is a bit pattern stored in myChar and it’s up to you to how to interpret it. The compiler does expect you to treat it as a character and it will warn you if you treat it as something else, but it’s just helpful advice to avoid you making a mistake.

Suppose that you really know what you are doing and you want to use char as if it was a numeric value and want it to be clear that you are doing this on purpose? The answer is that you can use a cast, which in C is denoted as a type enclosed in parentheses – (type). A cast is often optional as the compiler can work out what is going on, but it still useful to indicate to a human what you intend. Compilers will also often warn you that you are treating one type as a different type, but this is a helpful feature of the compiler and not something that is built into the C language.

A cast is written in front of a variable to tell the compiler that you want its bit pattern to be reinterpreted as the new type. Notice that this is a very simple process in most cases. Other languages often do a lot more work when you cast from one type to another.



Last Updated ( Tuesday, 24 May 2022 )