Abstract:
Systems programming is the activity of writing computer system software: used as a platform for other software, a layer of abstraction (scaffolding) i.e. the main ‘customer’ is other software, not necessarily users. Usually, system software has particular performance constraints such as: fast execution time, low memory consumption, low energy usage Because of this these languages allow for a more fine grained control over the execution of programs
Types of System Programming:
- Operating Systems
- Drivers
- Compilers
Core Concepts
Stack and Heap
When the lifetime of an automatically managed variable ends, its memory location is freed and can be reused by other variables
This memory management happens fully automatically as it is very easy to implement:
- every time a block is entered, put aside a location in memory for every variable declared in the block
- every time a block is exited, free the locations in memory for every variable declared in the block As this memory management strategy adds and removes memory locations in a last-in-first-out manner we call this stack-based memory management and the area of memory managed in this way the stack
The Heap which is managed manually by the programmer
Stack
Heap
Datatype Sizes
| Type (Signed/Unsigned) | Typical Byte Size | Value Range |
|---|---|---|
| char | 1 | [-127, +127] / [0,255] |
| short | 2 | [-32767, +32767] / [0,65535] |
| int | 4 | [-2147483647, +2147483647] / [0, 4294967295] |
| long | 4/8 | at least as for int |
| long long | 8 | [-2^{63}-1, +2^{63}+1] / [0, 2^{64}-1] |
Lexical Scoping
Each pair of brace: { } , is called a block in C and introduces a lexical scope. variable names must be unique in the same lexical scope. If multiple are defined, then the innermost blocks variable is used
Lifetime of Variables
Variables are stored at locations in memory that do not change over their lifetimes
There are three cases:
automatic
These are all variables declared locally in a block (i.e. inside a pair of {}) and their lifetime ends at the end of the block. All variables we have seen so far fall into this category.
int main() { int x = 42; } // end of the block - end of the lifetime of xstatic:
Variables declared with the static keyword or defined at file-level outside all blocks. The lifetime is the entire execution of the program.
int foo() {
static int count_calls_to_foo = 0; // the variable is initialized only once count_calls_to_foo++;
} // variable continues to liveallocated:
These are variables for which we must explicitly request memory using dynamic memory allocation functions (such as malloc). We manage the lifetime of these variables ourselves (next lecture!).
Multi Element Types
Struct
A struct consists of a sequence of members of (potentially) different types:
struct point { int x; int y; };
int main() {
struct point p = {1, 2};
printf("x = %d\ny = %d\n", p.x, p.y);
}Members are accessed like public class members in Java using the . notation
Members are stored next to each other in memory in the same order as defined in the struct
The type of a struct is written struct name, but we often use this trick to shorten it:
typedef struct { int x; int y; } point;
int main() { point p = {1, 2}; /* ... */ }String
Strings in C are represented as arrays of characters
char greeting[] = "Hello World";is the same as
char greeting[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};We use double quotes ” to write a (ASCII) string literal and single quotes ’ to write a character literal
Strings in C are terminated by the special character '\0' that is added automatically for string literals
To print a string with printf we use the %s formation character
printf("%s\n", greeting);If we forget the terminating '\0' character this will print the content of the memory until it hits the next bit pattern equivalent of '\0'!
Functions
Definition
Functions are probably the most important abstraction mechanism in computing science They allow us to write code in a modular fashion which we can then reuse
A function definition in C looks like this:
int max(int lhs, int rhs) {
if (lhs > rhs) { return lhs; } else { return rhs; }
}The return type specifies the data type of the value that will be returned after evaluating the function. If a function returns no value the special type void is used as return type
The name which should describe the behaviour of the function
A parameter list specifies the data type and name of each parameter expected by the function
The function body is a block containing the code executed when calling the function
To call a function we provide an argument for each parameter and capture/process the return value
int x = max(5, 3);Declaration
Instead of defining the full function, this only specifies the interface of how a function can be used
int max(int lhs, int rhs);This is important as this is what the Compiler looks at when it checks that the call being made is valid. The linker then searches for the definition, which can be in a different file