MPATE-GE 2618: C Programming for Music Technology Unit 4.1
Memory Memory in the computer can be thought of as a long string of consecutive bytes. Each byte has a corresponding address. When we declare a variable, we are in essence telling the computer to find a section of memory large enough for our needs. When the computer finds such a location, it assigns the variable name to the address of that location in memory. us each variable name is only an alias for the memory location.
Memory Address of num in memory int num = 7; 2024 2025 2026 2027 00000000 00000000 00000000 00000111 (Big Endian) 2024 2025 2026 2027 00000111 00000000 00000000 00000000 (Little Endian)
Arrays in memory When we declare an array by putting an index [x] after our variable name, we are telling the computer to find a consecutive section of memory large enough to store x values of that type. Note, they must be consecutive! e name of the array corresponds to the base address, or the starting point. int array[4]; Assuming the computer assigns array to the memory location beginning at 4096, where does the array end? e computer will allocate enough memory to store 4 ints. Assuming that an int is 4 bytes, that means that our array will be 4 * (size of int), or 4 * 4, or 16 bytes. It will thus extend to from 4096 to 4111.
Indexing arrays Why do array indices always begin at 0? Because the index is actually specifying the offset from the base address. Say that we wanted to find the second element of our array from before. We can access that location by typing array[1]. e computer, whenever it sees this subscript notation, uses the following to compute the memory location it should access: base address + (index * size of variable type) So in our case above, we would find the second element at 4096 + (1 * 4), or 4100. What would happen if we try to access information outside our array, say by referring to array[4]? e computer would return whatever was stored at 4096 + (4 * 4), or 4112. We have no way of knowing what is located at this position.
Two-dimensional arrays in memory Say we have a two-dimensional array: int array[4][3]; e first offset refers to rows, the second to columns. In memory, each row is stored consecutively. So if array begins at address 2024, array[2][1] refers to the position: base_addr + (row_offset * num_cols * int_size) + (col_offset * int_size) 2024 + ( 2 * 3 * 4 ) + ( 1 * 4 ) = 2052
e sizeof operator e sizeof operator returns the number of bytes for a given type or variable. unsigned long number; int size; size = sizeof(unsigned long); size = sizeof(number); // equiv. to line above 7
Virtual memory (VM) Whenever a process is created, the kernel provides a chunk of physical memory which can be located anywhere at all. However, through the magic of virtual memory (VM), the process believes it has all the memory on the computer. is is largely different from the virtual memory in the context of using hard drive space as memory when RAM runs out. Each process is given physical memory called the process s virtual memory space. 8
Typical memory layout High Address Args and environment vars STACK Command line arguments & environment variables A collection of stack frames Unused memory Low Address HEAP Uninitialized Data Segment (bss) Initialized Data Segment Text Segment Dynamic memory Static vars/uninitialized global vars (set to zero) Initialized global vars e program s code Note: bss = Block started by symbol
Call stack e stack stores information about the active subroutines of a program (also referred to as call stack or run-time stack). e main reason for the stack is to keep track of the point to which each subroutine should return control when it finishes executing. e caller pushes the return address onto the stack; the called subroutine, when it finishes, pops the return address off the call stack If a called subroutine calls on to yet another subroutine, it will push its return address onto the call stack, and so on, with the information stacking up and unstacking as the program requires. If the pushing consumes all of the space allocated for the call stack, an error called a stack overflow occurs (e.g. when you have a recursive function that never returns). 10
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() 11
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() 12
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() Frame for first_function() Return to main(), line 9 Storage space for an int Storage space for a char Storage space for a void * 13
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() Frame for first_function() Return to main(), line 9 Storage space for an int Storage space for a char Storage space for a void * 14
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() Frame for first_function() Return to main(), line 9 Storage space for an int Storage space for a char Storage space for a void * Frame for second_function(): Return to first_function(), line 20 Storage space for an int Storage for the int parameter named b 15
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() Frame for first_function() Return to main(), line 9 Storage space for an int Storage space for a char Storage space for a void * 16
1 #include <stdio.h> 2 void first_function(void); 3 void second_function(int); 4 5 int main(void) 6 { 7 printf("hello world\n"); 8 first_function(); 9 printf("goodbye world\n"); 10 11 return 0; 12 } 13 12 void first_function(void) 15 { 16 int foobar = 3; 17 char ch = 'c'; 18 void *ptr = NULL; 19 second_function(foobar); 20 foobar = 0; 21 } 22 23 void second_function(int b) 24 { 25 int n = b; 26 } Stack frames Frame for main() 17
Automatic vs. static variables Variables declared and passed to functions are automatic variables. As soon as you leave the function, they disappear. Static variables, on the other hand, stay around for the life of the entire program no matter where they are. void func(); void main(void) { int i; for (i = 0; i < 3; i++) { func(); } } void func() { static int i = 0; i++; } 18
Using multiple files If your projects start to get very large, you don t want to put all your code in one file. Instead, you want to divide it into separate.c files representing different modules. e.g. graphics functions in one module, sound in another, file I/O in yet another, etc. N.B. there is no reason to create modules just to divide something up for the heck of it. Do it to make your code easier to understand. Also, when you want functions in a file to be accessible to other modules, it s a good idea to put the prototypes in a header (.h) file.
Making sure code isn t redefined; Using make You ll get a compilation error if you have something defined twice. To prevent this from happening with your.h files, use the #ifndef, #define, and #endif preprocessor directives. e more files you have, the longer the input will be for gcc. With that in mind, starting with Problem Set 3, you will be using the make utility. Make looks inside a file called Makefile that you create for instructions on how to compile your projects. See the multifile project for how to use.h files and make.
e first computer bug sort of
Using gdb Most of the commands can be executed simply by typing their first letter (e.g. p for print). Functions and variables names can be autocompleted by hitting tab. Getting started (make sure you compile with g flag). % gdb foo debug foo (gdb) run start foo
gdb - Breakpoints break bar temporarily stops execution as soon as function bar is called break baz.c:n temporarily stops execution just before line n in baz.c info breakpoints list breakpoints and their numbers delete i delete breakpoint i delete delete all breakpoints clear n delete breakpoint at line n
gdb Stepping through code next execute the next line of code next n execute the next n lines of code step step into the next line of code continue continue execution from wherever you are to next breakpoint, if any finish finish the current function s execution list n print out some lines of code surrounding line n backtrace (or bt) display the stack frame (i.e. the series of function calls that got you there) frame n change the current frame to frame n
print v print the value in v display v continually display the value in v print a[i] print the ith element of array a print obj.b print obj s b data member gdb Printing variables