第8章 指针 21世纪高等学校精品规划教材
理解并掌握地址、指针和指针变量的概念 练掌握指针变量的定义、初始化和引用方法 理解并掌握指针与数组的关系 了解指针数组和多级指针的概念 了解指针与函数的关系 学会在程序设计中正确应用指针解决实际问题
引言 指针是C语言区别于其他程序设计语言的主要特征之一。
8.1 指针的概念 C语言允许使用变量名、数组名[下标]、函数名等标识符来访问内存 8.1 指针的概念 C语言允许使用变量名、数组名[下标]、函数名等标识符来访问内存 指针其实就是在内存中的地址,它可能是变量的地址,也可能是函数的入口地址 变量指针存储的地址是变量的地址 函数指针存储的地址是函数的入口地址 指针变量也简称为指针,是指它是一个变量,且该变量是指针类型的
8.2 指针变量的定义和初始化 定义指针变量的形式如下: 定义并初始化的形式为: 例如: 或者: 8.2 指针变量的定义和初始化 定义指针变量的形式如下: 数据类型 *指针变量名; 定义并初始化的形式为: 数据类型 *指针变量名=&变量名; 例如: int a; int *p=&a; 或者: int a,*p=&a; 没有指向的指针变量的值是随机的,称为“野指针”。
指针变量与 变量的关系 int a,*pa=&a;
8.3 指针运算 8.3.1 * 运算符和取地址运算符& int a=1000, *pa=&a; *(&a) == a &(*p) == p 8.3 指针运算 8.3.1 * 运算符和取地址运算符& int a=1000, *pa=&a; *(&a) == a &(*p) == p 指向运算符 * 和取地址运算符 & 互逆 不能写成: *(& p)
8.3.2 指针变量的引用 【例8-1】演示指针变量的引用 8.3.2 指针变量的引用 【例8-1】演示指针变量的引用 void swap1(int x , int y){int temp;temp = x;x = y;y = temp;} void swap2(int *x , int *y){int temp;temp = *x;*x = *y;*y = temp;} void swap3(int *x , int *y){int *temp;temp = x;x = y;y = temp;} void main() {int a,b; int *pa,*pb; pa=&a; pb=&b; a=10,b=20;swap1(a,b); printf("a=%d,b=%d,*pa=%d,*pb=%d\n",a,b,*pa,*pb); a=10,b=20;swap2(pa,pb); a=10,b=20;swap3(pa,pb); }
【例8-1】演示指针变量的引用。 swap1(int x,int y),主函数调用方式为: swap1(a,b); 值传递,a、b的值以及pa、pb指针变量都不受影响。 swap2(int *x,int *y),主函数调用方式为: swap2(pa,pb); 形参是指针变量,实参也是指针变量。交换算法中采用指向运算符*,所以*x、*y和pa、pb是对应相同的数据a、b,最后函数实现了交换。 swap3(int *x,int *y),主函数调用方式为: swap3(pa,pb); 形参是指针变量,实参也是指针变量。交换算法中临时指针变量虽然把x、y交换,但pa、pb没有交换,所以对应的数据a、b也没有受到影响,交换是失败的。
【例8-1】演示指针变量的引用。 swap3()分析图
8.3.3 指针的算术运算和关系运算 算术运算 指针变量的++和--运算。 指针加、减整数运算。 指向同一数组不同元素的指针相减运算。 8.3.3 指针的算术运算和关系运算 算术运算 指针变量的++和--运算。 指针加、减整数运算。 指向同一数组不同元素的指针相减运算。 假定有: char str[100]= "Hello World"; char *p=str,*q;
8.3.3 指针的算术运算和关系运算 p++ q=p+3; 字符W可以用str[6]表示,也可以用p[5]、q[2]表示
8.3.3 指针的算术运算和关系运算 指针不能进行如下等运算: 关系运算 关系运算是比较指针大小的运算。两个指针相等说明指向同一存储单元。 8.3.3 指针的算术运算和关系运算 关系运算 关系运算是比较指针大小的运算。两个指针相等说明指向同一存储单元。 p<q 指针不能进行如下等运算: p+q p*q p/q ……
8.4 指针与数组 8.4.1 指针与字符数组 char str[100]= "Hello World"; char *p=str; 8.4 指针与数组 8.4.1 指针与字符数组 char str[100]= "Hello World"; char *p=str; 字符w可以有如下多种表示形式: str[6] *(str+6) p[6] *(p+6)
【例8-2】演示指针和数组的关系。 #include <stdio.h> void main() { char str[100]="123456789"; char *p=str;char des[100],*q; while(*p != NULL) printf("%c",*p++); /*顺序输出*/ printf("\n"); while(--p >= str) printf("%c",*p); /*逆序输出*/ p=str; q=des; while(*p != NULL) *q++ = *p++; /*字符串拷贝*/ *q=NULL; printf("%s\n",des); return 0; }
8.4.2 指针与其他类型数组 【例8-3】演示指针和整型数组的关系,数组动态分配内存。 #include <stdio.h> 8.4.2 指针与其他类型数组 【例8-3】演示指针和整型数组的关系,数组动态分配内存。 #include <stdio.h> void main() { int a[10] = {1,2,3,4,5,6,7,8,9,10}; int *p=a,*q=p+9; int s; for(s = 0 ; q >= p ; q--) s = s + *q; printf("s=%d\n",s); }
8.4.3 指针与二维数组 二维数组其实可以看成由一维数组构造而成。就相当于几个队列构成一个方阵,方阵由队列组成,队列由具体的元素--人组成。 一级指针只能管理队列,如果管理方阵,则需要二级指针。 int a=1000,*pa,**ppa; pa=&a; ppa=&pa; *(*(ppa)) ≡ *(pa) ≡ a ≡ 1000
8.4.3 指针与二维数组 二级指针演示图
【例8-4】演示指针和二维数组的关系 main() { int s,t; int a[3][4]; int *p[3],*q; int i,j; s=t=0; for(i =0 ; i < 3 ; i++) { p[i] = a[i]; for(j = 0 ; j < 4 ; j++) a[i][j] = i * 3 + j; } for(i=0 ; i<3 ; i++) { q = p[i]; for(j=0 ; j<4 ; j++) {s = s + *(*(p+i)+j) ; t = t + *(q+j);} printf("s=%d,t=%d\n",s,t);
【分析】 int *p[3]是指针数组。 所谓指针数组,首先是一个数组,只不过其元素不是普通的变量,而是指针变量。即p[0]、p[1]、p[2]相当于前面提到的指针变量。 单独的指针变量可以指向一个一维数组,例如例题中的数组a的第一行
【分析】 p是指针数组的数组名 a[1][2]用p表示就是 *(*(p+1)+2),其实就是*(p[1]+2)、*(a[1]+2)
【总结】 p是二级指针 *p相当于*(p+0),级别降低为一级指针,相当于p[0]
8.5 指针与函数 8.5.1 指针作为函数的参数 实际调用该函数时,如: int swap2(int *x, int * y) 8.5 指针与函数 8.5.1 指针作为函数的参数 int swap2(int *x, int * y) { int temp; temp = *x; *x = *y; *y = temp; return 0; } 实际调用该函数时,如: swap2(&a,&b); 调用时,把实参的指针传送给形参,即传送&a、&b,这是函数参数的引用传递。但是,作为指针本身,仍然是函数参数的值传递方式。因为在swap函数中创建的临时指针在函数返回时被释放,它不能影响调用函数中的实参指针(即地址)值
8.5 指针与函数 8.5.2 函数指针 函数名代表了函数在内存中的入口地址 8.5 指针与函数 8.5.2 函数指针 函数名代表了函数在内存中的入口地址 int (*Copy)(const char *, const char*); Copy = &strcpy; /* Copy 指向strcpy函数 */ &运算符可以省略: Copy = strcpy; / *Copy 指向strcpy函数 */ 下面的3个调用是等价的: strcpy (des,str); /* 直接调用*/ (*Copy) ( des,str); /* 间接调用*/ Copy (des,str); /* 间接调用*/
【例8-5】演示函数指针。 #include <stdio.h> #include <math.h> int sum(int n) { int i,s=0; for(i=1; i<=n; i++) s = s + i; return s; } main() { double (*s)(double) = &sin; double PI=3.1415926; int (*f)(int); int n=100; printf("sin(PI/2)=%f\n",s(PI/2)); f=sum; printf("1+2+3+...+100=%d\n",f(n)); } 。
【例8-6】演示函数指针2。 #include <stdio.h> #include <math.h> int f1(int a,int b) { return a+b;} int f2(int a,int b) { return a-b;} int f3(int a,int b) { return a*b;} int f4(int a,int b) { if(b!=0) return a/b; else {printf("error\n");return 0; } }
【例8-6】演示函数指针2。 int f5(int a,int b) { if(b!=0) return a%b; else {printf("error\n");return 0;} } int f6(int n) { int i,s=0; for(i=1 ; i<=n ; i++)s = s + i; return s; int f7(int n) { int i,s=1; for(i=1 ; i<=n ; i++)s = s * i;
【例8-6】演示函数指针2。 int f8(int a,int b,int c) { return a+b+c;} void main() int (*f)(); int a,b,c; a=53; b=44; c=35; f=f1; printf("a+b=%d\n",f(a,b)); f=f2; printf("a-b=%d\n",f(a,b)); f=f3; printf("a*b=%d\n",f(a,b)); f=f4; printf("a/b=%d\n",f(a,b)); f=f5; printf("a%%b=%d\n",f(a,b)); f=f6; printf("1+2+3+...+100=%d\n",f(100)); f=f7; printf("1*2*3*...*8=%d\n",f(8)); f=f8; printf("a+b+c=%d\n",f(a,b,c)); }
8.5.3 返回指针的函数 函数的返回值可以是一个指针。需要返回指针的函数,其类型必须也是指针类型。例如: 8.5.3 返回指针的函数 函数的返回值可以是一个指针。需要返回指针的函数,其类型必须也是指针类型。例如: char * copy(char * s,char * t) { … return s; } 函数名copy和其返回值s的类型都是 char *。 注意copy是函数名,是一个指针常量,如果定义成: char (*copy)(…);加上括号的copy 是指针变量。 定义成指针变量的形式没有函数体部分,变量是简单的实体,不能再包括其他代码。
【例8-7】设计一个类似于strcpy的函数 #include <stdio.h> char* copy(char* s,char* t) { char *p=s,*q=t; while(*p != '\0') p++; while(*q != '\0') p++ = q++; *p ='\0'; return s; } main() { char s[100]="Hello "; char t[]="World!"; printf("%s\n",copy(s,t));
8.6 程序示例 【例8-8】编写一个查找字符位置的函数。 int atc(char *string,char c) 8.6 程序示例 【例8-8】编写一个查找字符位置的函数。 int atc(char *string,char c) { int pos=0; while(*string != c && *string != NULL) { pos++; string++;} if(*string == NULL) return 0; else return pos+1; } void main() { char str[]="Hello World!"; int pos; pos = atc(str,'o'); if(pos != 0) printf("o's position is:%d\n",pos); else printf("not found the char o\n"); pos = atc(str,'k'); pos!=0 ? printf("k's position is:%d\n",pos) : printf("not found the char k\n"); }
【例8-9】用指针方法统计字符串"I love music more than games "中单词的个数。 规定单词由字母组成,单词之间由空格分隔,字符串开始和结尾没有空格。 #include <stdio.h> void main() { char string[]="I love music more than games"; char *p=string; int n=0; while(*p != NULL) { if(*p == ' ') {n++; ++p;while(*p++ ==' '); } else p++; } n=n+1; printf("n=%d\n",n);
【例8-10】编写一个函数用来查找一个字符串在另外一个字符串中的位置,注意有可能出现多次,要求能够查找指定次数出现的位置。 #include <stdio.h> int at(char *,char *); int atn(char *,char *,int); void main() { int pos; char source[101],subs[21]; printf("Input a string:"); gets(source); printf("Input a substring:"); gets(subs); pos = at(subs,source); if(pos) printf("Found,The first posistion is :%d\n" ,pos + 1); else printf("Not found\n"); pos = atn(subs,source,2); if(pos) printf("Found,The second position is :%d\n" ,pos + 1); }
【例8-10】编写一个函数用来查找一个字符串在另外一个字符串中的位置,注意有可能出现多次,要求能够查找指定次数出现的位置。 int at(char *subs,char *source) { char *p1,*p2; p1 = source; p2 = subs; while(*p1 != '\0' && *p2 != '\0') { if(*p1 == *p2) { p2++; if(*p2 == '\0') return (int)(p1-source-(p2-subs)+1); } else p2=subs; p1++; return 0;
【例8-10】编写一个函数用来查找一个字符串在另外一个字符串中的位置,注意有可能出现多次,要求能够查找指定次数出现的位置。 int atn(char *subs,char *source,int times) { char *p1,*p2; p1 = source; p2 = subs; while(*p1 != '\0' && *p2 != '\0') { if(*p1 == *p2) { p2++; if(*p2 == '\0') { if(times == 1) return (int)(p1-source - (p2-subs)+1); else { p2=subs; times --;} } else p2=subs; p1++; return 0;
【分析】 程序中定位函数at用于查找第1次出现的位置,atn函数用于查到第n次出现的位置,指定第几次查找的函数在查找到目标串后需要考虑次数问题。atn函数可以替代at函数,替代的形式为: atn(subs,source,1); 查找算法的关键在于如何认定查找到的状态,确认找到了目标串时,正好是指针p2指向目标串的结束符,这一点非常重要,在这个状态下,指针p1指向找到位置的串的最后一个字符,所以在计算串的位置时需要减去串的长度,考虑到计数是从1开始,所以返回的位置表达式为: (int)(p1-source - (p2-subs)+1)
【例8-11】编写一个函数用来清理一个字符串中的空格,将多个连续的空格合并为一个空格 例如下面的字符串: I like this games. 清理后变成: I like this games. 程序如下: #include <stdio.h> #include <string.h> char *DeleteOtherSpace(char *); void main() { char s[100]; printf("Input a string:"); gets(s); printf("%s\n",DeleteOtherSpace(s)); }
【例8-11】编写一个函数用来清理一个字符串中的空格,将多个连续的空格合并为一个空格 char *DeleteOtherSpace(char *s) { char *p,*q; int IfFirstSpace = 1; p=q=s; while(*p != '\0') { if(*p != '□') {*q++ = *p++;IfFirstSpace = 1; /*遇到非空格字符*/ } else if( IfFirstSpace ) /*遇到第1个空格字符*/ {*q++=*p++; IfFirstSpace = 0} else p++; /*遇到第2个以上的空格字符*/ *q = '\0'; return s;
本章介绍了指针的概念以及指针变量的定义和初始化等。C语言的指针变量形式有: 一级指针变量:int *p,p可指向变量、数组元素。 二级指针变量:int **pp,pp可指向一级指针变量。 指向一维数组的指针变量:int (*p)[n],可用于二维数组的行指针变量。 指针数组:int *p[n],元素是一级指针变量。 指向函数的指针变量:int (*p)(),p可指向一个函数。 返回指针的函数:int *f(){…},f函数返回一个一级指针。
指针的运算包括变量的取地址运算“&”和指针的指向运算“. ”。“&”和“ 指针的运算包括变量的取地址运算“&”和指针的指向运算“*”。“&”和“*”是一对互逆的运算符。除此以外,指针变量还可以进行受限制的算术运算、赋值运算和关系运算。 指针可以指向常变量、数组、函数。特别是指针作为函数的参数时,函数的参数传递方式变成地址传递,相对于值传递和引用传递有质的不同。 指针具有很大的灵活性和风险性,同时也是C语言功能强大的基础条件之一,希望读者认真学习。
写一个函数,求一个字符串的长度 输入一个字符串,将其逆序输出。 输入一行字符,将其中的每个字符从小到大排列后输出。 从字符串中删除子字符串。从键盘输入一字符串,然后输入要删除的子字符串,最后输出删除子串后的新字符串。