Expressions

In C, expressions are used to calculate some value. The simplest expressions are constants and variables. There is practically no limit to how complex an expression can be.Examples:

i 5 3.1415 a + b rate * time x * (a + b / 7.0) - value / y x*(a+b/7.0)-value/y x *(a+ b/ 7.0)- value / y sqrt(25.8) + b * abs(c)

- All expressions consist of
*operators*and*operands*, just like formulas/equations from high-school algebra. - The
*type*of an expression is based on the type of its operands. - All expressions evaluate to a (usually single)
*value*. - If there are operands of different types (mixed mode), then some of the operands will be converted to the other type. (The conversion rules can be quite involved.)
- There are
*many*different operators in C. (Some will be used more than others.) - The basic operators that all languages have are:
- Arithmetic operators - for calculating values (e.g. add, subtract, multiply, divide, etc.)
- Relational operators - for comparing values (e.g. greater than, less than, equal to, etc.)
- Logical operators - for combining operators (e.g. greater than 7
*and*less than 12)

- Note that there is no semi-colon after the expressions above, as they are not C
*statements*. Sometimes, as we'll see, we may have a semi-colon after an expression (an*expression statement*). - There are
*unary*operators and*binary*operators. Most are binary operators. There is even a*ternary*operator (3 operands).

Simple arithmetic unary operators:-i +5 a + b rate * time

Some simple arithmetic binary operators:

Unary operator Meaning + Positive (redundant) - Negation

With the exception of the modulus operator, all other operators above can work with

Binary operator Meaning + Add - Subtract * Multiply / Divide % Modulo

(Remainder)

Precedence and Associativity

Just as mathematics, all operators have a certain

When two or more operators with the same precedence are used in an expression, you must look at the operator's3 + 4 * 2 is 11 and is the same as 3 + (4 * 2) (3 + 4) * 2 is 14 -4 + 7 is 3 and is the same as (-4) + 7 -(4 + 7) is -11

This precedence chart shows that there are quite a few different levels of precedence within the C operators. Each division (separated by -----) is a different precedence level.3 + 4 + 2 is 9 and is the same as (3 + 4) + 2 3 * 4 * 2 is 24 and is the same as (3 * 4) * 2 2 * 6 / 4 is 3 and is the same as (2 * 6) / 4 2 * (6 / 4) is 2

Assignment Operators

The assignment operator is very common. There areSimple assignment statements:

Note that thea = 1; a = b; a = 3 * b; a = 4 - 3 * b / 8;

Examples:

The assignment operator is unique compared to the arithmetic operators we've seen so far:inti;/* i holds an undefined value */doubled;/* d holds an undefined value */i = 10;/* i now holds the value 10 */i = 12.8;/* i now holds the value 12 */d = 10;/* d now holds the value 10.0 */d = 12.8;/* d now holds the value 12.8 */

- Most operators do not modify their operands.
- Assignment operators
*modify*the left operand. - The left operand must be able to represent a memory location.
- Any expression that can represent a memory location is considered an
*l-value*(or lvalue or lval).

Because of theinta, b, c;/* all are undefined */b = 5;/* b is now 5 */c = 10;/* c is now 10 */a = b + c;/* a is now 15, b and c are unchanged */a = b * c;/* a is now 50, b and c are unchanged */

This is the same as this:a = b = c = 5;/* all are now 5 */

Note that this is very different (and is illegal):a = (b = (c = 5));/* all are now 5 */

This is because the assignment operator requires an l-value, so we can store a value. These are illegal as well:((a = b) = c) = 5;/* This is not legal C code */

Remember, this is10 = 5;/* Illegal */10 = a;/* Illegal */a + b = 8;/* Illegal */10 = 10;/* Illegal */

These can be done more succinctly with/* get the current value of a, add 5 to it, *//* and put the new value back into a */a = a + 5;/* get the current value of b, subtract 6 from it, *//* and put the new value back into b */b = b - 6;

Note that/* get the current value of a, add 5 to it, *//* and put the new value back into a */a += 5;/* get the current value of b, subtract 6 from it, *//* and put the new value back into b */b -= 6;

These three assignment expressions are similar:

Pre-increment Post-increment Pre-decrement Post-decrement ++i i++ --i i--

There is an important but subtle difference between the

Assignment Compound Assignment Increment/

Decrementa = a + 1 a += 1 a++

++aa = a - 1 a -= 1 a--

--a

Notice the missinga = a + 1 a += 1 ++a

However, asa = 5; printf("value is %i\n", a = a + 1);/* value is 6 */a = 5; printf("value is %i\n", a += 1);/* value is 6 */a = 5; printf("value is %i\n", ++a);/* value is 6 */a = 5; printf("value is %i\n", a++);/* value is 5 */

a = a + 1; a += 1; ++a; a++;

More examples: Assuming that

Looking closer, this statement:

Statements Output a = 5; printf("The value of a is %i\n", ++a); printf("The value of a is %i\n", a); The value of a is 6 The value of a is 6 a = 5; printf("The value of a is %i\n", a++); printf("The value of a is %i\n", a); The value of a is 5 The value of a is 6 a = 5; printf("The value of a is %i\n", --a); printf("The value of a is %i\n", a); The value of a is 4 The value of a is 4 a = 5; printf("The value of a is %i\n", a--); printf("The value of a is %i\n", a); The value of a is 5 The value of a is 4

is equivalent to these statements:c = a++ + ++b;

Look closely at the expressions below to determine the output:b = b + 1; c = a + b; a = a + 1;

The statement below modifies the values of

Statements Output a = 5; b = 3; c = a++ + b++; printf("a = %i, b = %i, c = %i\n", a, b, c); a = 6, b = 4, c = 8 a = 5; b = 3; c = ++a + b++; printf("a = %i, b = %i, c = %i\n", a, b, c); a = 6, b = 4, c = 9 a = 5; b = 3; c = a++ + ++b; printf("a = %i, b = %i, c = %i\n", a, b, c); a = 6, b = 4, c = 9 a = 5; b = 3; c = ++a + ++b; printf("a = %i, b = %i, c = %i\n", a, b, c); a = 6, b = 4, c = 10

Remember, toc = a++ + ++b;

Fetch value from memory Increment the value by 1 Store the new value in memory

Notice that there is a time when the old value and the new value both exist. This is key to understanding the increment/decrement operators.

Order of Evaluation

Example expressions:In theintw = 1;intx = 2;inty = 3;intz = 4;intr; r = w * x + y * z;/* 1. same as: (w * x) + (y * z) */r = w + x * y + z;/* 2. same as: w + (x * y) + z */r = (w + x) * (y + z);/* 3. only way to write this */

must perform each of these evaluations: (The registers used here are completely arbitrary.)r = w * x + y * z/* 1. same as: (w * x) + (y * z) */

- The value stored at
must be fetched from memory. (Put in register A)**w** - The value stored at
must be fetched from memory. (Put in register B)**x** - The value stored at
must be fetched from memory. (Put in register C)**y** - The value stored at
must be fetched from memory. (Put in register D)**z** - The value in register A must be multiplied by the value in register B. (Put result in register E)
- The value in register C must be multiplied by the value in register D. (Put result in register F)
- The value in register E is added to the value in register F. (Put result in register G)
- The value in register G is stored in memory location
.**r**

- The value stored at
is fetched from memory. (Put in register A)**z** - The value stored at
is fetched from memory. (Put in register B)**w** - The value stored at
is fetched from memory. (Put in register C)**x** - The value stored at
is fetched from memory. (Put in register D)**y**

- The value stored at
is fetched from memory. (Put in register A)**w** - The value stored at
is fetched from memory. (Put in register B)**x** - The value in register A is multiplied by the value in register B. (Put result in register C)
- The value stored at
is fetched from memory. (Put in register A)**y** - The value stored at
is fetched from memory. (Put in register B)**z** - The value in register A is multiplied by the value in register B. (Put result in register D)
- The value in register C is added to the value in register D. (Put result in register E)
- The value in register E is stored in memory location
.**r**

- The value stored at
is fetched from memory. (Put in register A)**w** - The value stored at
is fetched from memory. (Put in register B)**x** - The value in register A is multiplied by the value in register B. (Put result in register C)
- The value stored at
is fetched from memory. (Put in register A)**y** - The value stored at
is fetched from memory. (Put in register B)**z** - The value in register A is multiplied by the value in register B. (Put result in register A)
- The value in register A is added to the value in register C. (Put result in register B)
- The value in register B is stored in memory location
.**r**

and**w**must be fetched before they can be multiplied.**x**and**y**must be fetched before they can be multiplied.**z**and**w**must be multiplied before adding to the product of**x**and**y**.**z**- All fetching and arithmetic operations must be done before storing the result into
.**r**

can be evaluated in a multitude of ways, the result willr = w * x + y * z;/* 1. same as: (w * x) + (y * z) */

This is how *optimizing compilers* work. They can re-order your code in ways that will make it execute
faster. Of course, they must not change the logic of the code (that would be a bug in the compiler.)
This optimization is where most of the current work on compilers is done.

Side-effects in Expressions

Anytime an operator causes a value in memory to change, it is called aHowever, this assignment statement is actually performing three assignments:e = a * b + c * d;/* Changes the value stored ate*/

After the statement completely executes,e = a++ * b++;/* 3 modifications */

This is problematic, though:

Sincee = a++ * a;/* dangerous code! */

In fact, the GNU gcc compiler will actually warn you about it:a = 2; e = a++ * a;/*eis undefined (either 4 or 6) */

Here's an example of undefined code:warning: operation on 'a' may be undefined

Depending on which compiler you use, you may get different results:#include<stdio.h>intmain(void) {inta = 5; a = a-- - --a * (a = -3) * a++ + ++a; printf("a = %i\n", a);return0; }

Fortunately, the GNU compiler will warn you about this:

Compiler Output GNU gcc a = 22Microsoft a = 4Borland a = 21

Some side-effect operators:main.c: In function `main': main.c:6: warning: operation on `a' may be undefined main.c:6: warning: operation on `a' may be undefined main.c:6: warning: operation on `a' may be undefined main.c:6: warning: operation on `a' may be undefined main.c:6: warning: operation on `a' may be undefined

There are 4 side-effect operators in this expression:= += -= *= /= ++ (pre/post increment) -- (pre/post decrement)

What will be printed by this code? The first thing you should do is identify which variables are going to have their values changed. You'll definitely want to refer to the precedence chart.a = b += c++ - d + --e / -f

To better understand the expression above, you should add parentheses to explicitly show the order of evaluation.inta = 1;intb = 2;intc = 3;intd = 4;inte = 5;intf = 6; a = b += c++ - d + --e / -f; printf("a = %i, b = %i, c = %i, d = %i, e = %i, f = %i\n", a, b, c, d, e, f);

There are rules that dictate precedence and associativity, but there is still ambiguity: Example:

Which value is fetched from memory first? (Compiler-dependent)intx = a * b + c * d + e * f;

More complex example (with side-effects):

The order in which expressions are evalutated is more relevant when the expressions have side effects. All compilers will not generate the same output.intPrintAndReturn(intvalue) { printf("%i\n", value);returnvalue; }intmain(void) {intx, y; x = PrintAndReturn(1) + PrintAndReturn(2) + PrintAndReturn(3); printf("x = %i\n", x); y = PrintAndReturn(1) + PrintAndReturn(2) * PrintAndReturn(3); printf("y = %i\n", y);return0; }

GNU MS Borland 1 1 1 2 2 2 3 3 3 x = 6 x = 6 x = 6 1 1 2 2 2 3 3 3 1 y = 7 y = 7 y = 7