330 likes | 355 Views
COMP ENG 2SH4 Principles of Programming McMaster University, 2017 Instructor: Sorina Dumitrescu. Topic 8. Pointers and Arrays. Dynamic Memory Allocation. Textbook Reading. Chapter 7, Sections 7-9. Pointer (Address) Arithmetic.
E N D
COMP ENG 2SH4 Principles of Programming McMaster University, 2017 Instructor: Sorina Dumitrescu Topic 8. Pointers and Arrays. Dynamic Memory Allocation
Textbook Reading • Chapter 7, Sections 7-9
Pointer (Address) Arithmetic • If pointer p points to an element of an array , and i>0 is of type int, then • p + i is a pointer which points to the element of the array situated i positions to the right, provided that it is within the array boundaries. • p - iis a pointer which points to the element of the array situated i positions to the left, provided that it is within the array boundaries. • Thus, p+i and p-i are pointers of the same type as p. • If p and q are pointers to elements of an array and q<p, then p – q isthe number of array elements between the pointed to elements, +1 (i.e. the difference between the array positions pointed to).
Example 1 double a[10]={0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0}; double *aPtr; aPtr = &a[4]; /* aPtr points to a[4] */ • aPtr + 3 • is a pointer of type double*. • Its value is &a[4+3], • it points to a[7]. Consider the statement: *(aPtr + 3) = 10.0; /* a[7] gets the value 10.0 */
Example 1 double a[10]={0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0}; double*aPtr; aPtr = &a[4]; /* aPtr points to a[4] */ • aPtr - 2 • is a pointer of type double*. • Its value is &a[4-2], • thus it points to a[2]. Consider the statements: *(aPtr -2) = 10.0; /* a[2] gets the value 10.0 */ *++aPtr = 10.0;/* same as *(++aPtr) = 10.0; */ /* aPtr is incremented by 1, thus it points to a[5]; a[5] gets the value 10.0 */
Example 2 • When we subtract two pointers, the result is an integer • If aPtr and bPtr point to elements of the same array, then aPtr-bPtrequals the difference between the subscripts of the pointed to elements. int i; int a[10]={10,11,12,13,14,15,16,17,18,19}; int*aPtr, *bPtr; aPtr = &a[8]; bPtr= &a[3]; i = aPtr – bPtr; // i is 8-3
Example 2 • When we subtract two pointers, the result is an integer • If aPtr and bPtr point to elements of the same array, then aPtr-bPtrequals the difference between the subscripts of the pointed to array elements. • In other words, aPtr-bPtr equals (value of aPtr – value of bPtr )/sizeof(T), where T is the data type of array elements. • sizeof(T) returns the number of bytes used to store a variable of type T. int i; int a[10]={10,11,12,13,14,15,16,17,18,19}; int*aPtr, *bPtr; aPtr = &a[8]; bPtr= &a[3]; i = aPtr – bPtr; /* (&a[8]– &a[3])/sizeof( int) == 8-3*/
a[0] 0x22aa00 00 0x22aa01 00 0x22aa02 00 0x22aa03 0A a[1] 0x22aa04 00 0x22aa05 00 0x22aa06 00 0x22aa07 14 a[2] 0x22aa08 00 0x22aa09 00 0x22aa0A 00 0x22aa0B 1E 0x22aa0C 00 0x22aa0D 00 0x22aa0E 00 0x22aa0F 28 0x22aa10 00 0x22aa11 00 0x22aa12 00 0x22aa13 32 Example 3 int main( void ) { int a[5] = {10, 20, 30, 40, 50}; int *p=a; printf("%d\n", *p); p++; printf("%p\n", p); p = p+2; printf("%p\n", p); } p 10 0x22aa04 0x22aa0C p++ p+2
Name of An Array • The name of an array is an address. • Therefore, it is of pointer type. • But, it is not a pointer variable. • It is aconstant pointer. • It cannot be modified. int a[6]; int *p=&a[1]; /* p is declared with type int* and assigned the address of a[1] */ a = p; /*syntax error – attempts to modify a constant pointer */
Name of An Array • The name of an array is the address of the first element. • It is aconstant pointerto the first array element. intaa[6]; • aais a pointer to aa[0] • aa+iis a pointer to aa[i] • *(aa+i) is another way of referring to aa[i]
Referring to Elements of An Array int a[10]; int *p; p = a; /* p is assigned &a[0]; p points to a[0] */ After p is assigned the value of a, we may regard p as another name for the array a. Then we have four possibilities to refer to element a[i] : • a[i] (array-subscript notation) • p[i] (pointer-subscript notation) • *(p+i) (pointer-offset notation) • *(a+i) (pointer-offset notation using array name)
a 2 4 6 8 9 p ... p+1 p a p+4 Referring to Elements of an Array. Example int a[ ] = {2,4,6,8,9}; int *p = a; *(p+5)=6; /* may lead to destruction of data or to program crash */ /* p == &a[0]; p and a can be used interchangeably to refer to arrays’ elements.*/ Instead of a[4] we can use p[4], or *(p+4), or *(a+4).
Looping through an Array int a[20]; int i; int*p; /* inputting data into the array*/ for(i=0;i<20;i++) scanf("%d",&a[i]); printf("\n"); /* printing each element of the array */ for(p=a;p<a+20;p++) printf("%d\t",*p); • The first loop uses the index i as the loop variable. • The second loop uses a pointer p as the loop variable. • Initially p points to the first array element. • At each iteration p is incremented. • After being incremented p points to the next array element.
Parameters of Array Type • When an array name is passed to a function, what is actually passed? • The address of its first element. • What is the parameter receiving this address? • It is a pointer. In the parameter list of a function definition or declaration data_type a[ ] and data_type *a are equivalent.
Parameters of Array Type. Example #include <stdio.h> #define N 3 void f1(int pa[], int n ); int main(void){ int i; int a[N]={0,1,2}; f1(a, N); for (i=0; i<N; i++) printf(“a[%d]=%d\t”,i,a[i]); } void f1( int pa[], int n ){ int i; for (i=0; i< n; i++) pa[i] *= 2; } #include <stdio.h> #define N 3 void f2(int *pa, int n ); int main(void){ int i; int a[N]={0,1,2}; f2(a, N); for (i=0; i<N; i++) printf(“a[%d]=%d\t”,i,a[i]); } void f2( int *pa, int n ){ int i; for (i=0; i< n; i++) pa[i] *= 2; }
Pointer and Array Declarations int a[3] = {1,3,5}; /* when an array is defined, memory is allocated for all elements of the array */ int*q = {1,3,5}; //syntax error /* when a pointer variable is defined a single memory location is reserved for the pointer */ • Pointer declaration and array declaration in the function list of parameters are equivalent. • Array declaration and pointer declaration outside the parameter list of a function are not equivalent.
Pointers and Strings char str[20]; scanf(“%s”, str); char str[20]; str=“Hello world!”; /* we can modify the elements of this array later */ char *p; scanf(“%s”, p);/* there is no space reserved in memory to store the characters; may lead to program failure or destruction of data; */ char *s; s = “John”; /* an array is created in the read-only memory to store the string. s is assigned the address of this array */
‘J’ ‘o’ ‘h’ ‘n’ ‘\0’ *s == s[0] == ‘J’ *(s+3) == s[3] == ‘n’ s Pointers and Strings char*s; s = “John”; /* an array to hold the string is created in the read-only memory; s is assigned the address of this array */ *s = ‘c’; /* program failure */
‘J’ ‘o’ ‘h’ ‘n’ ‘\0’ Pointers and Strings #include <stdio.h> int main(void) { char*s0=“John“; int i=0; i = my_strlen(s0); printf(“%d\n”,i); } /* returns the length of the string – the number of characters not including ‘\0’*/ int my_strlen(char *s){ int k; for(k=0; *s!=‘\0’; s++) k++; return k; } s0 s
Pointers and Strings #include <stdio.h> int main(void) { char array[]=“Welcome!“; char *ptr=“Bienvenue!“; int i=0; i += my_strlen(“Hello World!”); i += my_strlen(array); i += my_strlen(ptr); printf(“%d\n”,i); return 0; } int my_strlen(char *s){ int k; for(k=0; *s!=‘\0’; s++) k++; return k; } 30
sizeof Operator • "sizeof" can be applied to a variable name, a data type or an expression • Returns the number of bytes used in memory . • The result returned is of typesize_t(an integral type) . • sizeof( char ) • sizeof( 3.0 + 5.0 ) • sizeof( int [4] ) • sizeof( int * ) float f[10]; char s[] = "Canada"; char *p = “Bye!”; sizeof( p ); /* # bytes to store a pointer variable */ sizeof( f ); /* # bytes to store all array elements */ sizeof( s ); /* # bytes to store all array elements – including ‘\0’*/
Lifetime of C Objects • We have learnt how to create C objects ( e. g. variables, arrays) with • Automatic storage duration • Created when the block is entered • Destroyed when the block is exited • Static storage duration • Exist during the whole program execution • These two options allow for little control on the lifetime of objects.
Memory Management Routines • If we need to be able to control when objects are created and destroyed • Use dynamic memory allocation via C library memory management functions. • Functions malloc(), calloc(), realloc()allocate a contiguous portion of memory (a sequence of consecutive bytes) and return a pointer to it(a pointer to the first byte). • This portion of memory remains allocated until we deallocate it by calling function free(). • #include <stdlib.h>
Pointer to void • Pointer to void (void *) – a generic pointer – can represent any pointer type. • void*p; // declaration • A pointer to void contains a memory address, but the data type is unspecified (we don’t know to how many bytes it points to). • It cannot be dereferenced (using *p is not allowed) • Pointer arithmetic is not defined on void*.
C Library Memory Allocation Routines • void*malloc( size_t n ); • Allocates n consecutive bytes of memory • Returns a pointer to it (i.e., to the first byte) • Or returns a NULL pointer if the request cannot be satisfied. • The storage is not initialized. • void*calloc( size_t m, size_t n ); • Allocates memory space for an array of m objects each of size n bytes and returns a pointer to it. • Or returns a NULL pointer if the request cannot be satisfied. • The storage is initialized to 0.
Deallocating Memory • void free( void *p ); • Deallocates the portion of memory pointed to by p. • p must be a pointer to a block of memory previously allocated by malloc(), calloc() or realloc(). • It does nothing if p is NULL. • Allways free the memory that was dynamically allocated, when you do not need it anymore!!!
Optional: realloc() • void *realloc( void *p, size_t new_size ); • Changes the size of the block of memory pointed to by p to new_size (the new block could be somewhere else in memory) • The contents will be unchanged up to the smallest between the old and new sizes. • If the new size is larger the additional space is not initialized. • Returns a pointer to the new block or NULL if the request cannot be satisfied.
Variable Length Array #include<stdio.h> #include<stdlib.h> /* creates an array of size specified at run time*/ int main(void) { int n,i; float *p=NULL; printf(“Input the size of the array: n=“); scanf(“%d”,&n); if (n<0) { printf(“\nInvalid entry“); return 0; } printf(“\n”); p = malloc ( n * sizeof( float )); for( i = 0; i < n; i++ ) { printf(“Input array element %d:“, i); scanf(“%f”, p+i); } for( i = 0; i < n; i++ ) printf(“p[%d]=%6.2f\n”, i, *(p+i) ); free(p); return 0; }
Example • We want to write a function to add two n dimensional vectors. • Each vector is stored in a an n-size array. • The array names corresponding to the vectors to be added are passed to the function as arguments. • Where to store the sum vector? • Options: • Create an array to store the sum of vectors in the caller and pass its name to the function. • Create an array to store the sum of vectors in the function and return a pointer to it to the caller.
Adding Two Vectors #include <stdio.h> int* add( int v1[], int v2[], int n); int main(void) { int vec1[] = {1,2,3,4}; int vec2[] = {4,3,2,1}; int *p = NULL; p = add (vec1,vec2,4); … // some more code printf(“%d, %d, %d, %d\n”,p[0],p[1],p[2],p[3]); return 0;} Incorrect function add() int* add( int v1[], int v2[], int n){ int sum[n]; … // add vectors and store result in array sum return &sum[0]; }
Adding Two Vectors #include <stdio.h> int* add( int v1[], int v2[], int n); int main(void) { int vec1[] = {1,2,3,4}; int vec2[] = {4,3,2,1}; int *p = NULL; p = add (vec1,vec2,4); … // some more code printf(“%d, %d, %d, %d\n”,p[0],p[1],p[2],p[3]); return 0;} Incorrect function add() int* add( int v1[], int v2[], int n){ int sum[N]; //provided N is sufficiently large … // add vectors and store result in array sum return &sum[0]; }
Adding Two Vectors #include <stdio.h> int* add( int v1[], int v2[], int n); int main(void) { int vec1[] = {1,2,3,4}; int vec2[] = {4,3,2,1}; int *p = NULL; p = add (vec1,vec2,4); … // more work printf(“%d, %d, %d, %d\n”,p[0],p[1],p[2],p[3]); free(p); return 0; } Correct function add() int* add( int v1[], int v2[], int n){ int *q = malloc( n * sizeof(int) ); … /* q can be used as the name of an array of n ints; add vectors and store result in array q */ return q; }