430 likes | 809 Views
Pointers. Declaring, dereferencing, & assigning Call by value & reference Arrays (arithmetic, strings, pointers) Pointers to functions Command line arguments. Pointers. Pointers – variables that contain a memory address of another variable as its value What are they good for?
E N D
Pointers • Declaring, dereferencing, & assigning • Call by value & reference • Arrays (arithmetic, strings, pointers) • Pointers to functions • Command line arguments
Pointers • Pointers – variables that contain a memory address of another variable as its value • What are they good for? • Generic access to several similar variables • Call by reference function parameters • Dynamically allocated data structures • Creating copies of arrays and structures
Terminology • & operator – address operator • Returns the address of its operand • * operator – indirection or dereferencing operator • Returns the value of the object to which its operand points
Terminology • Declaring a variable int num = 5; • Declaring a pointer int *nptr = NULL;/*NULL is used to indicate that the pointer does not refer to a valid object*/ • Assigning an address to a pointer nptr = # • Dereferencing a pointer printf("%d",*nptr); /*output: 5*/
Assigning an Address /* See pointers.c */ int num = 5; int *nptr = NULL; /*defined in stdio.h*/ nptr = # /*assign num's address to nptr*/ printf("num=%d &num=0x%p \n",num,&num); /* num=5 &num=0x244ff44*/ printf("*nptr=%d nptr=0x%p \n",*nptr,nptr); /* *nptr=5 nptr=0x244ff44*/
Equivalent Code • The top statement is equivalent to the bottom 3 statements • int num = 10, *ptr = # • int num = 10; int *ptr = NULL; ptr = #
Dereferencing Pointers • With pointers, you can alter the value that the pointer is pointing to: /*See pointers2.c*/ intnum=10, *nptr=# ++*nptr; /*unary operators associate*/ (*nptr)++; /*right to left*/ printf(" num=%d &num=0x%p\n",num,&num); printf("*nptr=%d nptr=0x%p\n",*nptr,nptr); /* num=12 &num=0x244ff44 *nptr=12 nptr=0x244ff44 */
Dereferencing Pointers • Can also alter the actual pointer value: /*See pointers3.c*/ int x=3, y=6, z=9, *nptr=&y; printf(" x=%d &x=0x%p\n", x, &x); printf(" y=%d &y=0x%p\n", y, &y); printf(" x=%d &z=0x%p\n", z, &z); printf("*nptr=%d nptr=0x%p\n", *nptr, nptr); nptr++; //incremening by 4 bytes, because points to an int printf("*nptr=%d nptr=0x%p\n", *nptr, nptr); /* x=3 &x=0xffbffa4c y=6 &y=0xffbffa48 x=9 &z=0xffbffa44 *nptr=6 nptr=0xffbffa48 *nptr=3 nptr=0xffbffa4c */
Assigning Pointers to Pointers • Must be of the same type • If not, must use a cast operator • To make code flexible, can use type void • Generic pointer • Can be assigned to any pointer type • Any pointer type can be assigned to it • Cannot be dereferenced (must cast it first)
Dereferencing Pointers • The compiler must know the data type to know how to dereference a pointer • For integer pointers, the compiler knows that it refers to 4-bytes in memory • Since a void pointer can be any data type, the compiler cannot dereference it
Generic Pointer - void * /*See void.c*/ int num = 5; char ch = 'A'; int *iptr = # char *cptr = &ch; void *vptr = NULL; vptr = iptr; printf("*vptr=%d\n",*((int *)vptr)); /* *vptr=5 */ /* cast void to int pointer*/ vptr = cptr; printf("*vptr=%c\n",*((char *)vptr)); /* *vptr=A */ /* cast void to a char pointer*/
Call by Value • Changing the argument in the function does not change the original variable /*See swap.c*/ void swap1(int, int); . . . int x=3, y=7; swap1(x,y); printf("x=%d y=%d\n",x,y); . . . /*x=3 y=7*/ void swap1(int a, int b){ int temp = a; a=b; b=temp;}
Call by Reference • Changing the argument in the function does change the original variable /*See swap.c*/ void swap2(int *, int *); . . . int x=3, y=7; swap2(&x,&y); printf("x=%d y=%d\n",x,y); . . . /* x=7 y=3 */ void swap2(int *a, int *b){ int temp = *a; *a=*b; *b=temp;}
Call by Reference • Another version – arguments are pointers void swap2(int *, int *); . . . int x=3, *p1=&x, y=7, *p2=&y; swap2(p1,p2); printf("x=%d y=%d\n",x,y); . . . /* x=7 y=3 */ void swap2(int *a, int *b){ int temp = *a; *a=*b; *b=temp;}
Exercise 1: Draw the runtime stackfor each line of executable code /*See pointers-exercise1.c*/ 1 #include<stdio.h> 2 voidfunA(void *, int); 3 int main(){ 4 int a = 10, b = 20, c = 30; 5 int *ptr1 = &a, *ptr2 = &b, *ptr3 = &c; 6 ptr3 = ptr2; 7 b++; 8 (*ptr3)++; 9 c = *ptr3;10 funA(ptr1, *ptr2);11 printf("%d %d %d\n",a,b,c);12 return 0;13 }14 voidfunA(void *a, int b){15 int temp = *((int *)a);16 *((int *)a) = b;17 b = temp;18 } /* output: 22 22 22 */
Pointers & Arrays • In C, almost interchangeable • An array name can be thought of as a constant pointer • An array name is an address • Pointers can be used to do array subscripting • A pointer stores an address • This is called “pointer/offset notation”
Pointers & Arrays /*See array.c*/ int array[] = {10,20,30,40}; int *ptr = NULL; ptr = array; ptr = &array[0]; /*same as line above*/ //array subscript notation: printf("array[0] = %d\n",array[0]); //pointer/offset notation: printf("*(array+1)= %d\n",*(array+1)); printf("ptr[2] = %d\n",ptr[2]); printf("*(ptr+3) = %d\n",*(ptr+3)); /* array[0] = 10 *(array+1)= 20 ptr[2] = 30 *(ptr+3) = 40 */
Alternative Array Loop /*See array.c*/ int array[] = {10,20,30,40}; int *ptr = NULL; ptr = array; while(ptr!=(array+4)){ printf("%p = %d\n",ptr,*ptr); ptr++; } /* 0xffbffa58 = 10 0xffbffa5c = 20 0xffbffa60 = 30 0xffbffa64 = 40 */
How are arrays stored? /*See array.c*/ int in[]={10,20,30,40}; char ch[]={'a','b','c','d'}; int i=0; for(i=0;i<4;i++) printf("in[%d] = %p ch[%d] = %p\n",i,&in[i],i,&ch[i]); /* in[0] = 0xffbffa40 ch[0] = 0xffbffa38 in[1] = 0xffbffa44 ch[1] = 0xffbffa39 in[2] = 0xffbffa48 ch[2] = 0xffbffa3a in[3] = 0xffbffa4c ch[3] = 0xffbffa3b */
Pointer Arithmetic • Valid pointer operations • Assignment of pointers of the same type • Adding or subtracting a pointer & an integer • Comparing pointers of the same array • Assigning or comparing to zero • Invalid pointer operations • Add two pointers • Multiply, divide, shift, or and a pointer • Add float or double to a pointer • Assign pointers of different types without a cast
/*Pointer Arithmetic*/ /*See arithmetic.c*/ int i[] = {10,20,30,40}, *iptr=i; iptr=iptr + 3; /* 3*4=12 bytes */ printf("i=0x%p *i=%d\n",i,*i); printf("iptr=0x%p *iptr=%d\n",iptr,*iptr);/* i=0xffbffa58 *i=10 iptr=0xffbffa64 *iptr=40*/
/*Pointer Arithmetic*/ /*See arithmetic.c*/ double f[] = {1.1, 2.2, 3.3, 4.4}, *fptr=f; fptr=fptr + 3; /* 3*8=24 bytes */ printf("f=0x%p *f=%.1f\n",f,*f); printf("fptr=0x%p *fptr=%.1f\n",fptr,*fptr); /* f=0xffbffa30 *f=1.1 fptr=0xffbffa48 *fptr=4.4 */
Character Pointers • char array[] = "this is an array"; • Array of characters + '\0' • Can change the characters in the array • Cannot change the array’s address • char *ptr = "this is a pointer"; • Points to constant characters + '\0' • Cannot change the string constant • Can change the pointer value
Character Pointers /*See cptr.c*/ #include <stdio.h> int main(){ char a[] = "character array"; char *b = "constant array"; a=b; /*compiler error: incompatible types in assignment*/ a[0]='X'; b[0]='Z'; /*runtime error: Segmentation Fault*/ return 0; }
String Copy • Must be careful when copying strings • Should copy each character to new string • Will create two different strings • Should not simply assign a new address • Will have two pointers pointing to the same string
Bad String Copy /*See copy.c*/ #include <stdio.h> int main(){ char a[] = "array"; char *b = NULL; b=a; b[0]='Z'; printf("%s\n",a); /*Zrray*/ printf("%s\n",b); /*Zrray*/ return 0; }
Good String Copy /*See copy.c*/ #include <stdio.h> int main(){ char c[] = "array"; char d[6]; char *ptr1 = c, *ptr2 = d; while(*ptr1!='\0'){ *(ptr2++)=*(ptr1++); } *ptr2='\0'; //end of string d[0]='Z'; printf("%s\n",c); /*array*/ printf("%s\n",d); /*Zrray*/ }
Exercise 2: Draw the runtime stackfor each loop in the while-loop /*See pointers-exercise2.c*/ 1 #include<stdio.h> 2 void funX(char *); 3 int main(){ 4 char a[]="word"; 5 funX(a); 6 printf("%s\n", a); 7 return 0; 8 } 9 void funX(char *b){10 while((*b)!='\0' ){ 11 if((*b)>='a' && (*b)<='z'){12 (*b)= (*b) + 'A'-'a';13 }14 b++;15 }16 }/*A string is an array of character, ending in '\0', which is the "null character" */ /*Output: WORD*/
Arrays of Pointers • Arrays can contain pointers • Common use is to have an array of strings /*See aptr.c*/ char *array[]={"zero","one","two"}; /*adds null character ('\0') to end*/ printf("%s\n",array[1]); /*one*/ printf("%s\n",*(array+1)); /*one*/ printf("%c\n",array[2][1]); /*w*/ printf("%c\n",*(*(array+2)+1)); /*w*/ if('\0'==array[0][4]) printf("true\n"); /*true*/
Command-line Arguments int main(intargc, char *argv[]) • argc– “argument count” is number of command-line arguments the program was invoked with • argv– “argument vector” is a pointer to an array of character strings that contain the arguments • Can call these anything int main(int foo, char *bar[]) • Can also write *argv[] as **argv • a pointer to a pointer to a character
Command-line Arguments /*Will repeat 1st line of text on commandline*/ #include <stdio.h> int main(int argc, char *argv[]){ int i=0; for(i=0;i<argc;i++){ printf("argv[%d] = %s\n", i, argv[i]); } return 0; } /*See command.c*/
Program on UNIX • uhunix2: ./a.out one two three • argv[0] = ./a.out • argv[1] = one • argv[2] = two • argv[3] = three • uhunix2:./a.out one <electricity.txt two • argv[0] = ./a.out • argv[1] = one • argv[2] = two • uhunix2:./a.out * • argv[0] = ./a.out • argv[1] = (will print out the names of all the files in the directory, as * is used as a wildcard) • etc…
Pointers to Functions • A pointer can also store the address of a function • A function name is the address in memory of the start of the function • Function pointers can be • Passed to a function • Returned to functions • Stored in arrays • Assigned to other function pointers
An Array of Pointers to Functions /*See fptr.c*/ #include <stdio.h> void fun1(void); void fun2(void); void fun3(void); int main(){ void (*array[3])(void) = {fun1,fun2,fun3}; /*declare an array of pointers to functions*/ int i; for (i=0;i<3;i++) array[i]();/*make a function call*/ //(*array[i])(); /*alternative syntax*/ return 0; /*output: 1st 2nd 3rd */ } void fun1(void){printf("1st ");} void fun2(void){printf("2nd ");} void fun3(void){printf("3rd ");}
Array of Pointers to Functions void(*array[3])(void)={fun1,fun2,fun3}; • “array” is an array of 3 pointers to 3 functions with no arguments and return type void array[i](); (*array[i])(void); //alternative code • The function is dereferenced and no arguments are passed to it
An Array of Pointers to Functions II /*See fptr2.c*/ #include <stdio.h> int fun1(int); int fun2(int); int fun3(int); int main(){ int (*array[3])(int) = {fun1,fun2,fun3}; int i; for (i=0;i<3;i++) printf("%d ",(*array[i])(i)); /*1 3 5*/ /* printf("%d ", array[i](i)); */ return 0; } int fun1(intx){return x+1;} int fun2(intx){return x+2;} int fun3(intx){return x+3;}
Array of Pointers to Functions II int(*array[3])(int)={fun1,fun2,fun3}; • “array” is an array of 3 pointers to 3 functions with one int argument and return type int (*array[i])(i); array[i](i); • The function is dereferenced and integer “i” is passed as an argument to the function
Textbook Examples • Pointers to functions • Arrays of pointers are often used in menu-driven system • See p. 289, fig07_28.c
Passing Function Pointers to Functions /*See fptr3.c*/ #include <stdio.h> void fun1(int (*)(), float (*)()); int fun2(); float fun3(); int main(){ fun1(fun2,fun3); /*f1=12 f2=1.234000*/ return 0; } void fun1(int (*f1)(),float (*f2)()){ printf("f1=%d f2=%f", f1(), f2()); } int fun2(){return 12;} float fun3(){return 1.234;}
Passing Function Pointers void fun1(int (*)(), float (*)()); • “fun1” is a function of return type void, that takes two arguments –a function pointer that returns an integer with no arguments,and a function pointer that returns a float with no arguments
Passing Function Pointers fun1(fun2,fun3); • “fun1” is dereferenced with pointers to fun2 &fun3as arguments void fun1(int (*f1)(),float (*f2)()) • f1& f2 hold the addresses of fun2 & fun3
Textbook Examples • Pointers to functions • Ascending or descending bubblesort • See p. 286, fig07_26.c