Computer Organization and Design Pointers Arrays and Strings






![Pointer examples int i; int a[10]; int *p; // simple integer variable // array Pointer examples int i; int a[10]; int *p; // simple integer variable // array](https://slidetodoc.com/presentation_image_h2/4dc60fe6ed57aa8eb9d0e6ad0ac4065b/image-7.jpg)
![Pointer pitfalls int int i; // a[10]; *p; // simple integer variable // array Pointer pitfalls int int i; // a[10]; *p; // simple integer variable // array](https://slidetodoc.com/presentation_image_h2/4dc60fe6ed57aa8eb9d0e6ad0ac4065b/image-8.jpg)















- Slides: 23
Computer Organization and Design Pointers, Arrays and Strings in C Montek Singh Feb 19, 2018
C vs. Java ã For our purposes C is almost identical to Java except: l C has “functions”, JAVA has “methods” Ø function == method without “class” Ø i. e. , a global method l C has “pointers” explicitly Ø Java has them (called “references”) but hides them under the covers Ø JVM takes care of handling pointers, so the programmer doesn’t have to ã C++ is sort of in-between C and Java In this class, we will see how pointers/references are implemented in C
What is a “pointer” in C? ã A pointer is an explicit memory address ã Example l int i Ø i is an integer variable in memory Ø located at, say, address 1056 l int *p Ø p is a variable that “points to” an integer Ø p is located at, say, address 2004 l p = &i Ø the value in p is now equal to the address of variable i Ø i. e. , the value stored in Mem[2004] is 1056 (the location of i) Memory address i 1056 1060 1064 1068 p 1056 2000 2004
Referencing and Dereferencing ã Referencing an object means Memory l … taking its address and assigning it address to a pointer variable (e. g. , p = &i) ã Dereferencing a pointer means i 5 l … going to the memory address pointed to by the pointer, and accessing the value there (e. g. , *p) 1056 1060 1064 1068 ã Example int i; int *p; p = &i; *p = 5; // // // i is an int variable p is a pointer to int referencing i p is assigned 1056 dereference p i assigned 5 p 1056 2000 2004
Pointer expressions and arrays ã Dereferencing could be done to an expression l So, not just *p, but can also write *(p+400) Ø accesses memory location that is 400 th int after i ã Arrays in C are really pointers underneath! l int a[10]; // array of integers l a itself simply refers to the address of the l a is the same as &a[0] l a is a constant of type “int *” l a[0] is the same as *a l a[1] is the same as *(a+1) l a[k] is the same as *(a+k) l a[j] = a[k]; is the same as *(a+j) = *(a+k); start of the array // address of a[0]
Pointer arithmetic and object size ã IMPORTANT: Pointer expressions automatically account for the size of object pointed to l Example 1 Ø if p is of type “int *” Ø and an int is 4 bytes long Ø if p points to address 1056, (p+2) will point to address 1064 Ø C compiler automatically does the multiply-by-4 Ø BUT… in machine language, we will have to explicitly do the multiply-by-4 l Example 2 Ø char *q; // char is 1 byte Ø q++; // really does add 1 Memory address i 1056 1060 1064 1068 p 1056 1064 2000 2004
Pointer examples int i; int a[10]; int *p; // simple integer variable // array of integers // pointer to integer p = &i; p = a; p = &a[5]; *p *p = 1; *(p+1) = 1; p[1] = 1; p++; // // & means address of a means &a[0] address of 6 th element of a value at location pointed by p change value at that location change value at next location exactly the same as above step pointer to the next element
Pointer pitfalls int int i; // a[10]; *p; // simple integer variable // array of integers pointer to integer(s) So what happens when p = &i; What is value of p[0]? What is value of p[1]? Very easy to exceed bounds l C has no bounds checking!
Iterating through an array ã 2 ways to iterate through an array l using array indices void clear 1(int array[], int size) { for(int i=0; i<size; i++) array[i] = 0; } l using pointers void clear 2(int *array, int size) { for(int *p = &array[0]; p < &array[size]; p++) *p = 0; } l or, also using pointers, but more concise (more cryptic!) void clear 3(int *array, int size) { int *arrayend = array + size; while(array < arrayend) *array++ = 0; }
Pointer summary ã In the “C” world and in the “machine” world: l a pointer is just the address of an object in memory l size of pointer itself is fixed regardless of size of object l to get to the next object: Ø in machine code: increment pointer by the object’s size in bytes Ø in C: increment pointer by 1 (compiler knows object’s size) l to get the i-th object: Ø in machine code: add i*sizeof(object) to pointer Ø in C: add i to pointer ã Examples: l int R[5]; // 20 bytes storage l R[i] is same as *(R+i) l int *p = &R[3] is same as p = (R+3) (p points 12 bytes after start of R)
Strings in C ã There is no string type in C �� What? ! l Only low-level support for strings as character arrays Ø char s[] // array of characters Ø char *s // pointer to the beginning of a string l But a rich library of string processing functions (string. h) Ø reading: scanf(), fgets(), etc. Ø printing: printf(), puts(), etc. Ø processing: strcpy(), strlen(), strcat(), strcmp()
Strings in C: NULL termination ã All C functions assume string terminates with a NULL l NULL = ASCII 0 character Ø also written as ‘ ’ ã Example: l “hello” is actually { ‘h’ ‘e’ ‘l’ ‘o’ ‘ ’ } l uses 6 characters, not 5 Ø so, for a string with N real characters, declare it as char S[N+1] l but C functions define its length as 5 Ø strlen(“hello”) returns 5 l fputs(), printf(), etc. will print the string until they see a ‘ ’ ã Be mindful of the terminating character!
Declaring strings statically ã Statically (if size is known at compile time) l char S[6] = “hello”; // compiler puts in the size l char string_array[5][10]; l char S[] = “hello”; Ø an array of 5 strings, each up to 9 characters long (plus NULL) Ø string_array[0] is the 0 -th (very first) string Ø string_array[4] is the 4 th (last) string Ø string_array[0][4] is the 4 th character of the first string Ø etc.
Declaring strings dynamically ã Dynamically (if size is known only at run time) 1. declare a pointer Ø 2. determine size (at run time), e. g. , 99 real characters + NULL Ø 3. char *s; size is 100 allocate space in memory Ø Ø s = malloc(100); BUT remember: there is no bounds checking, so do not put a string with more than 99 real characters in s… ã Comparison to Java l char *s = malloc(100); l char[] s = new char[100]; // in C // in Java ã When finished with string l free(s); // free up the 100 bytes l Java: no need to free; JVM does garbage collection
Declaring strings statically/dynamically ã Examples 1. Declare an array of NUM strings, each at most LEN long (incl. the terminating NULL), where NUM and LEN are known at compile time Ø char string_array[NUM][LEN]; 2. Declare array of NUM strings, where only NUM is known at compile time Ø char *string_array[NUM]; Ø string_array[0] = malloc(length of string 0…); Ø string_array[1] = malloc(length of string 1…); Ø etc. 3. Declare an array of strings, where we don’t know how many strings there will be, and how long each will be, at compile time Ø char **string_array; Ø string_array = malloc(how many strings … * sizeof(char *)); Ø string_array[0] = malloc(length of string 0…); Ø string_array[1] = malloc(length of string 1…); Ø etc.
Declaring strings statically 1. Declare an array of NUM strings, each at most LEN long (incl. the terminating NULL), where NUM and LEN are known at compile time Ø char string_array[NUM][LEN]; Example: char string_array[3][10]; for(int i=0; i<3; i++) fgets(string_array[i], 10, stdin) H e H A l o n # $ ( i n # $ ( ! ^ * l a ! ! ! n o l h
Declaring strings dynamically 2. Declare array of NUM strings, where only NUM is known at compile time Ø char *string_array[NUM]; Ø string_array[0] = malloc(length of string 0…); Ø string_array[1] = malloc(length of string 1…); Ø etc. Example: char *string_array[3]; char temp[MAXLEN]; for(int i=0; i<3; i++) { fgets(temp, MAXLEN, stdin); string_array[i] = malloc(strlen(temp)+1); strcpy(string_array[i], temp); } H H e A i n l l l o o n h a ! ! ! n
Declaring strings dynamically ã For sorting: Swap strings by swapping pointers, not by copying contents! l char *t; // pointer to char l t = string_array[i]; // swap pointers! l string_array[i] = string_array[j]; // no copying chars l string_array[j] = t; t H H e A i n l l l o o n h a ! ! ! n
One more thing… Passing pointers as arguments to functions
Passing arguments by reference ã Example 1: l reading values into variables int x, y, z; scanf(“%d%d%d”, &x, &y, &z); l scanf() changes the values of x, y and z! How? Ø we provide it the addresses where x, y and z are located ã Example 2: l a function to double the value given to it void double_it(int x) { x = x*2; } l doesn’t work… because x is “passed by value” Ø a copy of x is modified inside the function, not the original
Passing arguments by reference ã Example 2 take 2: l a function to double the value given to it void double_it(int *x) { *x = (*x) * 2; } int main() { int y = 10; double_it(&y); // y is now 20 } l works! Because y is “passed by reference” Ø the address in memory where y is stored is sent to the function, which modifies it correctly at that location
Passing arguments: C vs. Java ã Which of these will work in Java? WHY? 1. Modify an integer double_it(int x) { x = x*2; } 2. Modify an object double_it(Point p) { p. x = p. x*2; } 3. Swap two objects swap(Point p 1, Point p 2) { Point temp = p 1; p 1 = p 2; p 2 = temp; }
Passing arguments: C vs. Java ã Java: l passes arguments by value for all the primitive data types Ø int, double, float, etc. double_it(int x) { x = x*2; } // won’t work l passes objects by reference Ø so member data of an object can be modified double_it(Point p) { p. x = p. x*2; } // works! l BUT: references are passed by value to methods… Ø so cannot swap two objects swap(Point p 1, Point p 2) { Point temp = p 1; p 1 = p 2; p 2 = temp; } // won’t work! ã Understanding C pointers will demystify everything!