C Notes
Recently I had to study C in order to complete a couple of school projects. I’ve never been proficient at C so I had to go back to the books.
I decided to study Beej’s Guide to C Programming and these are the notes I considered important from my readings.
Operators and expressions
- sizeof operator tells the size in bytes that a particular variable or data type uses in memory.
- % modulo operator (the remainder of a division)
- ? ternary operator. It represents an expression whole value depends on the result of a conditional. Example:
# If X is greater than 10, then add 17 to Y, otherwise add 37.
y += x > 10 ? 17 : 37;
- pre-decrement / pre-increment the value of a variable is decremented or incremented before the expression if evaluated. Representation:
++i;
--i;
- post-decrement / post-increment the value of a expression is first computed with the value as-is, and then the value is decremented or incremented after the value of the expression has been determined. Representation:
i++;
i--;
- char is always one byte in size.
- %zu print the sizeof a variable.
- %s print a string.
- %d prints a decimal.
- %f prints floats.
Pointers
- pointer is the address of some data in memory.
- & returns the ‘memory address of’ a pointer. In fact, it returns the first byte of the address.
- %p prints a pointer.
- We save pointers in a variable (pointer variable) so that we can use them later.
- A pointer to a variable is a “reference to a variable”.
- We identify a pointer by the asterisk before the variable name, example *p.
- We can create pointers of different types, example: int *p (pointer to an int).
- A NULL pointer doesn’t point to anything.
- * dereference operator. Same character as pointer but different meaning. Dereference operator tells the computer to use the variable the pointer points to instead of using the pointer itself. Example:
int i;
int *p; # not dereferencing
p = &i; # p points to i
i = 10;
*p = 20; # BINGO: dereference. p is an alias to variable i.
- Remember that a pointer is just a number. It’s a number that represents an index in computer memory, typically one that holds a value we’re interested in for some reason.
- You can declare a pointer as void:
void *foo = nil;
, meaning it has no type. In that case, you’re telling the compiler that foo is a pointer, which is really all it needs to know in order to create foo. Without the *, you’re trying to declare a variable that has no type, and that’s not something that a compiler can work with. - How do we ensure there’s only one “owner” for a piece of memory? One (simple) way to achieve this in C is for us to pass a pointer to the pointer.
- For double pointers, we need to double dereference the pointer to get back to the final variable value. Example:
**p
. - You can get a pointer to anything with & (including to a pointer).
- You can get the thing a pointer points to with * (including a pointer).
- There exist also pointers to functions. Functions are just collections of machine instructions in memory, so there’s no reason we can’t get a pointer to the first instruction of the function. We can identify a pointer to a function such as: (*pf), the parenthesis and the asterisk is the giveaway that it’s a pointer to a function. Example:
# The following is a pointer to a function that takes two int arguments and returns an int.
int (*pf) (int, int)
Structs
- struct is a user defined type that holds multiple pieces of data, potentially of different types.
- . the dot operator is used to access the individual fields of a struct, as:
structName.structField;
- The dot operator doesn’t work on pointers to structs. In order for it to work we need to dereference the struct.
- -> The arrow operator can be used instead. It acts like the dot operator in structs when working with pointers to structs.
- When using the arrow operator we no longer need to dereference the struct to access its fields like we would do if we were using the dot operator.
System Calls
- getaddrinfo() does all kinds of good stuff, including DNS and service name lookups, and fills out the structs we need for manipulation.
C Library Functions
- memset(void *str, int c, size_t n) copies the character c to the first n characters of the string pointed to by the argument str.
Arrays
- When you declare an array, you have to give it a size and the size has to be fixed.
- You can’t get the lenght of an array, C doesn’t record this information.
- You can’t initialize an array in C from a variable. Initializers must be constant values.
- You can’t have more items than what you initialized the array with but you can have less and the unsued ones will be initialized with zero.
- You can have multidimensional arrays in C.
- Just referring to the array name is the same as getting a pointer to the first element of the array.
Type qualifiers
- const is the most common type qualifier. It means the variable is constant and any attempt to modify it will result in a very angry compiler.
Extra notes
- optarg parse command-line options.
- switch(0) will always execute the block of code associated with the case 0.
- If you iterate over the bytes of any object, you get its object representation. Two things with the same object representation in memory are equal. If you want to iterate over the object representation, you should do it with pointers.