SEN2201 · Week 11 · Programming in C

Introduction to C: variables, types and operators

The structure of a C program, int/char/double, scope, integer division, ++/--, logical and bitwise operators, precedence. The foundation of “What is printed?” questions.

variabledata typeintchardoublescopelocal variableglobal variableinteger divisionmoduluspre-incrementpost-incrementshort-circuit evaluationbitwise operatorshiftprecedenceprintf

The last four weeks of the semester are C. Why C? Because C sits right above the level you have learned so far: a C variable is a location in memory, a C expression is a few LC-3 instructions, a function call is JSR and stack operations. That is why the textbook builds C on top of the LC-3. Most of the C questions on the exam are of the “What is printed?” type: you run the code in your head and write the output.

Golden rules:

  1. int / int = int, the fraction is truncated.
  2. True = nonzero, false = 0. Comparisons produce 1 or 0.
  3. x++ gives the old value, ++x gives the new value.
  4. && and || short-circuit.
  5. A variable declared inside a block shadows the one outside.

The first program

#include <stdio.h>

int main(void) {
    int count = 3;
    printf("count = %d\n", count);
    return 0;
}
count = 3
  • #include <stdio.h>: a preprocessor command for printf/scanf. Before compiling, the contents of that file are inserted here.
  • main: the program starts here.
  • Every statement ends with ;. Blocks are { }.

A C program is compiled: source code → preprocessor → compiler → assembly/machine code → linker → executable file. On the LC-3 this means producing assembly of the kind you wrote in week 10.

Variables and types

Every variable has a type, a name and a value. The type says how many bits will be reserved in memory and how the bits will be read (week 2).

TypeWhat does it hold?Exampleprintf
intsigned integer (2’s complement)int n = -5;%d
chara single character (ASCII code, small integer)char c = 'A';%c or %d
doublea number with a fraction (IEEE 754)double x = 2.5;%f

In LC-3 C, int is 16 bits; on your computer it is usually 32 bits. The numbers used in the exam fit in both.

Constants: 17 is decimal, 0x1F is hex (31), 017 is octal (15). Putting a zero in front makes the number octal: watch out.

Scope

  • A local variable is declared inside a function or block and is seen only there. In the LC-3 it lives on the stack (runtime stack).
  • A global variable is declared outside all functions and is seen everywhere. In the LC-3 it lives in the global data region (R4 points to this region).

A variable with the same name in an inner scope shadows the outer one:

int g = 10;

void show(void) {
    int g = 3;            /* a new, local g */
    printf("show: %d\n", g);
}

int main(void) {
    show();
    printf("main: %d\n", g);
    return 0;
}
show: 3
main: 10

PITFALL · a local with no initial value. If you write int x; and use x, its value is undefined (whatever was left in that memory location). Global variables, on the other hand, start automatically at 0.

Arithmetic operators

printf("%d %d %d %d\n", 7 / 2, -7 / 2, 7 % 3, -7 % 3);
3 -3 1 -1
  • Division truncates toward zero: -3.5 → -3.
  • The sign of the result of % is that of the left number.
  • If one is a double, the division is a real division: 7 / 2.0 = 3.5.

Assignment and compound assignment

int n = 10;
n -= 3;   /* 7  */
n *= 2;   /* 14 */
n /= 4;   /* 3  */
n %= 3;   /* 0  */
printf("%d\n", n);
0

Increment and decrement

ExpressionValuex afterwards
x++old xx + 1
++xx + 1x + 1
x--old xx - 1
--xx - 1x - 1
int i = 3;
int k = i++ * 2;     /* 3 * 2, then i = 4 */
printf("%d %d\n", i, k);
4 6

If you had written ++i * 2, i would become 4 first, and k = 8.

PITFALL · changing the same variable twice in one expression. Expressions like x = x++ + ++x; are undefined in C (undefined behavior). They give different results depending on the compiler. If you meet them in exam questions, the answer may be “undefined”.

Relational and logical operators

< <= > >= == != give 1 (true) or 0 (false) as the result. && (and), || (or), ! (not) count every nonzero value as true.

int a = 3, b = 0;
printf("%d %d %d %d\n", a && b, a || b, !b, a == 3);
0 1 1 1

Short-circuit: in A && B, if A is false, B is not computed. In A || B, if A is true, B is not computed. This makes guarded expressions like (i != 0) && (10 / i > 1) possible.

The conditional operator: condition ? a : b. If the condition is true, a; otherwise b.

Bitwise operators

OperatorMeaningExample (12 = 1100, 10 = 1010)
&AND12 & 10 = 8
|OR12 | 10 = 14
^XOR12 ^ 10 = 6
~NOT (every bit)~5 = -6
<<shift left5 << 2 = 20
>>shift right40 >> 3 = 5

~x = -x - 1 (2’s complement). Do not mix up & and &&: 12 & 10 = 8, 12 && 10 = 1.

char arithmetic

Since a char is an integer, you can calculate with it:

char c = 'b';
c = c - 'a' + 'A';      /* lowercase → uppercase */
printf("%c\n", c);
printf("%d\n", '7' - '0');
B
7

Precedence and associativity

LevelOperatorsDirection
1() [] -> . x++ x--left to right
2++x --x ! ~ -x *p &x (type)right to left
3* / %left to right
4+ -left to right
5<< >>left to right
6< <= > >=left to right
7== !=left to right
8&left to right
9^left to right
10|left to right
11&&left to right
12||left to right
13?:right to left
14= += -= …right to left

If you are not sure, use parentheses; in the exam, solve step by step according to the table:

2 + 3 * 4 % 5
2 + 12 % 5        * and % are at the same level, the left one first
2 + 2
4

From C to the LC-3

a = b + c; (all three local) is roughly translated by the compiler like this (R5 is the frame pointer, week 12):

        LDR  R0, R5, #-1    ; R0 = b
        LDR  R1, R5, #-2    ; R1 = c
        ADD  R0, R0, R1
        STR  R0, R5, #0     ; a = R0

So every C variable is a memory location, and every operation is one or a few instructions.

Common mistakes

Wrong ideaThe truth
7 / 2 = 3.53; for a fraction use 7 / 2.0
double d = 7 / 2; → 3.53.0
y = x++ → y is the new valuey is the old value
10 > 5 > 1 is true(10>5)=1, 1>1 = 0
& and && are the same& is bitwise, && is logical
!5 = -50; for -5 write -5, for -6 ~5
017 = 17Octal: 15
A local variable starts at 0Undefined; a global starts at 0

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1easy

What is printed?

printf("%d %d\n", 17 / 5, 17 % 5);
Show answer
3 2

If two ints are divided the result is an int, the fraction is truncated: 17 / 5 = 3. The remainder: 17 = 3·5 + 2 → 2.

Question 2easy

What is printed?

int x = 5;
int y = x++;
printf("%d %d\n", x, y);
Show answer
6 5

x++ (post-increment) first gives the old value, then increments. y = 5, x = 6.

Question 3easy

What is printed?

int x = 5;
int y = ++x;
printf("%d %d\n", x, y);
Show answer
6 6

++x (pre-increment) first increments, then gives the new value. x = 6, y = 6.

Question 4medium

What is printed?

double d = 7 / 2;
printf("%.1f\n", d);
Show answer
3.0

The right side is computed first: 7 / 2 is two ints → 3. Assigned to a double it becomes 3.0. For 3.5 you must write 7 / 2.0 or (double) 7 / 2.

Question 5medium

What is printed?

printf("%d\n", 10 > 5 > 1);
Show answer
0

In C a comparison produces 1 (true) or 0 (false) and is evaluated from left to right: (10 > 5) = 1, then 1 > 1 = 0. It is not read as a chain like in mathematics.

Question 6medium

What is printed? Does the program crash?

int i = 0;
int r = (i != 0) && (10 / i > 1);
printf("%d\n", r);
Show answer
0, no crash

&& short-circuits: if the left side is 0 the right side is never computed. So 10 / 0 does not run. The result is 0.

Question 7easy

What is printed?

printf("%d %d %d\n", 12 & 10, 12 | 10, 12 ^ 10);
Show answer
8 14 6

12 = 1100, 10 = 1010. AND: 1000 = 8. OR: 1110 = 14. XOR: 0110 = 6. The same as the mask operations in week 2.

Question 8medium

What is printed?

printf("%d %d\n", 5 << 2, 40 >> 3);
Show answer
20 5

Shifting left by k bits multiplies by 2k2^k: 5 · 4 = 20. Shifting right (for a positive number) divides by 2k2^k: 40 / 8 = 5.

Question 9easy

What is printed?

char c = 'A' + 2;
printf("%c %d\n", c, c);
Show answer
C 67

A char is an integer: 'A' = 65, 65 + 2 = 67. %c prints it as a character ('C'), %d prints it as a number (67).

Question 10medium

What is printed?

printf("%d\n", 2 + 3 * 4 % 5);
Show answer
4

* and % are at the same level, left to right: 3 * 4 = 12, 12 % 5 = 2. Then 2 + 2 = 4.

Question 11medium

What is printed?

int x = 4;
printf("%d %d %d\n", !x, !!x, x > 3 ? 10 : 20);
Show answer
0 1 10

Every nonzero value is true. !4 = 0, !!4 = !0 = 1. The conditional operator: x > 3 is true → 10.

Question 12hard

What is printed?

int g = 10;

void show(void) {
    int g = 3;
    printf("show: %d\n", g);
}

int main(void) {
    show();
    printf("main: %d\n", g);
    return 0;
}
Show answer
show: 3 / main: 10

The int g inside show is a new local variable and shadows the global g. The global g is not touched. main sees the global g: 10.

Verification: Every code snippet was compiled and run as C99 with Zig cc (clang), and the outputs were copied exactly (verify/SEN2201/c/w11a.c; the first program and the char example are at the start of w11b.c; node verify/SEN2201/c/run.mjs w11).

Review card

int / int -> int, TRUNCATED: 7/2=3, -7/2=-3 | the sign of % comes from the left: -7%3=-1 double d = 7/2; -> 3.0 (the division was done as int first) x++ gives the old value, ++x the new value | y = x++ -> y is the old x True = nonzero, false = 0 | a comparison produces 1/0 | 10>5>1 = 0 && and || short-circuit: if the left side decides the result, the right does not run ! logical (!4=0) | ~ bitwise (~5=-6) | & | ^ bitwise | << *2^k | >> /2^k char = small integer: 'A'=65, '7'-'0'=7, 'b'-'a'+'A'='B' Precedence: () > ++ -- ! ~ (unary) > * / % > + - > << >> > < <= > >= > == != > & > ^ > | > && > || > ?: > = += ... A local variable shadows the global | a block {} is a new scope printf: %d int, %c char, %f double (%.2f), %x hex, %s string In LC-3 C, int is 16 bits (2's complement), on a PC usually 32 bits