• 1. 第8章 指 针掌握指针与指针变量的概念; 掌握数组的指针和指针数组的使用; 掌握字符串指针和指向字符串的指针变量的使用; 掌握指针函数和函数指针的使用; 了解指向指针的指针的概念及其使用。
  • 2. 预 备 知 识内存:就是内部存储器,是由存储单元组成 的。它的特点是存储单元是线性连续 的。存储单元的最小单位是字节。 1. 内存的概念
  • 3. 地址:为了访问内存中的某个存储单元,我们 要为它编号,这种编号称为内存地址。 通过地址我们就能够访问该地址所标 识的存储单元。2. 地址的概念
  • 4. 变量的地址:变量在内存中总占用几个连续的 字节,开始字节的地址,就是变量的地址。2007存储单元
  • 5. 指针:一个变量的地址称为该变量的指针。指针变量:若一个变量专用于存放另一个变量 的地址(指针),则该变量称为指针变量
  • 6. 指针的对象:当把变量的地址存入指针变量后, 我们就可以说这个指针指向了该变量。 变量的存取方法:直接存取和间接存取。 直接存取:直接根据变量名存取数据。 间接存取:通过指针变量存取相应变量的数据。
  • 7. 8.1 空间的动态分配与指针运算8.1.1 问题的提出和程序示例 P198例8.18.1.2 空指针 空指针是一个特殊的值,写作NULL,其数值为0。
  • 8. 8.1.3 存储器的申请 格式: (指针所指对象的数据类型*) malloc (sizeof (指针所指对象的数据类型)*个数) 功能:从内存中申请一块指定字节大小的连续空间,返回该存储块的首地址。 如:int *pi,*pj; pi=(int *) malloc (sizeof(int)); pj=(int *) malloc (sizeof(int)*10); 注意: (1)函数malloc必须要说明为“void *malloc()”,或者在程序中加入“include ”stdlib.h””。 (2) 指针必须指向已经分配好的空间才有意义。
  • 9. 8.1.4 存储器的释放 格式:free(指针变量名) 功能:释放以指针变量名所的位置开始的存储块。 如:free(pi); free(pj); 注意: (1)“free(p);”释放p所指的空间,不改变p的内容。 (2) free()必须与malloc() 配对使用。
  • 10. 8.1.5 指针值的算术运算 1. 在指针值上加减一个整数 p+n指向的是p所指的数据存储单元之后的第n个数据存储单元。 2. 指针值的比较 参与比较的指针必须是指向相同类型数据的指针变量,且要指向同一段连续的内存空间。 3. 指针减法 p-q= n表示p为q后面的第n个数据存储单元。
  • 11. 8.2 指针与函数8.2.1 返回指针值的函数 定义格式: 返回的指针指向单元的类型 *函数名(参数表) { } 说明格式: 返回的指针指向单元的类型 *函数名(参数表); 调用格式: 指针变量=函数名(实参表);
  • 12. 8.2.2 指向函数的指针 说明格式: 类型 (*指针变量名)(参数表); 调用格式: (*指针变量名)(实参表);
  • 13. 如果函数2作为参数传递给函数1,则函数1的 定义格式 : 函数1 (类型 (*指向函数2的指针变量) (形参表)) { (*指向函数2的指针变量) (实参表); } 调用格式: 函数1 (函数2);
  • 14. 8.3 指针与一维数组 8.3.1 问题的提出与程序示例数组的指针 :是数组的起始地址。 数组元素的指针 :是数组元素的地址。 当指针变量指向数组或数组元素时,它就是指向数组的指针变量。
  • 15. C规定:⑴数组名代表数组的首地址(起始地址), 也就是第一个元素的地址。 ⑵当指针变量p指向数组时,p+1指向数组 的下一个元素。假设一个整型元素占两 个字节,p+1是使p的地址加2个字节。 如:int a[10],*p ; 则:p=a ; 与 p=&a[0];等价 称指针变量p指向数组元素a[0] p+i、a+i 、&p[i]、&a[i] 都是a[i]的地址。 *(p+i)、*(a+i)、p[i]、a[i]都是a[i]的值。 P211例
  • 16. 定义指向数组元素的指针时,“*”号前的数据类型应该与数组元素的数据类型一致。 如:short int x[10]; long *px; px=x;
  • 17. 实参与形参都可用数组名或指针变量。其对应关系有以下4种情况:⑴实参与形参都用数组名;⑵实参用数组名,形参用指针变量;⑶实参为指针变量,形参用数组名;⑷实参、形参都用指针变量。注意:用指针变量作实参时一定要有确定的值。8.3.2 数组名及指针作为函数参数
  • 18. 例:从10个数中找出其中最大值和最小值。 void max_min(int a[],int n,int *max,int *min); main() { int i,a[]={2,4,1,6,7,32,45,75,45,90},max,min; for(printf("The original array="),i=0;i<10;i++) printf("%5d",a[i]); max_min(a,10,&max,&min); printf("max=%d min=%d",max,min); } void max_min(int a[], int n, int *max, int *min) { int i; *max=*min=a[0]; for(i=0;ia[i]) *min=a[i]; } }
  • 19. 上例中如果形参数组用指针变量,则程序如下: void max_min(int *x,int n,int *max,int *min); main() { int i,a[10]={2,4,1,6,7,32,45,75,45,90},max,min; for(printf("The original array="),i=0;i<10;i++) printf("%5d",a[i]); max_min(a,10,&max,&min); printf("max=%d min=%d",max,min); } void max_min(int *x, int n, int *max, int *min) { int i; *max=*min=*x; for(i=1; i*x) *min=*x; } }
  • 20. 8.3.3 指针与字符串 字符串指针:字符串的首地址。 字符指针变量:指向字符串的指针变量。 例如:char *strp=“Hello!”; 定义了一个指向字符串的指针变量,并将字符串的首地址赋值给strp指针变量,即:使指针变量strp指向字符串首地址。 字符指针变量定义格式: char *指针变量名; 字符指针变量定义时可以赋初值。
  • 21. C程序访问字符串有以下两种方法: 1. 用字符数组存放一个字符串 例如:char s []="I am a student.";字符串输出语句可写成: ⑴ printf(“%s\n”,s); ⑵ for(i=0; s[i]; i++)printf(“%c”, s[i]); ⑶ for(i=0; s[i]; i++)printf(“%c”,*(s+i)); 也可用s[i]!=‘\0’2.用字符指针指向一个字符串 例如:char *ps="I am a student.";字符串输出语句可写成: ⑴ printf(“%s\n”,ps); ⑵ for(; *ps; ps++)printf(“%c”, *ps);
  • 22. 说明:⑴字符数组由若干个元素组成,每个元素中放一个字符。而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中; ⑵字符数组和字符指针变量都可以在定义时赋初值,但以下方法对字符数组非法,对字符指针变量合法: char s[10]; s=“hello!”; ╳ char *ps; ps=“hello!”; √⑶字符指针变量必须先赋值后使用,否则对系统构成危险。因此,不可以如下编程: char *ps; scanf(“%s”, ps); ╳ 而应写成:char *ps,s[10]; ps=s; scanf(“%s”, ps); √
  • 23. ⑷字符数组名是指针常量,只能表示一个确定的字符串,不能改变。而字符指针变量的值是可以改变的,它可以代表不同的字符串。 ⑸若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指字符串中的字符。 如:char *a=“I love China!”;printf(“%c”,a[5]); ⑺若把字符指针所指对象当作数组使用,应注意对象的长度。 如:char str1[10],*ps=str1; ps[10]=‘\0’; ╳⑹用指针变量指向一个格式字符串,可以用它代替printf函数的格式字符串。 如:int a; char *format; format=“a=%d\n”; 则:printf(format,a);相当于printf(“a=%d\n”,a);
  • 24. 程序举例:将字符串a复制为字符串b。#include “stdio.h” main() { char a[]="I am a boy.", b[20]; int i=0; do { *(b+i)=*(a+i); i++; }while(*(a+i)!=‘\0’); /* 也可写成while(*(a+i));*/ puts(b); }
  • 25. 上例程序还可写成:#include “stdio.h” main() { char a[]="I am a boy.", b[20],*p1=a,*p2=b; int i=0; do { *p2=*p1; p2++;p1++; }while(*p1!='\0'); /*也可写成 while(*p1);*/ puts(b); }
  • 26.  指向指针的指针变量(也称多级指针),只能存放指针变量的地址。定义格式:  类型标识符 **指针变量名; 例如:int **pp, *p, i=50;    p=&i; pp=&p; 50i10001000p4000pp4000注意:要使用一个多级指针指向目标值,必须连续使用指针运算符“*”。 例如:上例中**pp就是i的值含意:使指针变量p指向i,指针变量pp指向p8.4 二级指针
  • 27. 8.5 指针数组 8.5.1 问题的提出与程序示例 8.5.2 指针数组的定义和使用定义格式:类型标识符 *指针变量名[常量表达式]; 例如: int *p[2]; 含意:p是一个一维指针数组,每个元素都是 一个指向整型变量的指针变量。可以将 整型变量的地址赋值给元素p[0]或p[1] 注意:区别 int *p[2] 、int (*p)[2] 的含意。指针数组:每个元素都是基类型相同的指针变量的数组。 指向数组的指针
  • 28. 1. 利用指针数组处理多个串 方法⑴:先用指针数组指向字符数组, 再处理。 #include “stdio.h” main() { char name[][80]={"aaa","bbb","ccc","ddd","eee"}; char *pname[10], i ; for(i=0;i<5;i++)pname[i]=name[i]; for(i=0;i<5;i++)puts(pname[i]); }
  • 29. 1. 利用指针数组处理多个串 方法⑴:先用指针数组指向字符数组, 再处理。 #include “stdio.h” main() /*与用行指针解决上述问题比较*/ { char name[][80]={"aaa","bbb","ccc","ddd","eee"}; char (*pname)[80]=name, i ; for(i=0;i<5;i++)puts(pname[i]); }
  • 30. 方法⑵:用指针数组指向字符串常量。 #include “stdio.h” main() { char *pname[10]={"aaa","bbb","ccc","ddd","eee"}; int i ; for(i=0;i<5;i++)puts(pname[i]); }
  • 31. 2. 用指针数组指向动态内存 void指针类型介绍: 新标准增加了void指针类型,它用于定义一个指针变量,但不指定它是指向哪种类型数据。在将它的值赋给另一个指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。
  • 32. 例如: char *p1; int a=2; void *p2; p2=&a; p1=(char *)p2;强制类型转换void指针变量定义 同样,可用 p2=(void *)p1; 将p1的值转换成void 指针类型。 可以将一个函数定义为void 指针类型。表示该函数返回的是一个地址,它指向空类型,如需要引用此地址,也需要根据情况进行类型转换。如, 库函数:void *malloc(unsigned size); 功能:分配size字节的存储区。函数调用例: char *p; p=(char *)malloc(60);分配60字节的存储区,并返回该内存区的地址,并赋值给p
  • 33. #include “stdio.h” void sort(char *ps[ ],int n); #define N 5 main() { char *ps[100],i; for(i=0;i
  • 34. 在C语言中,一个二维数组可以看成是一个一维数组,其中每个元素又是一个包含若干元素的一维数组。 假如有定义:int a[2][3]; 则C语言编译程序认为a数组是由a[0],a[1]两个元素组成的一维数组,a[0]和a[1]分别是包含三个元素的一维数组名,分别代表a数组元素的起始地址(即a[0]是第0行元素的首地址, a[1]是第1行元素的首地址)。因此,a和a[0]是两个基类型不同的指针常量。8.6 指针与二维数组8.6.1 用指针方法操作二维数组
  • 35. 假如有定义: int a[3][5], i,j ; (其中0≤i <3, 0≤j <5)则:⑴a[i]和*(a+i)(无条件等价)都是第i行第0列 元素的地址,那么a[i]+j、*(a+i)+j、 &a[0][0]+5*i+j都是第i行第j列元素的地址。 ⑵数组a中任意元素a[i][j]的引用可以表示成如 下几种形式: a[i][j]、*(a[i]+j)、*(*(a+i)+j)、(*(a+i))[j]、 *(&a[0][0]+5*i+j )
  • 36. 1. 定义一个指向指针的指针。 2. 为二级指针申请一个指针数组。 3. 为指针数组的元素赋值。 如: float **x; int m,n,i; scanf(”%d%d”,&m,&n); x=(float **)malloc(m*sizeof(float *)); for(i=0;i
  • 37. 8.6.3 用指向数组的指针操作二维数组 定义格式:类型标识符 (*指针变量名)[m]; 例如:假若有语句 int a[2][3], (*p)[3]; p=a; 注意:*p两侧的圆括号不可缺少。 则:⑴ p是一个指向由3个整型元素组成的一 维数 组的指针变量。⑵ p指向a数组,p+1指向数组a的下一行首地 址,a和p的基类型相同,则a数组中任意元 素a[i][j]还可以如下表示:*(p[i]+j) 、*(*(p+i)+j) 、(*(p+i))[j] 、p[i][j]
  • 38. 例:使用指向数组的指针变量访问数组元素。#include “stdio.h” main() { float fa[5][10], (*pf)[10]=fa; int i,j; for(i=0; i<5; i++) for(j=0; j<10; j++) *(*(pf+i)+j)=i*j; for(i=0;i<5;i++) for(puts(" "),j=0;j<10;j++) printf(" %6.0f",fa[i][j]); }
  • 39. main函数是由系统调用的,可以有参数。 可执行文件名 参数1 参数2 …… 参数n 实参是和命令一起给出的,也就是在DOS提示符下,输入本程序的可执行文件名和需要传给main函数的参数,命令行的一般形式为:main函数实参:带参数的main函数的原型是: main( int argc, char *argv[ ]) ;命令行参数的数目(包括可执行文件名)指针数组,各元素分别指向命令行中的各参数(包括可执行文件名),即字符串的首址8.7 命令行参数
  • 40. 例:若以下面程序编译后生成可执行程序 file.exe, 在DOS提示符下按下述形式执行程序: file China Bejing Shenyang 写出程序运行结果。#include main(int argc, char *argv[ ]) { while(--argc>0) printf(“%s\n”, argv[argc]) ; }运行结果: Shenyang Bejing China
  • 41. 定 义含 义 int *p ;p为指向整型数据的指针变量 int *p[10] ;定义指针数组p,它有10个指向整型数据的指针元素 int (*p)[10] ;p为指向含10个元素的一维数组的指针变量 int *p( ) ;p为带回一个指针的函数,该指针指向整型数据 int (*p)( ) ;p为指向函数的指针,该函数返回一个整型值 int **p ;p是一个指针变量,它指向一个指向整型数据的指针变量