 If I haven't made it clear yet, we shouldn't presume anything about the relative positions of variables in memory. If we have a contiguous chunk of memory on the other hand, then it can make sense to play around with pointer arithmetic. So how do we get contiguous chunks of memory? There are two ways, by allocating memory on the heap or by creating arrays on the stack. We'll look first at allocating memory on the heap. Recall from earlier videos that the heap space of a process starts out unallocated, meaning that the OS hasn't actually mapped the processes virtual addresses to actual physical memory, and so using those unallocated addresses triggers a hardware exception. To use heap space, a process must explicitly request that the OS allocate a chunk with a system call. In the system call, the process specifies how big the chunk should be, and the OS responds either with the starting address of a newly allocated chunk of that size, or with an error if the request cannot be fulfilled. Once a process no longer needs a chunk of heap space, the process should tell the operating system to deallocate that chunk with another system call to which is passed the chunk's starting address as argument. If a process hangs on to allocated chunks it doesn't need, the process wastes memory and risks running out, especially if the process allocates many chunks over a lengthy running time. A server program, for example, is typically kept running for many days if not months or years, and so failing to properly deallocate unused memory will very likely lead to the process exhausting available memory at some point. In garbage collected languages, the language itself does the allocating and deallocating for us, but in C, that job is left to the programmer. The C standard library contains the functions malloc and free, which invoke the allocate and deallocate system calls respectively. malloc, short for memory allocation, takes a number of bytes to allocate as argument and returns a void pointer. So here the void pointer returned by the first call to malloc is assigned to void pointer variable v, and the void pointer returned by the second call to malloc is assigned to float pointer f. We then do whatever we want with these two heap chunks represented by these pointers, but once we're done with them, we should deallocate these chunks by passing the pointers to free. The free function expects a void pointer as argument, so we can pass f to free without having to cast. When allocating memory, we very often want chunks sufficient to store a particular number of values of a particular type. For example, I might want to store 100-ints. If I know that my target platform uses 4-byte-ints, I could then simply allocate 400 bytes, but what if I intend to compile my code for multiple platforms? The solution to this problem is the size of operator, which returns the size in bytes of a specified type. Size of is a strange operator in two regards. First, it is written as a word rather than a symbol. Second, size of takes a type as operand, not an expression. Anyway, here are three examples. The first returns the number of bytes occupied by an int. The second returns the number of bytes occupied by a double. And the third returns the number of bytes occupied by a float pointer. Notice that the float pointer type requires parense because otherwise the asterisk wouldn't be considered part of the type. For consistency and clarity, some programmers like to always surround the type in parentheses in the style of a function call, even though size of is not a function. So here we put size of to practical use. We invoke malloc with the argument seven multiplied by the size of int. So say, assuming four byte ints, malloc here returns a chunk that is 28 bytes. If we then assign 12 to the dereference of p plus zero, we are writing the int value 12 into the first four bytes of the chunk. Assigning 13 to the dereference of p plus one writes the int value 13 into the second four bytes of the chunk. And then assigning 14 to the dereference of p plus six writes the int value 14 into the last four bytes of the chunk. If we compile and run this code on a platform in which ints are some different size, it will still work as intended. The allocated chunk will always be the right size to fit seven ints. Remember that the memory allocation system call is just a request, not an order. And so sometimes the OS will refuse the allocation, such as when the system has no more memory to give. Therefore in practice, we should always check the result of malloc for a null pointer, the value returned when the allocation request is refused. Here for example, before using the allocated memory return to p, we first check if p is null and branch accordingly. In the branch where allocation fails, we may simply have to abort our program. In the branch where the allocation succeeds, we do what we want with the memory chunk, but then must remember to deallocate the chunk when we no longer need it. What C calls an array is a chunk of memory allocated on the stack, rather than the heap. Because an array is stack allocated as part of its containing function stack frame, it is automatically deallocated by the language. When execution leaves the scope in which an array is allocated, the memory occupied by the array should be considered deallocated. And so any pointer values representing addresses in bounds of the array should no longer be used. Accessing the memory of an out-of-scope array won't always trigger a hardware exception, but accessing out-of-scope memory should always be considered unsafe and illogical. Creation of an array looks like a variable declaration in which the variable name is suffixed with square brackets containing a size in terms of the type. The name of the array itself is not really a variable, but rather a pointer value representing the first byte of the array. Otherwise, array names are like pointer variables in all respects, except you may not assign them new values. So in these two examples, Jack is a float pointer value representing the address of the first byte of an array that is the size of eight floats. And Jill is a char pointer value representing the address of the first byte of an array that is the size of 200 chars. In this code, we have an int array i and a char array c. Because the name i is an int pointer value, we can assign it to the int pointer variable p. We can also dereference array names just like a normal pointer. Here, when we assign negative six to the dereference of i, we're writing the int value negative six at the start of the array. When we assign eight to the dereference of i plus one, we're writing the int value eight at the address which is one int sized chunk up in memory from the start of the array i. And the last line, when we assign the char value five to the dereference of c plus seven, we're writing the char value five at the address which is seven char sized chunks up in memory from the start of the array c. Note that we have to cast the number little five to char because the number little is an int and we can't assign an int to a char l value. Now let's look at a somewhat realistic example of something we might do with arrays and heap allocations. Here's a function sum that takes an int pointer nums and an int n as argument. Nums is expected to point to the start of a contiguous block of ints. Meanwhile, n is expected to denote the number of ints in the block. The function then loops from zero up to but not including n, and in each iteration we add to sum the dereference of nums plus our counter i. At the end of the loop, sum will hold the sum of every int in the block. So here's one example of how we might call the sum function. Say we create an array of three ints called a and then we populate the ints to the array with various int values. If we then pass the array name a and three to sum, it will return the sum of all those ints. Because the sum function expects an int pointer, we just as well could pass it a block of heap allocated memory. Here, assuming the colt and malloc doesn't return a null pointer, the pointer p represents a block the size of three ints and we can pass this block to sum. If you're wondering why we might ever use blocks of heap memory rather than arrays, which seem more convenient, well consider the problem with this function. This function fred returns a char pointer and the body simply creates and returns a pointer representing the start of the char array. While legal, what this function does is very dangerous because the array is allocated in the stack frame of the colt of fred and so goes out of scope once the call returns. So what the caller of fred gets back is a pointer to memory that is unsafe to use because it will likely get overwritten by the frames of subsequent function calls. And even if that doesn't happen, the processes stack boundary might move such that access in the memory of the array will trigger a memory access violation. All around, using stack allocated memory that goes out of scope is just a bad scene. In this alternative version of fred, we heap allocate a block of memory instead of creating an array. Because this block lives on the heap, it is safe to use until we explicitly deallocate it. Notice that it's the caller of fred's responsibility to test whether fred returns a non-null pointer and to free the block. Now that we've introduced arrays in heap allocation, we can talk about strings. In truth, C has no actual string type. When you see a string literal in double quotes, the expression is actually a char pointer value that represents the address of where the characters of the string are actually stored. The characters of a string literal are encoded as ASCII and stored in the processes text section, aka the unmodified part of your process's memory where the code itself is stored. The end of the string is denoted by a null byte, a byte that is all zero bits. So here we have a string literal with the characters ABC. This text is stored as four contiguous bytes in the processes text section, and the string literal is a char pointer value representing address of the first of these four bytes. So here when we assign the string to char pointer S and then dereference S plus one, we get back the char value residing in the second byte of the string, which will be 98, the ASCII value for lowercase b. The C standard library contains a number of functions for working with strings in this format. The Stirland function, for instance, short for string length, returns a number of characters in a string up to but not including the null byte that terminates it. Here we pass Stirland the character pointer S, and Stirland will read the bytes at that address until it encounters a null byte and returns the count of bytes read, not including the null byte. So the int variable I will be assigned the value three. C has no built-in support for strings in other formats, but if you want to work with, say, unicode strings, you can do so by storing their bytes in arrays and heap allocated blocks. Just keep in mind that these standard library string functions will only correctly handle ASCII encoded characters, terminated by null bytes. When using unicode strings, you'll need unicode specific functions to provide the equivalent functionality on those strings.