#include #include // max total size of statically allocated arrays is platform-dependent // (platform = OS + hardware, so Linux+Intel or macOs+Mac or Android+ARM) #define MAX_STATIC 1000000 /** * An example of the difference between statically allocated arrays, * variable-length arrays, and dynamically allocated arrays (1-D and 2-D). */ int main(int argc, char *argv[]) { // some dummy values to attempt illegal operations with and to // see addresses of int dummy_int = 42; int dummy_array[] = {0, 0, 0, 0, 0}; printf("&dummy_int = %p\n\n", (void *)&dummy_int); // statically-allocated array used as expression gives address of element // at index 0; address of statically-allocated array gives the same thing // (there is no separate storage for a pointer to first element, but // the compiler knows how to compute its address -- it is a fixed offset // from the start of the current function's stack frame) int hof[] = {20, 33, 8, 4, 5}; printf("hof = %p\n", (void *)hof); printf("&(hof[0]) = %p\n", (void *)&(hof[0])); printf("&hof = %p\n\n", (void *)&hof); // hof = dummy; // illegal -- can't assign to static array // same thing for a variable-length array even though the address of the // first element is actually stored somewhere (the compiler just won't // let you get at it) int size = argc >= 2 ? atoi(argv[1]) : 1; if (size <= 0 || size > MAX_STATIC) { fprintf(stderr, "%s: bad size %d for variable-length array\n", argv[0], size); return 1; } int vla[size]; for (int i = 0; i < size; i++) { vla[size] = 10; } printf("vla = %p\n", (void *)vla); printf("&(vla[0]) = %p\n", (void *)&(vla[0])); printf("&vla = %p\n", (void *)&vla); // vla = dummy; // still illegal // the following is platform-dependent and changes when you change // the code in main: I looked at the assembly-language generated by // gcc on the Zoo to determine that the address of vla is stored 60 // bytes after dummy_int int *address_after_dummy = &dummy_int + 15; int **address_of_vla = (int **)address_after_dummy; printf("pointer to vla's elements is stored at %p\n", (void *)address_after_dummy); printf("the pointer stored there is %p\n\n", (void *)*address_of_vla); // with a dynamically-allocated array, we explictly use a variable // (of type int *) to store the pointer to the elements int *dynamic = malloc(sizeof(int) * size); for (int i = 0; i < size; i++) { dynamic[i] = 21; } printf("dynamic = %p\n", (void *)dynamic); printf("&(dynamic[0]) = %p\n", (void *)&(dynamic[0])); printf("&dynamic = %p\n\n", (void *)&dynamic); free(dynamic); dynamic = hof; // legal -- there is a place to store the pointer // with a statically-allocated array the compiler knows how to compute // the address of a2D[0][0] and it knows the number of columns, // so it can compute the address of everything else in the array // without storing a pointer anywhere (and note a pointer to a row // is a pointer to the 1st element in that row) int a2D[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; printf("a2D = %p\n", (void *)a2D); printf("&a2D = %p\n", (void *)&a2D); printf("&a2D[0][0] = %p\n", (void *)&a2D[0][0]); printf("a2D[0] = %p\n", (void *)a2D[0]); printf("&a2D[0] = %p\n", (void *)&a2D[0]); printf("a2D[1] = %p\n", (void *)a2D[1]); printf("&a2D[1] = %p\n", (void *)&a2D[1]); printf("a2D[2] = %p\n", (void *)a2D[2]); printf("&a2D[2] = %p\n\n", (void *)&a2D[2]); // variable-length 2-D array has a place to store pointer to the 1st // element (and the number of columns [a register in this case]) // but otherwise works like a fixed-sized statically-allocated 2-D array int vla2D[size + 1][size + 2]; int height = size + 1; int width = size + 2; size = 0; vla2D[0][0] = 99; vla2D[height - 1][width - 1] = 101; printf("vla2D = %p\n", (void *)vla2D); printf("&vla2D = %p\n", (void *)&vla2D); printf("&vla2D[0][0] = %p\n", (void *)&vla2D[0][0]); printf("&vla2D[h-1][w-1] = %p\n", (void *)&vla2D[height - 1][width - 1]); printf("vla2D[0] = %p\n", (void *)vla2D[0]); printf("&vla2D[0] = %p\n", (void *)&vla2D[0]); printf("vla2D[1] = %p\n", (void *)vla2D[1]); printf("&vla2D[1] = %p\n", (void *)&vla2D[1]); address_after_dummy = &dummy_int + 5; int **address_of_vla2D = (int **)address_after_dummy; printf("pointer to vla2D's elements is stored at %p\n", (void *)address_after_dummy); printf("the pointer stored there is %p\n\n", (void *)*address_of_vla2D); // a dynamically-allocated 2-D array is an array of pointers to the // 1st elements of the rows; since a dynamically allocated array of X // is a pointer to the 1st X, this becomes a pointer to a pointer the // 1st element of the 1st row int **dynamic2D = malloc(sizeof(int *) * height); for (int r = 0; r < height; r++) { // need to allocate each row separately (and could give a different // size for each row) dynamic2D[r] = malloc(sizeof(int) * width); for (int c = 0; c < width; c++) { dynamic2D[r][c] = (r + 1) * (c + 1); } } printf("dynamic2D = %p\n", (void *)dynamic2D); printf("&dynamic2D = %p\n", (void *)&dynamic2D); printf("&dynamic2D[0][0] = %p\n", (void *)&dynamic2D[0][0]); printf("&dynamic2D[h-1][w-1] = %p\n", (void *)&dynamic2D[height - 1][width - 1]); printf("dynamic2D[0] = %p\n", (void *)dynamic2D[0]); printf("&dynamic2D[0] = %p\n", (void *)&dynamic2D[0]); printf("dynamic2D[1] = %p\n", (void *)dynamic2D[1]); printf("&dynamic2D[1] = %p\n", (void *)&dynamic2D[1]); for (int r = 0; r < size + 1; r++) { free(dynamic2D[r]); } free(dynamic2D); // illegal -- a2D degrades to a pointer to an array of arrays of 4 ints, // which is not the same thing as a pointer to a pointer to an int // (dynamic2D[r] and a2D[r] are computed differently) // dynamic2D = a2D; printf("%d\n", size); // please don't optimize out my size = 0 printf("%d\n", dummy_array[0]); // and shut up about unused variables }