"As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programmes." -- Maurice Wilkes discovers debugging, 1949.
error C2106: '=' : left operand must be l-value
int main(void)
{
// ASSUME:
// address of i is 100
// address of j is 200
// address of pi is 300
int i, j; // 2 ints, i and j
int *pi; // pointer to an int
// Ok
i = 3; // put 3 (literal) into location 100
j = i; // put 3 (contents of location 100) into location 200
pi = &i; // put 100 (address of i) into location 300;
j = *pi; // put 3 (contents of address 100) into location 200;
*pi = 3; // put 3 (literal) into location 100 (not 300)
// Errors
pi = 3; // Attempting to put 3 (literal) into location 300.
// Error: 'int *' differs in levels of indirection from 'const int'
3 = i; // Error: left operand must be l-value
return 0;
}
Simple guidelines:
More complex example:int x = a * b + c * d + e * f;
int PrintAndReturn(int value)
{
printf("%i\n", value);
return value;
}
int main(void)
{
int x, 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);
return 0;
}
The order in which expressions are evalutated is more relevant when the expressions have
side effects.
The order that these subexpressions are evaluated can change the value of the expression.a = (b + c) * d / (e - 4) * (f + 5 * g);
int a[4] = {1, 2, 3, 4};
int b[4] = {5, 6, 7, 8};
int i = 0;
int c = a[i++] + b[i]; /* i++ causes a side-effect */
The answer is: it depends. It depends on when the variable i was incremented. The value of
c is going to be either 6 or 7. Do you see why?
a[i] + b[i] or a[i] + b[i + 1]
warning: operation on `i' may be undefined
Sequence Guarantee Operators
There are 4 sequence guarantee operators:
Operator Symbol Syntax
------------------------------------------------------
logical AND operator && expr1 && expr2
logical OR operator || expr1 || expr2
conditional operator ? : expr1 ? expr2 : expr3
comma operator , expr1, expr2
Also, the semi-colon is a sequence guarantee point and means that any side-effect operator is guaranteed
to have "done its duty" when you reach the semi-colon.
There are several side-effect operators:
Operator Meaning
-------------------------------------------------------
= Assignment
++ -- Pre/Post increment/decrement
+=, -=, *=, /=, %= Arithmetic assignments
&=, !=, ^=, <<=, >>= Bitwise assignments
Examples using the comma operator
What do the following functions print?
void TestCommaSequence1(void)
{
int a = (1, 2, 3, 4);
printf("a = %i\n", a);
}
With side-effect operators:
void TestCommaSequence2(void)
{
int a, b;
a = printf("1\n"), printf("22\n"), printf("333\n"), printf("4444\n");
printf("a = %i\n", a);
b = (printf("1\n"), printf("22\n"), printf("333\n"), printf("4444\n"));
printf("b = %i\n", b);
}
When using side-effect operators (and you will use them all the time), you need to make sure that the meaning of the entire expression is not ambiguous. If it is ambiguous (ill-formed or mal-formed), it is not guaranteed to evaluate the same in all situations (read: compilers).
GNU's gcc compiler warns about the function above:
Additional Resources:warning: left-hand operand of comma expression has no effect
Sequence Points and Expression Evaluation
Sequence Points
Overcomplex statements can confuse your compiler. Knowing where the sequence points are can help make your intentions clear
Microsoft's implementation of its sequence guarantee points.
Address 1000 1001 1002 1003 ---------- ---- ---- ---- ---- big-endian 12 34 56 78 little-endian 78 56 34 12
Network issues:Processor Family Endian --------------------------------- Pentium (Intel) Little Athlon (AMD) Little Alpha (DEC/Compaq) Little 680x0 (Motorola) Big PowerPC (Motorola & IBM) Big SPARC (Sun) Big MIPS (SGI) Big Java Virtual Machine Big
#ifdef BIG_ENDIAN /* no-ops */
#define htons(x) (x) /* host-to-network short (16-bit) */
#define htonl(x) (x) /* host-to-network long (32-bit) */
#define ntohs(x) (x) /* network-to-host short (16-bit) */
#define ntohl(x) (x) /* network-to-host long (32-bit) */
#else /* assume little endian and swap bytes */
#define htons(x) ((((x) & 0xff00) >> 8) | ((x) & 0x00ff) << 8))
#define htonl(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
#define ntohs htons
#define ntohl htonl
#endif
TIFF picture with byte-order encoded in first 16-bit word. You'll need to open the file in a hex editor to see the bytes.
Introduction to Endian "One, little two, little three little Endians..." This page gives a brief overview of big vs. little endian.
Some file formats and their endianness
Exercise: Write a function that determines whether your computer is little-endian or big-endian.