# CS 211 - Lecture 02 ## Arrays, Pointers Bernhard Firner 2026-01-26 --- ## Reading * Chapter 2 in CS:APP --- ## Primitive Types * `Primitive` types exist in the hardware * For example, in the CPU * Every other type of data must be assembled from primitive types * We'll get into the C way to do that in a later lecture --- ## Primitives * `int`, `unsigned int`, etc * Whole numbers, with or without negatives if they are signed or unsigned * `float`, `double` * Fractional numbers * `bool` * `char` * `void` --- ## Printf And Types * printf uses the % tokens to determine what to print * See [https://cppreference.com/w/c/io/fprintf.html](https://cppreference.com/w/c/io/fprintf.html) * `%d` and `%i` print signed integers * `%ld` and `%li` are `long` versions, holding larger numbers * `%c` is a single character * `%s` is a string * `%f` is a floating point number * `%.3f` would print with 3 digits of precision * `%p` is a pointer --- ## More `printf` ```C /* * This is an example program! * To compile code: * gcc example.c -o example */ #include
int main(int argc, char** argv) { printf("The name of the program is %s\n", argv[0]); printf("The first character of the program name is %c\n", argv[0][0]); printf("The first argument is %s\n", argv[1]); printf("The first character of the second argument is %c\n", argv[1][0]); float pi = 3.14159265358979; printf("Pi is about %f, or simply %.3f\n", pi, pi); printf("The first and second arguments are at %p and %p\n", argv[0], argv[1]); return argc; } ``` -v- ``` $ ./a.out test The name of the program is ./a.out The first character of the program name is . The first argument is test The first character of the second argument is t Pi is about 3.141593, or simply 3.142 The first and second arguments are at 0x7fff82006545 and 0x7fff8200654d ``` -v- ``` $ ./././././a.out test The name of the program is ./././././a.out The first character of the program name is . The first argument is test The first character of the second argument is t Pi is about 3.141593, or simply 3.142 The first and second arguments are at 0x7ffc49b1952d and 0x7ffc49b1953d ``` * `./` just means `current directory` in your shell * So you can repeat it as many times as you like --- ## Functions * A function has a return type, a name, and an argument list: ```C //This function takes in an int and returns double that value int doubleInt(int a) { return 2*a; } ``` --- ## Strong Types * It won't work with floats! * C++, java, etc have ways to make functions more flexible * But C is a thin layer on our hardware, and our hardware isn't flexible like that --- ## Main * Main is a special function * Normally declared: * `int main(int argc, char** argv)` * But this version is okay if you won't use any arguments: * `int main(void)` --- ## More about types * Did you notice that string isn't a type? * We printed one though: ```C printf("The first argument is %s.\n", argv[1]); ``` * A string is an array of characters * And argv is an array of strings --- ## Pointers and Memory * Some people find C pointers confusing * So ask any questions as they come up --- ## Simple Array ```C #include
int main(void) { // Create an array of 4 chars unsigned char chars[4] = {1, 2, 4, 8}; // Print them out printf("Char 1 is %i\n", chars[0]); printf("Char 2 is %i\n", chars[1]); printf("Char 3 is %i\n", chars[2]); printf("Char 4 is %i\n", chars[3]); return 0; } ``` --- ## Arrays * Arrays should feel similar to some of java's types * Indexing begins at 0 * If you don't initialize the array, it will be filled with arbitrary values * See [https://cppreference.com/w/c/language/array.html](https://cppreference.com/w/c/language/array.html) --- ## Array Elements
index
0
1
2
3
value
1
2
4
8
--- ## Pointers are Numbers * Pointers are numbers that correspond to a memory location ```C #include
int main(void) { // Create an array of 4 chars unsigned char chars[4] = {1, 2, 4, 8}; // Print out the memory locations printf("Char 1 is at %p\n", chars); printf("Char 2 is at %p\n", chars+1); printf("Char 3 is at %p\n", chars+2); printf("Char 4 is at %p\n", chars+3); return 0; } ``` --- ## Output ``` Char 1 is at 0x7ffd8bedd214 Char 2 is at 0x7ffd8bedd215 Char 3 is at 0x7ffd8bedd216 Char 4 is at 0x7ffd8bedd217 ``` * Notice that the value (the memory address) goes up by 1 each time * This is because a char is 1 byte large --- ## Pointers and Memory * Any array is a pointer, but we can have a pointer without an array * The `&` operator, called "address of", gets a pointer to an object ```C #include
int main(void) { // Create some chars. unsigned char a, b, c, d; // Print out the memory locations printf("Char 1 is at %p\n", &a); printf("Char 2 is at %p\n", &b); printf("Char 3 is at %p\n", &c); printf("Char 4 is at %p\n", &d); return 0; } ``` --- ## argv ```C #include
int main(int argc, char** argv) { printf("The program name is %s\n", argv[0]); return 0; } ``` * Name the program "blub.c" and compile with "gcc blub.c -o blub" * Run it, and the output will be: * The program name is ./blub --- ## Declaring a string * C gives us syntactic sugar to declare strings ```C #include
int main(int argc, char** argv) { // Array style char blub[5] = {'b', 'l', 'u', 'b', '\0'}; char* blub2 = "blub"; printf("The first string is %s\n", blub); printf("The second string is %s\n", blub2); return 0; } ``` --- ## Equivalent ```C char blub[5] = {'b', 'l', 'u', 'b', '\0'}; char* blub2 = "blub"; ``` * These two declarations are equivalent * C strings are just arrays, so they do not know their own length * The special `\0` (null character) is used to mark the end --- ## Characters * Use `man ascii` on linux to see all ASCII characters * You'll see '\0' has value 0, 'A' is 65, 'a' is 97, etc * Or see the [cppreference example](https://cppreference.com/w/c/language/ascii.html) --- ## Pointers * Pointers correspond to memory on your computer * The OS chooses an arbitrary range for each program * Allows for virtual memory, swap, virtual machines, etc * On an embedded device, all memory will refer to physical locations * Just think of things that way for now, until you take an OS course --- ## Arrays of other Types * Obviously we can have arrays of other types * Including pointers ```C #include
int main(void) { // Create some chars. char* str = "test"; char* pointers[4] = {str, str+1, str+2, str+3}; printf("Str 1 is at %s\n", pointers[0]); printf("Str 2 is at %s\n", pointers[1]); printf("Str 3 is at %s\n", pointers[2]); printf("Str 4 is at %s\n", pointers[3]); return 0; } ``` --- ## More Useful Example ```C char* busch = "Busch"; char* college_ave = "College Avenue"; char* livingston = "Livingston"; char* douglass = "Douglass"; char* cook = "Cook"; char* campuses[5] = {busch, college_ave, livingston, douglass, cook}; ``` --- ## argv again * These two versions of argv are equivalent: * `int main(int argc, char** argv)` * `int main(int argc, char* argv[])` --- ## argv details * argv looks something like this:
index
0
1
2
...
content
pointer1
pointer2
pointer3
...
* And `argv[0]` points to something like this:
index
0
1
2
3
4
5
6
character
.
/
b
l
u
b
\0
--- ## Pointers and Memory * Feeling good? * When we get to assembly, the idea of variables will go away * Data is either in a temporary location, or at a memory address --- ## Inspecting Memory * Let's do one more thing with pointers * We can inspect memory of our variables --- ## Examining Memory ```C #include
/* * This function doesn't return anything, so it returns 'void' * 'char' is the smallest unit of memory. * size_t is a special type that we use along with pointers. * It is guaranteed to have the same range as memory. */ void show_bytes(unsigned char* data, size_t len) { for (int i = 0; i < len; ++i) { printf("Byte %i is %u\n", i, data[i]); } printf("\n"); } int main(void) { // One byte char a = 10; // Two bytes (also called a word) short b = 10; // Four bytes on most systems (also called a double word) int c = 10; // Eight bytes long long int d = 10; // The sizeof operator returns the number of bytes in a type or variable. show_bytes(&a, sizeof(char)); // For the non-char types we need to cast to unsigned char* show_bytes((unsigned char*)&b, sizeof(short)); show_bytes((unsigned char*)&c, sizeof(int)); show_bytes((unsigned char*)&d, sizeof(long long int)); } ``` --- ## Output ``` Byte 0 is 10 Byte 0 is 10 Byte 1 is 0 Byte 0 is 10 Byte 1 is 0 Byte 2 is 0 Byte 3 is 0 Byte 0 is 10 Byte 1 is 0 Byte 2 is 0 Byte 3 is 0 Byte 4 is 0 Byte 5 is 0 Byte 6 is 0 Byte 7 is 0 ``` --- ## Endianness * Notice how the smallest part of the value is in the first byte? * This is **little endian** format * That's the standard for Intel (and Intel compatible machines) * Least significant byte first, most significant last * The opposite is **big endian** format --- ## Hexedicimal * Normally, we prefer to examine memory in hexedecimal * Change the printf string to "%x" * Use "%X" to print in upper case A-F characters * If we want to always print in pairs of characters, "%02x" ```C void show_bytes(unsigned char* data, size_t len) { for (int i = 0; i < len; ++i) { printf("Byte %i is 0x%02x\n", i, data[i]); } printf("\n"); } ``` --- ## Hex? * Maybe you haven't used this before * Hexedecimal is a base 16 number system * 0-9 remain unchanged * A is 10, B is 11, C is 12, D is 13, E is 14, F is 15 --- ## Why Hex? * Because all binary data is a power of 2 * Since it is made up of bits * One digit in hex goes from 0-15, and is 4 bits * $2^4 = 16$ * It would take too long to write things in binary, so we write them in hex --- ## Hex Ranges * A char is a byte, which is 8 bits * It takes two hex characters to represent * The maximum value is 255, which is 0xFF * The maximum value of a 4 byte int is 0xFFFFFFFF * Far easier to remember than 4294967296 --- ## Integer Ranges * On a 64-bit machine, an `unsigned int` holds values from $0$ to $2^{32}-1$ * An `int` holds negative values, from $-2^{31}$ to $2^{31}-1$ * Notice that $2^{31}$ is half of $2^{32}$ * So half of the range goes to the negative values * `unsigned long int` could be 32 or 64 bits * `unsigned long long int` will always be 64 bits --- ## Constants * C provides these values so you don't need to guess * `INT_MIN` and `INT_MAX` for `int` * Defined in [limits.h](https://cppreference.com/w/c/header/limits.html) * For fun, try adding $1$ to `INT_MAX` and see what you get! --- ## Using Hex * Hex is often used when manipulating binary data * But we'll leave some of those details for next time