1.12k likes | 1.29k Views
全国 C 等级考试复习. 第 2 次. p. x. 一、概念 指针是 c 的特色之一,也是难点。 使用指针能有效地表示复杂的数据结构;动态分配内存; 方便地使用数组,字符串;实现双向地址调用;使 c 能直接处 理单元地址,具有低级语言的特点。这对设计系统软件很重要。 对变量访问方式: 单元地址: 单元地址: 1000 10 a pa 1000 1 a
E N D
全国C等级考试复习 第2次
p x 一、概念 指针是 c 的特色之一,也是难点。 使用指针能有效地表示复杂的数据结构;动态分配内存; 方便地使用数组,字符串;实现双向地址调用;使 c能直接处 理单元地址,具有低级语言的特点。这对设计系统软件很重要。 对变量访问方式: 单元地址: 单元地址: 1000 10 a pa1000 1 a 1002 20 b pb1002 2 b
main() main() { int a, b; { int a=1; b=2; a=10; int *pa, *pb; b=20; pa=&a; pb=&b; printf (“%d,%d\n”,a, b); *pa=100; *pb=200; } printf (“%d,%d\n”, *pa,*pb ); } 通过指针变量来访问: 间接访问 即由指针变量存放的地址来 得到变量的值。 通过变量名访问: 直接访问 给变量赋值; 将变量值输出
二、指针变量 指针——就是变量在内存中的地址。 指针变量——存放指针的变量,即用来存放 变量地址的变量。 指针类型由其所指向的数据类型决定 指针变量定义: 类型标识符 *变量名 int *pa, *pb; /* 定义pa, pb分别指向整型类型 */ float *pf1, *pf2; /* 定义pf1, pf2分别指向实型变量 */ char *pc1, *pc2; /* 定义pc1,pc2分别指向字符型变量 */ pa 和 pb 只可指向整型变量, 不可指向其他类型变量; pf1, pf2 只可指向实型; pc1, pc2 只可指向字符型;
1、运算符 • *:间接引用运算符 *p表示指针变量p所指内容 • *p可用于左值 *p=2; • & 取地址符 • int x,*p; • p=&x ; 则 *p=x; *&x=x; • *p*&xx • 注意:不允许将任何非地址类型的数据赋给指针变量 • 例:int a,*p; • a=4; p=&a; • scanf(“%d”,p); scanf(“%d”,&a); • printf(“%d”,*p); printf(“%d”,a);
p=p+1; int *p; 2、指针变量的加法和减法运算包括+ +和- - 指针运算是地址的运算: 取决于指针指向的数据类型(T)。 px+n, px-n ——将指针从当前位置 向前或向后移动n个数据单位。 pxn的结果为: pxnsizeof(T) px-py:两指针位置之间的数据个数,不是地址差。 px-py的结果为:( px-py) / sizeof(T) y= px++ —— y= ( px++ ),注意优先级和结合性 y= ++ px —— y= ( ++ px) 问题:y= px++和y= (px)++的意义?
【1.74】已知:int a[]={1,2,3,4}, y, *p=a;则执行语句y = (*++p)--;之后, 数组a各元素的值变为______。 A) 0,1,3,4 B) 1,1,3,4 C) 1,2,2,4 D) 1,2,3,3 变量y的值是____。 P ++p A) 1 B) 2 C) 3 D) 4 y的值是++p所指的对象的值2 (*++p)– 则是a[1]的值自减 [知识点] 指针指向数组,地址所在的单元格值变化,也就是数组元素值的变化
例2 用函数实现两数交换 void swap(int *pa, int *pb) { int temp; temp=*pa; *pa=*pb; *pb=temp; } main ( ) { int a, b; int *pa1, *pb1; scanf(“%d,%d\n”,&a, &b); pa1=&a; pb1=&b; if (a< b) swap(pa1, pb1); printf(“%d,%d \n”,a, b); } 参数传递 函数中实现a b &a a &a pa1 pa1 a 3 30 pa1 a &a pa &a pa &a 3 pb1 b b &b &b b pb1 pb1 &b 30 30 3 &b pb &b pb
例3:在函数中交换指针值。是不行的 void swap(int *pa, int *pb ) main( ) { int a, b; { int *p; /* 交换指针的值 int *pa,*pb; p=pa; 返回时,pa,pb scanf(“%d,%d”,&a, &b); pa=pb; 均释放,达不 pa1=&a; pb1=&b; pb=p; 到交换a,b的目 printf(“%d,%d\n”,a,b); } 的 */ if (a<b) swap(pa1,pa2); printf(“%d,%d\n”,a, b); } void swap(int *pa, int *pb) { int *p; /* 指针变量 p 未赋值,是随机值,可能会使系统不 *p=*p1; 正常 */ *p1=*p2; *p=*p2; }
【1.77】函数的功能是交换变量x和y中的值,且通过正确调用返回交换的结果。能正确执行此功能的函数是 。 A) funa (int *x, int *y) B) funb (int x, int y) { int *p; { int t; p=x;*x=*y;*y=*p; t=x;x=y;y=t; } } C) func (int *x, int *y) D) func (int *x, int *y) { *x=*y;*y=*x; { *x=*x+*y;*y=*x-*y;*x=*x-*y; } } [知识点] 指针指向数组
复习:一维数组元素及其地址表示 int a[5] = { 1, 2, 3, 4, 5 } ;
等价于 复习:指向一维数组元素的指针 int a[5] = { 1, 2, 3, 4, 5 } , *p; p=a; p=&a[0]; 指针变量p与数组名a 都表示数组的首地址, 但数组名a是指针常量, 而p是指针变量
复习:指向一维数组元素的指针 注意: • p++合法, 但a++不合法, 因为a是地址常量, 不能改变, • 而p 是一个指针变量, 可以改变 (2) 要注意指针变量的当前值,保证它指向数组中有效的元素 (3) 注意指针变量的运算 ①*p++ *(p++) 先取p所指向的变量,再使p自加指向下一个变量 ②*(p++) 与*(++p)不同, *(++p)是先使p自加指向下一个变量,再取其值 ③ (*p)++ 先取p所指向的变量,再使变量的值加1 (4) 指针变量也可以加下标 p[i] a[i] *(p+i)
N m 6 2 p a temp q a+5 t a+2 例:将数组a中的n个数按相反顺序存放 #include <stdio.h> #define N 6 void main( ) { int a[N]={ 1, 3, 4, 6, 7, 9 }; int temp, m=(N-1)/2; int *p , *q , *t ; p=a; q=a+N-1; t=a+m; for( ; p<=t; p++, q-- ) { temp=*p; *p=*q; *q=temp; } for( p=a; p<a+N; p++ ) printf(“%3d”, *p ); printf(“\n”); } 9 a[0] a[1] a[2] a[3] a[4] a[5] a+1 a+2 a+3 7 6 1 4 3 4 3 1 a+2 a+4 a+3
【1.68】已知:char s[10], *p=s,则在下列语句中,错误的语句是 。 A) p=s+5; B) s=p+s; p p+5 C) s[2]=p[4]; D) *p=s[0]; [知识点]注释:选项B有两处错误,一是数组名是常量,不能出现的赋值符号的左侧,二是指针变量只能和整数做加,不能和作为地址常量的数组名相加。
【1.69】已知:char s[100];int i;则引用数组元素的错误的形式是 。 A) s[i+10] B) *(s+i) C) *(i+s) D) *((s++)+i) [知识点] s作为数组名是地址常量,而s++是s=s+1,C语言不允许对常量进行赋值
【1.73】已知:int a[ ]={1,2,3,4,5,6,7,8,9,10,11,12},*p=a;则值为3的表达式是 。 A) p+=2,*(p++) B) p+=2,*++p C) p+=2,*p++ p p+2 D) p+=2,++*p *++p: 4 ,*p++ :a[2]++:4 ,++*p : a[3]:4 [知识点] 指针移动
【1.70】已知:char s[6], *ps=s;则正确的赋值语句是 。 A) s="12345"; B) *s="12345"; C) ps="12345"; D) *ps="12345"; [知识点] 将字符串首地址赋值给指针,指针指向该字符串
a, &a[0] 元素 地址 地址 a+1, &a[1] a &a[0] a[0] &a[1] a+1 a[1] &a[2] a+2 a[2] a+2, &a[2] 二维数组与指针 int a[3]; • 二维数组的地址 • 二维数组名a表示二维数组的首地址, 也是第0行的首地址, a &a[0] a+1代表第1行的首地址, a+1 &a[1] a+2代表第2行的首地址, a+2 &a[2]
a[1]+2 &a[1][2] a[1] &a[1][0] a, &a[0] a[0] &a[0][0] a[0]+1 &a[0][1] a+1, &a[1] a+2, &a[2] a[2]+3 &a[2][3] a[2] &a[2][0] • 二维数组的地址 第2讲 二维数组与指针 • a[0], a[1], a[2]既然是第二重一维数组名, 它们就代表了相应一维数组的首地址, 因此a[0]代表了第0行第0列元素的地址, 即 a[0] &a[0][0] a[0]+1 &a[0][1] a[1] &a[1][0] a[1]+2 &a[1][2] a[2] &a[2][0] a[2]+3 &a[2][3]
*a+1 a[0]+1 &a[0][1] a, &a[0] a[0] &a[0][0] a+1, &a[1] a+2, &a[2] *(a+1) a[1] &a[1][0] a[2]+3 &a[2][3] • 二维数组的地址 思考:*a,*a+1,*(a+1),*(a+2)+3表示什么? 因a &a[0], 所以*a *(&a[0]) a[0] &a[0][0] 即: *a a[0] &a[0][0] *a表示元素a[0][0]的地址 *a *a+1表示元素a[0][1]的地址 *(a+1)表示元素a[1][0]的地址 *(a+2)+3表示元素a[2][3]的地址 *(a+2)+3
a[0]+0 a[0]+4 第0班第0个战士 第0班第4个战士 a 0班 1班 2班 a+1 a+2 排长“指向”班,纵向管理,走一步就跳过1个班 班长“指向”战士,横向管理,走一步就只是指向下一个战士 二维数组a 相当于排长 一维数组a[0]、a[1]、a[2]相当于班长 二维数组元素a[0][1] 就相当于战士
a+1与a[0]+1是不同的:a+1是序号为1的行的首地址,指向序号为1的行(相当于排长走到序号为1班的第一个战士位置);而a[0]+1是指向0行1列元素(相当于序号为0班的第1个战士)。a+1与a[0]+1是不同的:a+1是序号为1的行的首地址,指向序号为1的行(相当于排长走到序号为1班的第一个战士位置);而a[0]+1是指向0行1列元素(相当于序号为0班的第1个战士)。 • a、a[0]的值虽然相同,但是由于指针的类型不同(相当于排长和班长面对的对象一样,但意义不同),a是指向一维数组,a[0]指向a[0][0]元素,因此,进行加1运算,得到的结果是不一样的。 • 二维数组a是指向行的。在指向行的指针前面加一个*,就转换为指向列的指针。反之,在指向列的指针前面加一个&,就转换为指向行的指针。
例如:a和a+1是指向行的指针 *a和*(a+1)是指向列的指针,分别指向a数组0行0列 的元素和1行0列的元素 反之:a[0] 是指向0行0列元素的指针,由于a[0] 等价于*(a+0), 因此&a[0]等价于&*a,也就是与a等价,指向二维数组 的第0行。 不要简单的把&a[i]为求a[i]单元的物理地址,因为并不存在a[i]这样一个实际的变量。 &a[i](即a的值)与a[i]的值是一样的,但是含义不同。 &a[i]指向行,而a[i]指向列。
小结:二维数组的地址分为行地址和列地址 • 二维数组名a代表二维数组的首地址, • 即: 第0行的地址为 a &a[0] 第1行的地址为 a+1 &a[1] 第2行的地址为 a+2 &a[2] • 二维数组的列地址即元素的地址
指向二维数组的指针变量 1、指向数组元素的指针变量 例:用指针变量输出二维数组中的元素 #include<stdio.h> void main( ) { int a[3][4]={{1,3,5,7},{2,4,6,8},{9,10,11,12}}; int *p; for( p=a[0];p<a[0]+12; p++) { if ( (p-a[0])%4==0) printf(“\n”); printf(“%4d”, *p); } } 思考: p=a; p=&a[0][0]; p=*a; p=*a[0]; 错 对 对 错
指向二维数组元素的指针 普通指针变量 int *p,可以指向整形数据或整型数组元素。 若使p不是指向整型变量,而是指向一个包含m个元素的一维数组(即二维数组)。这时,如果p先指向a[0](即p=&a[0]),则p+1不是指向a[0][1],而是指向a[1],p的增值以一维数组的长度为单位。 那么指针变量 p 应该怎么定义呢?
2048 2026 2010 36 q x p a • 指向二维数组元素的指针 int a[3][4]; int **p; p=a; 合法吗? 非法! int x=36, *q,**p; q=&x; p=&q; 2026 2010 p是二级指针, 它指向一个整型指针变量q, 而q指向一个整型变量 a是一种地址表示方法, a实际指向了第一重一维数组元素a[0],而 a[0]是第二重一维数组
虽然在定义p的时候只用了一个*,但p实际上是一个二级指针变量虽然在定义p的时候只用了一个*,但p实际上是一个二级指针变量 • 指向二维数组元素的指针 定义格式:类型名 (*指针变量名)[数组长度]; int a[3][4]; int (*p)[4]; p=a; p指向一个包含有4个整型数据的一维数组 合法! *p所指的对象是有4个整形元素的数组,即p是指向一维数组的指针。p的值就是该一维数组的起始地址,p不能指向一维数组中的某个元素。
指向二维数组的指针变量 例:输出二维数组中的元素 #include<stdio.h> void main( ) { int a[3][4]={{1,3,5,7},{2,4,6,8},{9,10,11,12}}; int (*p)[4], i, j; p=a; for( i=0; i<3; i++) { for( j=0; j<4; j++) printf(“a[%d][%d]=%-4d”, i, j, *(*(p+i)+j) ); printf(“\n”); } } 分析: *(*(p+i)+j)是什么? p=a => *(p+i)=a[i] => *(p+i)+j =a[i]+j =&a[i][j] *(*(p+i)+j) = a[i][j]
【1.67】设有说明int (*p)[4];其中的标识符p是 。 A) 4个指向整型变量的指针变量 B) 指向4个整型变量的函数指针 C) 一个指向具有4个整型元素的一维数组的指针 D) 具有4个指向整型变量的指针元素的一维指针数组 [知识点]题干中由于*和p被小括号括起,所以p应被解释为一个指针,而后的下标运算符[]说明所指向的对象是一个有4个int型元素的一维数组;如果是int (*p)(),则是指向函数的指针。对于int *p[4],则根据运算符的优先级,先考虑p和[]运算符的关系,所以它就是一个指针数组了。
【1.72】已知:int a[4][3]={1,2,3,4,5,6,7,8,9,10,11,12}; int (*ptr)[3]=a,*p=a[0]; 则以下能够正确表示数组元素a[1][2]的表达式是 。 A) *(*(a+1)+2) p B) *(*(p+5)) C) (*ptr+1)+2 ptr D) *((ptr+1)[2])
【1.71】已知:char a[3][10]={"BeiJing","ShangHai","TianJin"}, *pa=a;不能正确显示字符串"ShangHai"的语句是 。 A) printf("%s",a+1); B) printf("%s",*(a+1)); C) printf("%s",*a+1); D) printf("%s",&a[1][0]); pa [知识点] 字符串
指 针 数 组 (1) 一、指针数组的概念: 1、定义: 一个数组的各个元素均为指针类型数据, 称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。 2、一维数组的定义形式: 类型名 *数组名[数组长度] ; 例如:int *p[4] ; 由于[ ]比*优先级高,先有p[4]形式,是数组, 有4个元素。再与p 前面的“*”结合,“*”表示 此数组是指针类型的,每个数组元素(相当于一 个指针变量)都可指向一个 int 型变量。
指 针 数 组 (2) 二、指针数组的运用 注意:不要写成int(*p)[4],这是指向大小为4的一维数组 的单个指针变量。 例10.27将若干字符串按字母顺序(由小到大)输出。
函数指针 指针变量可以指向变量地址、数组、字符串、动态分配地址,同时也可指向函数,每一个函数在编译时,系统会分配给该函数一个入口地址,函数名表示这个入口地址,那么,指向函数的指针变量称之函数指针变量. 例如: float fun(int,char); float (* p)( ); p=fun;
2.用函数指针变量调用函数 可用函数指针来调用函数. 其形式为: (*函数指针变量名)(<实参表>) 例如: int i=5; char ch=‘a’; float fun(int,char),(*p)( ); p=fun; (*p)(i,ch); … int i=5; char ch=’a’; float fun(int,char); fun(i,ch);
[例7.18]利用直接函数调用,求函数返回值。 #include<stdio.h> main() { int a=3,b=5; float f1(),f2(); printf(”%4.1f\n”,f1(a,b)); printf(”%4.1f\n”,f2(a,b)); } float f1(int x,int y) { return x+y;} float f2(int x,int y) { return(1.0*y)/x;} 运行结果: 8.0 1.7
[例7.19]利用函数指针变量调用,求函数返回值[例7.19]利用函数指针变量调用,求函数返回值 #include <stdio.h> sub(int x,int y,float(*fun)()) { float result; result=(*fun)(x,y); printf(”%4.1f\n”,result); } float f1(int x1,int y1) { return x1+y1; } float f2(int x2,int y2) { return (y2*1.0)/x2; } main( ) { int a=3,b=5; float fl( ),f2(); sub(a,b,f1); sub(a,b,f2); }
【1.66】说明语句"int (*p)( );"的含义是 。 A) p是一个指向一维数组的指针变量 B) p是指针变量,指向一个整型数据 C) p是一个指向函数的指针,该函数的返回值是一个整型 D) 以上都不对 [知识点]函数返回值为指针的函数的定义
结构体变量引用 结构体成员引用 引用结构变量成员的一般形式是: 结构体变量名.成员名 .为成员运算符,优先级最高,结合性为自左向右。 例如: strStu1.iNum 第一个学生的学号 strStu2.cSex 第二个学生的性别 如果成员本身又是一个结构体则必须逐级找到最低级的成员才能使用。 例如: strStu1.strBirthday.iMonth第一个学生出生的月份 成员可以在程序中单独使用,与普通变量完全相同。 C程序设计快速进阶大学教程
结构体与指针 • 一个指针变量也可以指向结构体变量,一个指针变量当用来指向一个结构体变量时,称之为结构体指针变量。结构体指针变量中的值是所指向的结构体变量的首地址。通过结构体指针即可间接访问该结构变量。 • 结构体指针定义说明的一般形式为: • struct 结构名 *结构指针变量名; • struct student *pStrStudent; • pStrStudent =&strStu1; C程序设计快速进阶大学教程
12.1.5 结构体与指针 • 通过结构体指针变量,就能间接地访问结构体变量的各个成员。 • 其访问的一般形式为: • (*结构指针变量).成员名 • 或为: • 结构指针变量->成员名 • ->为指向运算符,优先级最高,结合性为自左向右。两种写法完全一样。 • (*pStrStudent).iNum • 或者: • pStrStudent->iNum • 应该注意(*pStrStudent)两侧的括号不可少,因为成员符"."的优先级高于"*"。 C程序设计快速进阶大学教程
【1.78】有定义如下: struct sk { int a; float b; }data ,*p; 如果 p=&data;则对于结构变量data的成员a的正确引用是____。 A)(*).data.a B) (*p).a C) p->data.a D) p.data.a [知识点] 结构体
【1.79】已知: struct st { int n; struct st *next; }; static struct st a[3]={1,&a[1],3,&a[2],5,&a[0]},*p; 如果下述语句的显示是2,则对p的赋值是____。 printf("%d",++(p->next->n)); A) p=&a[0]; B) p=&a[1]; C) p=&a[2]; s[0] s[1] s[2] D) p=&a[3]; 1 3 5 [知识点]注释:使用C对p进行赋值,则p->next是a[0]的地址,引用其成员n再做前增1运算,结果就是2。
结构体数组 数组的元素也可以是结构体类型的 struct Student { int iNum; char *pcName; char cSex; float fScore; }strStuArr[5]; C程序设计快速进阶大学教程
结构体数组 struct student strStuTemp; int i1,i2,iFlag; for(i1=0;i1<4;i1++) { iFlag=i1; for(i2=i1+1;i2<5;i2++) if(strStuArr[i2].fScore<strStuArr[iFlag].fScore) iFlag=i2; if(iFlag!=i1) { strStuTemp=strStuArr[i1]; strStuArr[i1]=strStuArr[iFlag]; strStuArr[iFlag]=strStuTemp; } } for(i1=0;i1<5;i1++) { printf("Num=%d Name=%s ",strStuArr[i1].iNum,strStuArr[i1].pcName); printf("Sex=%c Score=%f\n",strStuArr[i1].cSex,strStuArr[i1].fScore); } return 0; } 例12.2 对学生表按照成绩从小到大排序 #include <stdio.h> #include <string.h> /*声明结构体类型*/ struct student { int iNum; char *pcName; char cSex; float fScore; }; int main() { /*定义结构体数组*/ struct student strStuArr[5]= { {101,"Li ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'M',87}, {105,"Wang ming",'F',58} }; C程序设计快速进阶大学教程
【1.80】已知: struct person { char name[10]; int age; }class[10]={"LiMing",29,"ZhangHong",21,"WangFang",22}; 下述表达式中,值为72的一个是____。 A) class[0]->age + class[1]->age+ class[2]->age B) class[1].name[5] C) person[1].name[5] D) clase->name[5] [知识点]字符所对应的ASCII码值
【1.81】已知: struct { int i; char c; float a; }test; 则sizeof(test)的值是 。 A) 4 B) 5 C) 6 D) 7 [知识点]结构体类型的长度是全体成员长度之和
【1.82】已知: union { int i; char c; float a; }test; 则sizeof(test)的值是 。 A) 4 B) 5 C) 6 D) 7 [知识点]共用体类型的长度是全体成员最长的长度