/** hello.c * This is the code from the 9/13/21 evening session. * Compile: gcc -o hello hello.c * Run: ./hello */ #include #include // Demonstrate how to print out pointers (addresses) and // how to get an address of a variable on the stack. void first_demo(int N) { int *a; a = malloc( sizeof(int) * N ); // allocate space on the heap printf( "N is at address %p\n", &N ); // print address of N (on stack) printf( "a is at address %p\n", &a ); // print address of a (on stack) printf( "the ptr needs %lu bytes of space\n", sizeof(int *) ); // print number of bytes needed for the pointer printf( "a is pointing to this address on the heap : %p\n", a ); // print address on heap. } // Demonstrate how to allocate, set the values of, and print arrays. // They can be on the stack or on the heap. // We also introduce pointer arithmetic. // If you have a pointer to a 4-byte thing, then adding 1 to that pointer // adds 4 to it (so it increments by 1 4-byte thing). void second_demo() { // allocate an array on the stack int stacka[10]; for (int i = 0; i < 10; i++) { stacka[i] = 2*i; } // for (int i = 0; i < 10; i++) { // printf( "%d: %d\n", i, stacka[i] ); // } // allocate an array on the heap int *heapa; heapa = (int *)malloc( sizeof(int)*10 ); for (int i = 0; i < 10; i++) { heapa[i] = 2*i; } for (int i = 0; i < 12; i++) { printf( "%d: %d\n", i, heapa[i] ); } printf( "stacka is at address %p\n", &stacka ); printf( "heapa is at address %p\n", &heapa ); printf( "heaps points to %p on the heap\n", heapa ); int first = heapa[0]; // easy way to access 0th element of array //int second = heapa[1]; // easy syntax for accessing 1st element of array int second = *(heapa+1); // pointer arithmetic to access 1st element of array printf( "first %d, second %d\n", first, second ); printf( "heapa addresses: %p %p\n", heapa, heapa+1 ); printf( "stacka addresses: %p %p\n", stacka, stacka+1 ); // Let's examine just the first byte of the array. // Make a 1-byte variable (unsigned char) and tell it to take // the value at the address on the heap. unsigned char c; c = (unsigned char)*heapa; printf( "c is %u\n", c ); // Now, let's step through the array of 4-byte integers one byte at a time // Make a pointer to 1 byte and use pointer arithmetic in a loop // to access each element. unsigned char *ptrc; ptrc = (unsigned char*)heapa; printf( "ptrc %p %p\n", ptrc, ptrc+1 ); for (int i = 0; i < 8; i++) { printf( "byte %d: %u\n", i, *(ptrc+i) ); } // Result: it prints out 0, 0, 0, 0, 2, 0, 0, 0 // This demonstrates that the machine is little endian. } // Make an N-int array and returns. int* third_demo(int N) { // right way - make space on the heap. return (int *)malloc( sizeof(int)*N ); // wrong way //int b[N]; //return b; // Why is this wrong? Because the space on the heap is reused for someone else once third_demo // returns. } // Let's make an object-like thing called a struct (it has data fields but not methods) // By using typedef, we are making a new type, so we can simply refer to this as a Container. typedef struct { int a; unsigned char b; } Container; // Demonstrate how to set and get the fields of the struct // when the struct is on the stack. void fourth_demo() { Container c; c.a = 4; // If we have a Container, we use the . to access the fields c.b = 0; printf( "c.a is %d, c.b is %u\n", c.a, c.b ); printf( "c.a is at %p, c.b is at %p\n", &c.a, &(c.b) ); printf( "c is %lu big\n", sizeof(Container) ); } // Demonstrate how to set and get the fields of the struct // when the struct is on the heap (or ,more correctly, when we have // a pointer to the struct instead of the struct itself). void fifth_demo() { Container *ptrc; ptrc = (Container *)malloc( sizeof(Container) ); ptrc->a = 5; // use the arrow to access the field ptrc->b = 8; printf( "c.a is %d, c.b is %u\n", (*ptrc).a, ptrc->b ); // alternate method is to dereference the ptr, then use the dot. I think we need the parentheses to enforce precedence. printf( "c.a is at %p, c.b is at %p\n", &(ptrc->a), &(ptrc->b) ); printf( "c is %lu big\n", sizeof(Container) ); } // Demonstrate that we can allocate space, use it, free it, then possibly // still access. This is something it would be nice if C didn't allow us to do. void sixth_demo() { int *ptr; ptr = (int *)malloc(sizeof(int)); *ptr = 5; printf( "ptr is at %p, and points to value %d\n", ptr, *ptr ); free(ptr); // Don't do this! printf( "ptr is %p, and points to value %d\n", ptr, *ptr ); } // Demonstrate what happens if we never allocate the space, but try // to use the pointer. On the computer in Davis 117, it caused a // Segmentation Fault. In this case, that was akin to a Null Pointer Exception in Java. void seventh_demo() { int *ptr; printf( "ptr is at %p\n", ptr ); *ptr = 5; printf( "ptr has value %d\n", *ptr ); } // Double the value of each item in nums. // num_nums tells use how many items are "in" nums void double_it(int *nums, int num_nums) { for (int i = 0; i < num_nums; i++ ) { nums[i] = 2*nums[i]; } } // Demonstrate how to call a function and pass it a pointer to an array. void eighth_demo() { int *a = (int *)malloc( 4*sizeof(int) ); for (int i = 0; i < 4; i++) { a[i] = i+1; } double_it( a, 4 ); for (int i = 0; i < 4; i++) { printf( "%d\n", a[i]); } } // Swap the values at *a and *b. // a is a pointer to an int // b is a pointer to an int // we want what is in the locations they point to to be swapped. void swap(int *a, int *b) { int c = *a; *a = *b; *b = c; } // Demonstrate how to use the swap function to swap the values // of two variables in the ninth_demo function. // Creepy! void ninth_demo() { int num1 = 5; int num2 = 2; printf( "before %d %d\n", num1, num2 ); swap( &num1, &num2 ); printf( "after %d %d\n", num1, num2 ); } int main( int argc, char *argv[] ) { printf( "hello, world\n" ); //first_demo(3); //second_demo(); //int *a = third_demo(10); //printf( "a at %p\n", a ); //fourth_demo(); //fifth_demo(); //sixth_demo(); //seventh_demo(); //eighth_demo(); //ninth_demo(); return 0; }