January 29, 2019
For today:
1) Order Head First C or another book.
2) Read Think OS, Chapter 1 (make sure you get PDF version 0.8.0) and do the reading quiz
3) Get your Linux partition squared away (see below).
4) Start Homework 01
Today:
1) Stack and state diagrams
2) Linux tutorial Part 1
3) Think OS, Chapter 1
For next time:
1) Read Head First C Chapter 2
2) Finish Homework 01
Recall from Python:
1) When you call a function, Python puts a new frame on the stack.
2) The frame contains the parameters and local variables of the function.
3) When the function ends, the stack frame disappears (along with the parameters and local variables). Only the return value survives.
Almost everything is the same in C, with just a few differences:
1) In Python, we don't know or care how stack frames are arranged in memory.
In C, we know that they are arranged from top to bottom (high memory to low), and managed with a stack pointer, which is a special register that points to the current stack frame.
In a correct C program, we should never care how stack frames are arranged, but if you know what's going on, it can explain otherwise strange behavior.
2) There is also a subtle difference in the way we represent variables in a state diagram.
In Python, a variable is a reference; it can refer to any kind of value. So we draw it with an arrow.
In C, a variable is the name of a location; it refers to the contents of the location. We draw it by putting the name next to the box.
No arrows yet. We will use those for pointers and only for pointers. Coming soon.
Draw a stack diagram for the following program just before it executes return z in add().
/* add: return the sum of x and y */
int add(int x, int y) {
int z = x + y;
return z;
}
/* test_add: test add and print the result. */
void test_add() {
int sum = add(3, 4);
printf("%d\n", sum);
}
/* check_parity:
return "n is even" if n is even,
and "n is odd" otherwise
*/
char *check_parity(int n) {
switch (n%2) {
case 0:
return "n is even";
case 1:
return "n is odd";
}
}
void main() {
test_add();
char *s = check_parity(3);
printf("%s\n", s);
}
If you have a Linux environment set up, you should be able to try out the examples in the tutorial.
If not, sign up for PythonAnywhere and open a bash console.
1) Start here
2) Read Introduction to the UNIX Operating System
What's a shell? (BTW, writing a shell is a good Project 1 option.)
3) Do Tutorial One: ls, mkdir, cd, pwd
Additional topic: what's the difference between absolute and relative path names?
4) Do Tutorial Two: cp, mv, rm, rmdir, cat, less, head, tail, grep, wc
5) Do Tutorial Three: redirection, pipes, sort, who
We'll do more next time.
Optional UNIX reading:
Credit: Julia Evans, Bite Size Command Line
Think OS: Chapter 1
Note on reading Think OS: in the tradeoff between conciseness and readability, Think OS and Head First C are at opposite ends. You might have to read them differently.
Stages of compilation:
Preprocessing
Parsing
Static checking
Code generation
Optimization
Linking
Why should you care?
1) If you have not already cloned ExercisesInC, do it now.
2) cd into exercises/ex01 and load hello.c in the text editor of your choice. Compile hello.c and run a.out. Compile it so the executable is called hello, and run it.
3) Compile hello.c using the -c flag, which generates object code. Use nm to see what functions are defined in hello.c, and which ones are used but not defined.
Define a new function, and add a reference to another function. Run gcc and nm again to see the effect.
Run the following command to find out where libc.a lives:
gcc --print-file-name=libc.a
Then use nm to find out what's in libc.a
4) Compile hello.c using the -S flag, then load hello.s in a text editor. How much sense can you make of it?
Compile hello.s to generate a.out and run it.
5) Modify hello.c to add these lines:
int a = 3;
int b = 4;
int c = a + b;
printf("c is %d\n", c);
Compile hello.c to get hello.s. What does the generated code look like?
Find the instruction addl and replace it with subl. Compile your modified version of hello.s and run it.
Compile hello.c again with -O2 and see what the generated code looks like. What happened to the addl?
6) Add the following lines to hello.c, compile to hello.s, and load hello.s
if (c%2 == 0) {
printf("c is even\n");
} else {
printf("c is odd\n");
}
How much sense can you make of the assembly code? How does the modulus operator get compiled? What if you compute c%3 ?
7) Rewrite the if statement using a switch, compile, and look at the assembly code.
What happens if you forget the break statements? Why do you think the default behavior of the switch statement is to fall through? See this article on fallthrough.
8) Run gcc on hello.c so it runs the preprocessor only. Use wc to count the lines in the output. Add a line to include stdlib.h as well as stdio.h, and run the preprocessor again.
If you enjoyed these exercises, you might like this article: Understanding C by learning assembly
It might be a good (part of) Project 1.