Week 5 #
Programming in C #
Memory Model #
- Array of bytes, “Addresses” are indexes
- Variables may occupy several consecutive bytes, its address refers to the first occupied byte
- “Pointer”: Variable/parameter that stores an address
int i = 69; // Suppose i was occupying bytes 45 to 74
int *p = &i // 45
Memory Regions #
- Text:
- Stores code
- Pointers pointing to functions point here
- Global:
- Stores global variables
- Stack:
- Used for function calls
- Stores local variables
- Auto allocation and deallocation
- Heap:
- Manual allocation (
malloc(), calloc()
) and deallocation (free()
) - Used for dynamic data outside of functions
- Manual allocation (
Global Variables #
- Two types of varaibles
int pubVar = 10; // top-level public global variable int f() { static int privVar = 1010; // function private global variable pubVar++; privVar++; ... }
Integer Types #
- All possible combinations: {
signed, unsigned
$\times$char, short, int, long, long long
} - Byte size depends on platform
- eg.
x86-64
Type Size (bytes) char
(defaultsigned
)1 short
2 int
4 long
8 long long
8
- eg.
Integer Literal Notation #
Literal | Type |
---|---|
3 | int |
c | char |
3U | unsigned int |
3L | long |
3UL | unsigned long |
3LL | long long |
3ULL | unsigned long long |
printf("%lu\n", 3UL) // Good usage
printf("%lu\n", 3) // Bad since 3 is `int`
Type Casting with Numbers #
- Larger size type to smaller:
- Automatic conversion
- Lose some information in a natural way (
double
toint
removes decimal place) - Better to explicitly typecast:
double d = 420.69; int i = (int)d;
- Smaller size type to larger:
- Automatic conversion
- Completely lossless
Implicit Number Promotion #
- The smaller operand type gets promoted to the larger operand type
- Note:
char
andshort
are always promoted toint
- Note:
- eg.
// Suppose the following double d = 65.0; int i = 65; char c = 'A'; /** i / c -> promotes c to `int` and preforms integer division d / j -> promotes j to `double` and preforms floating-point division **/
Enumeration Types #
- New “types” and integer constant names
enum rps { ROCK, // ROCK = 0 PAPER, // PAPER = 1 SCISSORS // SCISSORS = 2 }; enum coin { HEAD, // HEAD = 0 TAIL // TAIL = 1 }; enum rps a = PAPER; // a = 1 enum coin c = HEAD; // c = 0
- “Types” are simply
int
, are mixable and not checked - Practically useful for meaningful names only
Union Types #
- Overlapping “fields” that share the same space
union myUnion { // sizeof(myUnion) = "largest field size" = 4 (for this example) unsigned short s; unsigned int i; unsigned char b[4]; }; union myUnion u; // can use u.s, u.i, u.b[j] etc.
- Use Cases:
- High-level: Data has multiple mutually exclusive cases
- Low-level:
- Store an
int
ini
- Read
b[0]
tob[3]
to discover howi
splits
- Store an
Tagged Union Idiom #
- Example: Suppose you wanted an array that holds both
int
anddouble
- Idiom: Make an outer
struct
struct int_or_double { enum { INT, DOUBLE } tag; // remembers which case you are in union { // shares the same space in memory regardless of int or double int i; // case value is an int double d; // case value is a double } data; }; struct int_or_double a[10]; // as wanted
Type Alias with typedef
#
- Very general, i.e can use with
struct
,enum
,int
,double
etc.typedef struct node { int i; struct node *next; } nodetype; // use `nodetype` instead of `struct node`
- Cannot use the same
typedef
name for more than one thingtypedef coin { HEAD, TAIL } coin; typedef int coin; // illegal since `typedef coin` is already defined