第 十 章 指 针
§10.1 指针的概念 内存区域被划分成一个一个的内存单元,一个内存单元可 一、内存单元的内容和内存单元的地址: 以存放一个字节的数据,每一个内存单元都有一个编号,统称 为地址。 例:int i=3; char j=‘s’; 地址 内存单元 3 2000 2001 2002 2004 3000 变量i 变量j 编译时为变量i分配2000和2001两 个字节的内存单元,为j分配2003 一个字节的内存单元 2003 75 因为内存单元的地址就是“指向”了该内存单元,故通常把内存单元的地址称为指针。
二.内存单元的访问: j 直接访问:通过变量名访问。 char j=‘s’; printf(“%c”, j ); 间接访问:通过该变量的指针来访问。 p=&j; 称:指针变量p指向变量j,p是变量j的指针。 j 2000 75 p j 2000 75
变量的指针:是一个变量的地址, 是常量。 指针变量:是存放变量地址的变量,其 值是该变量的地址。 要引用指针,必须定义指针。 定义指针,是为了通过指针访问内存单元。 一个数组或函数占有一段连续内存单元,假如将一个数组或函数的首地址赋给指针变量,通过指针就可以找到该数组或函数。
一、指针变量的定义 §10.2 变量的指针 类型标识符 * 标识符 例如:int *p “标识符”是指针变量名。 如 p §10.2 变量的指针 一、指针变量的定义 类型标识符 * 标识符 例如:int *p “标识符”是指针变量名。 如 p “*”表示定义指针变量 “类型标识符”表示该指针变量所指向的变量类型。 指针指向一个变量的地址,用*表示指向。
二、指针变量的引用 &: 取地址运算符 *: 取内容运算符 *和 &的优先级相同。 例: int a=5,b; int *p; p=&a; 指针变量只能存放地址。 未经赋值的指针变量不能用。 C语言提供两种相关地址运算符: &: 取地址运算符 *: 取内容运算符 *和 &的优先级相同。 例: int a=5,b; int *p; p=&a; b=*p; p a , b 2000 5 2002 ... ... 3000 2000 &a 类型说明中的*表示 定义指针变量 &a 5 p a 表达式中的*是运算符 *p表示p指向地址里的值
三、指针变量的赋值: (1)、int a,*pa; pa=&a; (2)、int a=5,*p=&a; (3)、int a,*pa=&a,*pb; pb=pa; (4)、int a[5],*pa; pa=a; (5)、char *pc; pc=“c language”; /*整型变量a的地址赋给指针变量pa*/ /*指针变量p取得整型变量a的地址.*/ /*指针pa的值赋给指针pb*/ /*将数组a的首地址赋给相同类型的指针pa*/ /*将字符串的首地址赋给指针变量*/
int *pointer_1, *pointer_2; /* 定义指针变量 */ a = 100; b = 10; [例10.1] main () { int a,b; int *pointer_1, *pointer_2; /* 定义指针变量 */ a = 100; b = 10; pointer_1 = &a; pointer_2 = &b; printf("%d,%d\n", a,b ); printf("%d,%d\n", *pointer_1,*pointer_2 ); } 程序运行结果: pointer-1 a &a 100 100,10 b pointer-2 &b 10
[例10.2] 输入a和b两个整数,按先大后小的顺序输出a和b。 main () { int *p1, *p2, *p , a, b; scanf("%d,%d",&a,&b); p1 = &a; p2 = &b; if (a < b) { p = p1; p1 = p2; p2 = p; } printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n", *p1, *p2 ); } 运行:5,9 p1 a p &a 5 &b p2 b &b 9 &a a=5,b=9; max=9,min=5 该例不交换变量a、b的值,而是交换指针p1、p2的值。
四、指针变量作为函数的参数 if (a<b) swap(pointer_1, pointer_2) ; [例10.3] 题目要求同[例10.2],输入a和b两个整数,按先大后小的顺序输出a和b。 swap( int* p1,int *p2 ) /*交换指针p1、p2所指向的变量的值 */ { int p; p = *p1; *p1 = *p2; *p2 = p; } main () { int a, b; int *pointer_1, *pointer_2; scanf("%d,%d",&a,&b); pointer_1 = &a; pointer_2 = &b; if (a<b) swap(pointer_1, pointer_2) ; printf("\n%d,%d\n",a,b); 把变量a和b的地址传送给形参
§10.3 数组的指针 指针可以指向数组和数组元素,对数组元素的访问: 既可以使用数组下标,也可以使用指针。 一、指向数组元素的指针变量 指向数组元素的指针变量,其类型应与数组元素相 同。例1: int a[7]; int *p; p=&a[0]; 或 p=a; 例2: float b[10],*p=b ; 或 float b[10],p=&b[0]; 1 2 3 4 5 6 7 &a[0] p a[0] a[6] /*指的是把数组a的首地址赋给指针p*/
如果数组元素是整型,p+1表示p的地址加2; 如果数组元素是实型,p+1表示p的地址加4; 二、通过指针引用数组元素 设:int a[10],*p=&a[0]; (1).p+1和a+1都是指向下一个元素a[1]; 如果数组元素是整型,p+1表示p的地址加2; 如果数组元素是实型,p+1表示p的地址加4; a表示数组首地址。 p+i 和a+i 指向元素a[i]。 (2).*(p+i) 访问元素a[i]; 例: *(p+5)和*(a+5) 与a[5]等效; (3).指向数组指针可以带下标: 例: p[i]与*(p+i)等效.
[例10.5] 输出数组的全部元素。(设10个整数) 1、下标法 main () { int a[10]; int i; for(i=0;i<10;i++) scanf("%d", &a[i]); printf("\n"); printf("%d ", a[i] ); } 特点:常用、过程简单直观
2、用数组名计算数组元素的地址。 main( ) { int a[10]; int i; for(i=0;i<10;i++) scanf("%d", &a[i]); printf("\n"); printf("%d ", *(a+i) ); } 特点:不移动指针 但要计算每个元素的地址 费时间
for( p=a ; p<(a+10) ; p++ ) printf("%d ", *p ); } 3、用指针访问各元素。 main () { int a[10]; int *p , i; for(i=0;i<10;i++) scanf("%d", &a[i]); printf("\n"); for( p=a ; p<(a+10) ; p++ ) /* p++使p指向下一个元素 */ printf("%d ", *p ); } 特点:移动指针 速度快
??试问:以上程序可以输出a数组中的 例:通过以下程序输出a数组中的10个元素。 main() { int a[10],*p,i; p=a; for(i=0;i<10;i++) scanf("%d",p++); printf("\n"); for(i=0;i<10;i++,p++) printf("%d\t",*p); } ??试问:以上程序可以输出a数组中的 10个元素吗?
三、数组名作函数参数 1、形参和实参都用数组名 main() { int array[10]; … f( array ,10); } f( int arr[],int n) { … }
2、实参用数组名,形参用指针变量。 main() { int array[10]; … f(array ,10); } f( int *arr,int n) { … }
3、实参、形参都用指针变量。 main() f( int *arr,int n) { { … p=array; … } f(p,10); } int array[10],*p; p=array; … f(p,10); } f( int *arr,int n) { … }
4、实参用指针变量,形参用数组名。 main() f( int arr[ ],int n) { { … p=array; … } int array[10],*p; p=array; … f(p,10); } f( int arr[ ],int n) { … }
§10.4 字符串的指针 一、字符串的表现形式 C语言中,有两种方式可以实现字符串: 字符数组、字符指针。 [例10.16]:字符数组 main() { char string[ ] ={“How are you!”}; printf("%s\n",string); } 用字符串对字符数组赋值 string是数组名,代表字符数组的首地址。 数组可以用下标访问,也可以用指针访问
printf("%10s%10s\n",name1,name2); } [例10.17]字符指针 main() { char *name1 =”Mary"; char *name2=“Hare”; printf("%10s%10s\n",name1,name2); } name1、name2是指针变量 name1 name2 Mary Hare char *name1 =“Mary”; 等价于: char * name1; name1 = “Mary”; 它把字符串常量的首地址赋给指针name1.
特点:不移动指针 [例10.18] 将字符串a复制到字符串b。 main () { char a[ ] = "I am a boy."; char b[20]; int i; for (i=0; *(a+i) !='\0'; i++) *(b+i) = *(a+i); *(b+i) = '\0'; printf("string a is: %s\n",a); printf("string b is:"); for (i=0; b[i] !='\0'; i++) printf("%c",b[i]); printf("\n"); } 用数组名计算数组元素的地址 I a m 数组a b o y . \0 数组b b 特点:不移动指针
特点:移动指针 [例10.19]:用指针处理字串复制. 数组a 数组b main () p1 p2 o y . \0 数组b p2 main () { char a[ ] = "I am a boy.",b[20],*p1,*p2; int i; p1=a;p2=b; for ( ; *p1 !='\0'; p1++,p2++) *p2 = *p1; *p2 = '\0'; printf("string a is: %s\n",a); printf("string b is:"); for (i=0; b[i] !='\0'; i++) printf("%c",b[i]); printf("\n"); } 特点:移动指针
(a+7)是用指针指向数组元素a[i]的第7个字符 例10.21: main() { char *a=“I love china!”; a=a+7; printf(“%s”,a); } 运行结果如下: China! (a+7)是用指针指向数组元素a[i]的第7个字符
可以看到: 1、字符数组和字符指针的概念不同。 2、字符指针指向字符串,而C语言中, 字符串按数组方式处理,因此,字符指 针和字符数组的访问方式相同。
§10.5 函数指针 一、用函数指针变量调用函数 函数指针定义的一般形式: 函数返回值类型(*指针变量名)( ) §10.5 函数指针 一、用函数指针变量调用函数 函数指针定义的一般形式: 函数返回值类型(*指针变量名)( ) 例: int (*p)( ) 说明:(*p)( ) 表示定义一个指向函数的指针变量, 它不固定指向哪一个函数,它是专门用来存放函数的入口 地址的,该函数返回一个整型值。 C规定:函数的名称就就代表函数的入口地址。
int max(int x, int y); /*原型*/ {int z; if (x>y) z = x; else z = y; [例9.23] 求a和b中的大者。 用函数名调用子函数max() int max(int x, int y); /*原型*/ {int z; if (x>y) z = x; else z = y; return z; } main () {int a,b,c; scanf("%d,%d", &a, &b); c = max(a, b); printf("a=%d,b=%d,max=%d",a,b,c);
用函数指针变量调用函数时,只须将(*p)代替函数名max,并加上实参 [例9.23] 求a和b中的大者。 用函数指针调用函数max() int max(int x, int y) /*原型*/ {int z; if (x>y) z = x; else z = y; return z;} main () { int (*p) (int, int); /*定义函数指针p */ int a,b,c; p = max; /*函数max的入口地址赋给函数指针p*/ scanf("%d,%d", &a, &b); c = (*p)(a,b); /*用函数指针p调用函数 */ printf("a=%d,b=%d,max=%d",a,b,c); } 用函数指针变量调用函数时,只须将(*p)代替函数名max,并加上实参
§10.6 返回指针的函数 (自学) 一般形式: 类型标识符 * 函数名(参数表) 例、 int * a (int x, int y) 类型标识符 * 函数名(参数表) 例、 int * a (int x, int y) 声明一个函数,函数名为a,其返回值类型是“指向整型 的指针”, 函数形式参数为int x 和 int y。 注意:在a的两侧分别为*运算符和( )运算符,a先与( ) 结合,这是函数形式;函数前有个*号,表示该函数为指针 型函数.
§10.7 指针数组和指向指针的指针 (自学) 一、指针数组 概念:指针数组是一个数组,该数组中的 每一个元素均是指针变量。 形式:类型标识符 *数组名[数组元素个数] 例: int * p[4]; 定义一个指针数组,数组名p,有4个元素, 每一个元 素是指向整型变量的指针。如: char *name[3]={”math”,”computer”,”ccm”}; 可用 puts(name[i]) 语句输出。
p 二、指向指针的指针 定义举例: char * * p; char *name[]={“Ann”,“Hare”,“Jack”}; p=name; p是一个指向指针的指针。 被p指向的指针指向字符变量。 name指针数组 二维字符数组 p name[0] name[1] name[2] Ann Hare Jack
程序举例: main() { int i; char *name[]={"Ann","Hare","Jack","Mary"}; char **p; printf("\n Students's names are:\n"); for(i=0;i<4;i++) { p=name+i; printf("%6s\t",*p); } 运行结果: Ann Hare Jack Mary name指针数组 二维字符数组 p name[0] name[1] name[2] Ann Hare Jack name[3] mary
§10.8 指针使用小结 一、有关指针的数据类型 定义: int i; 定义整型变量i int *p; p是指向整型数据的指针变量 §10.8 指针使用小结 一、有关指针的数据类型 定义: int i; 定义整型变量i int *p; p是指向整型数据的指针变量 int a[n]; 定义数组a,元素类型为int,元素个数是n int *p[n]; p是指针数组,包含n个指针,每一个指针可 以指向整型数据 int f(); f是函数,返回值是int int (*p)(); p是函数指针,所指向的函数返回整型数据 int *p(); p是函数,返回值是指针,该指针指向整型数据 int **p; p是指针,指向一个指向整型数据的指针
二、指针运算小结 1、指针变量加/减运算 2、指针变量赋值 p++、p--、p+i、p-i、p+=i、p-=i 加1表示指向下一个数据。 加1表示指向下一个数据。 2、指针变量赋值 p = &a; 变量a的地址赋给p,即指针p指向a p = array; 数组array首地址赋给p p = &array[i]; 数组元素array[i]的地址赋给p p = max; 函数max的入口地址赋给p p1 = p2; 指针p2的值赋给指针p1, 即p1、p2所指的数据相同 p=NULL; 指针变量可以由空值,NULL的值为0
3、空指针 p = NULL 空指针p=NULL表示p不指向任何数据。 在stdio.h中,NULL被定义为0: #define NULL 0 习惯上,不使用 p = 0而使用 p = NULL 指针变量p可以与NULL作比较, 例: if (p = = NULL) ... 注意:空指针不指向任何数据,与p未赋值不同。 当p未赋值时,其值是不确定的,而空指针 的值是确定数0。
4、指针变量相减 当p1、p2指向同一个数组的元素,指针相减p2-p1等于p1、p2间的元素个数。 注意:指针相加无意义。 5、两个指针的比较 当p1、p2指向同一个数组的元素时可以比较,如:p1 > p2。表示p1在p2之后。若p1、p2不是指向 同一个数组的元素,比较无意义。
三、空类型指针 void 例、 char *p1; void *p2; p1 = (char *)p2; p2 = (void *)p1; 空类型指针与其他类型指针之间赋值时,应进行强制类型转换, 例、 char *p1; void *p2; p1 = (char *)p2; p2 = (void *)p1;
第10 章 课堂练习 10.1 指针变量a所指的字符串长度为 , 这个长度是可以用strlen(a)测出来的。 第10 章 课堂练习 10.1 指针变量a所指的字符串长度为 , 这个长度是可以用strlen(a)测出来的。 char *a=“\nMY Name is\”zhang li\”.\n”; (1)26 (2) 27 (3) 28 (4) 24 (5) 23 10.2 下面程序的作用是: 将两个变量中的值互换, 请检查程序是否正确,如不正确的,改正之。 main ( ) { int a=3,b=4; int *p1,*p2,*p; p1=&a;p2=&b; p=p1;p1=p2;p2=p; printf(“a= %d,b= %d\n”,a,b); }
第10 章 课堂练习 10.3已知p1和p2为指针变量, 且已指向同一个整 型数组中的元素,a是一个整型变量,问下面 第10 章 课堂练习 10.3已知p1和p2为指针变量, 且已指向同一个整 型数组中的元素,a是一个整型变量,问下面 哪一个语句不能正确执行? (1) a=*p1 (2) a=*p1+*p2 (3) a=*p1-*p2 (4) p1=a-p2 10.4 有一个二维数组a[3][4],第2行第3列元素的正 确表示方法为 。 (1) &a[2][3] (2)a[2]+3 (3) *(a+2)+3 (4)*(a[2]+3) 10.5 指向一个包含4个整型元素的一维数组的指针 变量的定义形式为: (1) int (*p)[4] (2) int *p[4] (3) int * (p[4]) (4) int (*p)[ ]
第10 章 课堂练习 10.6 若有语句:int a=4,*p=&a; 下面均代表地址的一组选项的是: 第10 章 课堂练习 10.6 若有语句:int a=4,*p=&a; 下面均代表地址的一组选项的是: 1) a,p,&*a 2) *&a,&a,*p 3) &a,p,&*p 4) *&p,*p,&a 10.7 以下程序段的输出结果为: char a[]=“Program”,*ptr; ptr =a; for ( ;ptr<a+7;ptr+=2) putchar(*ptr); 1) Program 2) Porm 3) 有语法错误; 4) Por