/* arrays_in_c.c * For use in CSE351 Lec 13 on Arrays * * Compile with gcc -g array_in_c.c if you want to see source in GDB */ #include #include // see function f4() for use of this macro substitution #define AR_SIZE 5 int i; /* Here we use (a pointer to) a heap-allocated array. * Java analogy: reference to an array (int[] a = new int[5]) * Differences from Java: * - no default initialization * - no bounds checks */ int f1() { // Use malloc get "fresh" space in the Heap and return a pointer // to that array [space]. // Important: ar_p is a pointer, not an array variable. int *ar_p = (int*)malloc(5*sizeof(int)); // No default initialization, so better initialize. // Recall: array indexing still works on pointers. for(i=0; i<5; i++) { ar_p[i] = i*3; // 0, 3, 6, 9, 12 } // Remember ar_p[e] means *(ar_p+e) where the plus // scales by the sizeof the type of things ar_p points to. int i1 = ar_p[3]; // dereference 4 bytes at address 12 bytes above ar_p ar_p[4] = 42; // set the 4 bytes at address 16 bytes above ar_p to 42 ar_p++; // add 4 bytes to ar_p -- note: this does NOT work on array variables int i2 = ar_p[3]; // uses updated value for ar_p // No bounds checks in C (line below often works). //ar_p[100] = 910; free(ar_p); // We will discuss memory management later in course. return i1+i2; } /* We can pass pointers (to arrays) to other functions. * Usually pass array size/bounds as another argument. */ int f2(int *b, int len) { // passes a pointer (like a Java reference) if(len > 3) b[3] = 942; printf("inside f2(): sizeof(b) = %ld\n",sizeof(b)); printf(" &b (stack) = %p\n",&b); printf(" b = %p\n\n",b); return b[2]; } /* Can also write int x[] and such for function parameters, but * *confusingly*, this still means int *, maybe with some better compiler * warnings. Putting an array size in the [] for an argument has no effect, * but can be used for programmer documentation. */ int f3(int b[], int len) { // takes a pointer just like f2 above if(len > 6) b[6] = 942; printf("inside f3(): sizeof(b) = %ld\n",sizeof(b)); printf(" &b (stack) = %p\n",&b); printf(" b = %p\n\n",b); return b[2]; } /* Example calls to f2() and f3(). * Example use of macro substitution (customary to use all caps). */ void f4() { // AR_SIZE is defined at top of this file (not an actual variable). // Better coding practice so you only have to change size in // in one place (single source of truth). int *a_heap = (int*)malloc(AR_SIZE*sizeof(int)); printf("inside f4(): sizeof(a_heap) = %ld\n",sizeof(a_heap)); printf(" &a_heap (stack) = %p\n",&a_heap); printf(" a_heap (heap) = %p\n\n",a_heap); f2(a_heap,AR_SIZE); // sets a[3] to 942 f3(a_heap,AR_SIZE); // happens to have no effect because !(6 < 5) } // ------------------------ // typedef creates "a more convenient name" for a type, but is interchangeable // with its definition. Syntax is weird, especially for arrays. // Java analogy: none typedef int zip_dig[5]; // now we can use "zip_dig x" anywhere we can use "int x[5]" and // it means the same thing int f6(zip_dig b, int len) { if(6 < len) // can reverse order for comparisons, but not assignment b[6] = 942; return b[2]; } // these do NOT work because we need int*, /not/ int [5]: // and int [5] means int * only in function parameters // zip_dig x = (int *) malloc(5*sizeof(int)); // int * x = (zip_dig) malloc(5*sizeof(int)); // zip_dig x = (zip_dig) malloc(5*sizeof(int)); // --------------------------- // stack-allocation of arrays // * have so far seen putting a pointer to an int-array on the stack // * now see putting the array itself on the stack: // * for a local-variable declaration, that's what int x[5] means // Java analogy: none int f8() { int a1[5]; // uninitialized space for 5 ints on stack (local array variable) zip_dig a2; // another uninitialized local-array of 5 ints for(i=0; i<5; i++) { a1[i] = i*3; a2[i] = i*4; } // special syntax for explicit initialization of each array element int a3[5] = {9, 8, 1, 9, 5}; zip_dig a4 = {9, 0, 2, 1, 0}; // Array name (a1, a2, a3, or a4) is implicitly converted to the // address of array [almost] /everywhere/. // Because of this, you get strange behavior with address-of operator (&). // That is, writing &a1 or &a3 means a1 or a3. int *p, *p2; p = a1; //p2 = &a2; // causes warning, don't do it printf("inside f8(): sizeof(a1) = %ld\n",sizeof(a1)); printf(" &a1 = %p\n",&a1); printf(" a1 = %p\n\n",a1); //f6(a1,5); // "looks" like types line up, but implicit & is happening f3(a1,5); // implicit & here too //f6(p,5); // this is fine too i = a3[1] + p[1]; // (yep that's *(&a3 + 1) + *(p+1)) // cannot do this: no way ever to assign to a (whole) array // (need a loop or library/helper function) // a4 = p1; // but this works fine: a4[2] = p[3]; return a1[0] + a2[0] + a3[0] + a4[0] + i; } // excercise from Lec 13, Slide 6: int val[5] = {9,8,1,9,5}; // global array (not on the stack - appears in Static Data) // suppose val is at address x (so occupries 20 bytes from x to x+20) // what is the type and value of each of these expressions: // type value // val[4] // // val // // val + 1 // // &val[2] // // val[5] // // *(val+1) // // val + i /* Demonstrate use and handling of arrays and pointers in terms * of creation and across function calls. * Also demonstrates memory layout sections. */ int main(int argc, char *argv[]) { printf("\nGlobal var: &val = %p\n\n",&val); printf("Calling f4, which uses array on the Heap:\n"); printf("-----------------------------------------\n"); f4(); printf("Calling f8, which uses array on the Stack:\n"); printf("------------------------------------------\n"); f8(); return 0; }