C语言程序设计教程指针(新)nullnull8.1 指针与指针变量 8.2 指针与函数 8.3 指针与数组 8.4 指针与字符串 8.5 指针数组与命令行参数 8.6 程序举例第 8 章 指针C语言程序设计教程8.1 指针与指针变量8.1 指针与指针变量8.1.1 指针的概念 1.内存与变量地址 内存地址:内存是计算机用于存储数据的存储器,以一个字节作为存储单元,为了便于访问,给每个字节单元一个唯一的编号,第一字节单元编号为0,以后各单元按顺序连续编号,这些单元编号称为内存单元的地址 。 变量地址:变量所分配存储空间的首字节单元地址...
=q都是允许的。 指针的关系运算在指向数组的指针中广泛的运用,假设 p、q是指向同一数组的两个指针,执行p>q的运算,其含义为,若表达式结果为真(非0值),则说明p所指元素在q所指元素之后。或者说q所指元素离数组第一个元素更近些。 注意:在指针进行关系运算之前,指针必须指向确定的变量或存储区域,即指针有初始值;另外,只有相同类型的指针才能进行比较。 8.1.4 多级指针 8.1.4 多级指针 把指向指针型数据的指针变量称为指向指针的指针,或称多级指针。 二级指针的定义形式如下: 数据类型 **指针变量 例如: int a,*p,**pp; a=22; p=&a; pp=&p; 思考:如何用pp得到a的值? 假设变量a的地址为4000,指针p的地址为4100,二级指针pp的地址为4800。a、p、pp三者的关系如上图。pp(4800) p(4100) a(4000)8.2 指针与函数8.2 指针与函数8.2.1 指针作为函数参数 利用指针作函数参数,可以实现函数之间多个数据的传递,当形参为指针变量时,其对应实参可以是指针变量或存储单元地址。 函数形参为指针变量,用指针变量或变量地址作实参 例8.3 编写一个交换两个变量的函数,在主程序中 调用,实现两个变量值的交换。程序如下:程序如下:#includevoid swap(int *p1,int *p2); /*函数声明*/ main() { int a,b; int *pa,*pb; scanf(″%d%d″,&a,&b); pa=&a; /* pa指向变量a */ pb=&b; /* pb指向变量b */ swap(pa,pb); printf(″\na=%d,b=%d\n″,a,b); }或:swap(&a,&b);接上页:接上页:void swap(int *p1,int *p2) {int temp; temp=*p1; /*交换指针p1、p2所指向的变量的值*/ *p1=*p2; *p2=temp; }程序运行结果如下: 输入: 12 22↙ 输出: a=22,b=12两点说明:两点说明: (1) 若在函数体中交换指针变量的值,实参a、b的值并不改变,指针参数亦是传值。 例如: int *p; p=p1; p1=p2; p2=p; 不要希望如此完成a,b值的交换。 (2) 函数中交换值时不能使用无初值的指针变量作临时变量。 例如: int *p; *p=*p1; *p1=*p2; *p2=*p; p无确定值,对 p的使用可能带来不可预期的后果。8.2.2 指针函数8.2.2 指针函数指针函数:是指返回值为指针的函数 指针函数的定义形式: 类型标识符 *函数名(参数) 例如: int *fun(int a,int b) { 函数体语句 } 在函数体中有返回指针或地址的语句,形如: return (&变量名); 或 return (指针变量); 并且返回值的类型要与函数类型一致。 例8.3 分析如下程序例8.3 分析如下程序main( ) { int a,b,*p; int *max(int x,int y); scanf(“%d,%d”,&a,&b); p=max(a,b); printf(“max=%d”,*p); } int *max(int x,int y) { if x>y) return (&x); else return (&y); }8.2.3 指向函数的指针8.2.3 指向函数的指针 一个函数包括一组指令序列,存储在某一段内存中,这段内存空间的起始地址称为函数的入口地址 称函数入口地址为函数的指针。函数名代表函数的入口地址 可以定义一个指针变量,其值等于该函数的入口地址,指向这个函数,这样通过这个指针变量也能调用这个函数。这种指针变量称为指向函数的指针变量。 定义指向函数的指针变量的一般形式为: 类型标识符(*指针变量名)( ); 例如: int (*p)(); /* 指针变量p可以指向一个整型函数*/ float (*q)(); /* 指针变量q可以指向一个浮点型函数*/null 刚定义的指向函数的指针变量,亦象其它指针变量一样要赋以地址值才能引用。当将某个函数的入口地址赋给指向函数的指针变量,就可用该指针变量来调用所指向的函数 给函数指针赋初值:将函数名(函数的入口地址值)赋给指针变量 例如 int m, (*p)( ); int max(int a,int b); 则可以 p=max; /* p指向函数max() */ 指针调用函数的 一般形式为: (*指针变量)( 实参表); 如上例:m=(*p)(12,22); /*比较 m=max(12,22); */注意:注意: 用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。 函数指针可以作为函数参数,此时,当函数指针每次指向不同的函数时,可执行不同的函数来完成不同的功能 例 8.4 函数max()用来求一维数组的元素的最大值,在主调函数中用函数名调用该函数与用函数指针调用该函数来实现。程序如下:程序如下: #include "stdio.h" #define M 8 main() {float sumf,sump; float a[M]={11,2,-3,4.5,5,69,7,80}; float (*p)(); /*定义指向函数的指针p*/ float max(float a[],int n); /*函数声明*/ p=max; /*函数名(函数入口地址)赋给指针p*/ sump=(*p)(a,M); /*用指针方式调用函数*/ sumf=max(a,M); /*用函数名调用max()函数*/ printf("sump=%.2f\n",sump); printf("sumf=%.2f\n",sumf); } 程序接上页:程序接上页:float max(float a[],int n) { int k; float s; s=a[0]; for (k=0;k *x){*x=*p;*y=i;} }8.3.2 二维数组与多维数组的指针表示法8.3.2 二维数组与多维数组的指针表示法1.二维数组的地址 设有一个二维数 组a,它有三行四列: int a[3][4]; 数组名a: 代表整个二维数组的首地址,也就是第0行的首地址。 a+i: 代表第i行的首地址。(见下页图) null数组名a代表整个二维数组的首地址:上图 a数组包含三个元素:a[0],a[1],a[2]. 而每个元素又 是一个一维数组,它包含4个元素(即4个列元素), 如:a[0]又包含:a[0][0],a[0][1],a[0][2],a[0][3].null一维数组名a[i]: 代表第i 行的首地址,即第i行中第0列元素的地址(既&a[i][0])。 a[i]+j: 代表第i行中的第j个元素的地址,即为&a[i][j]。 注意地址变化的单位数值在不同的场合的实际字节数是不同的: “a+1”中的“1”实际代表数组中一行元素所占的总字节数; “a[i]+1”中的“1” 代表数组中一个元素所占的字节数。 nulla代表第0行的首地址,a+1代表第1行的首地址,a+2代表第2行的首地址.每行存放4个整型数据(即1个元素占2个字节),因此,这里+1的含义是:+4*2=+8个字节.null 例8.6 用指针表示法输出二维数组的各元素。例8.6 用指针表示法输出二维数组的各元素。#include main() {static int a[2][3]={{0,1,2,},{3,4,5}}; int k,j,*p; for (j=0;j<2;j++) /* 方式1 */ { for (k=0;k<3;k++) printf("%5d",*(a[j]+k)); /* a[j]是j行首地址,a[j]+k是j行k列元素的地址*/ putchar(‘\n’); } putchar(‘\n’);接上页:接上页:for (j=0;j<2;j++) /* 方式2 */ { for (k=0;k<3;k++) printf("%5d",*(*(a+j)+k)); /* *(a+j)是j行首地址,*(a+j)+k是j行k列元素的地址*/ putchar(‘\n’); } p=a; /* p指向数组的第一个元素 */ for (j=0;j<2;j++) /* 方式3 */ { for (k=0;k<3;k++) printf("%5d",*(p++)); /* 输出p所指示的元素 */ putchar(‘\n’); } }null 输出的结果是: 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5null2.指向二维数组的指针变量 有两种情况:一是直接指向数组元素的指针变量; 二是指向一个含有m个元素的一维数组。 这两种不同形式的指针变量,其使用方法不同。 指向数组元素的指针变量,即简单指针变量,如: int *p,a[3][4]; p=&a[1][2]; (2) 指向由m个元素组成的一维数组的指针变量 定义形式: 类型 (*指针变量)[元素个数] 如: int (*p)[4]; null注意: (1)int (*p)[4]; 定义一个指针变量p ,p 指向包含4个元素的一维数组。 (2)p+i与 *(p+i)的区别: p+i是指向第i行的指针(第i行的首地址); *(p+i)是指向第i行第1个元素的地址; 两者数值相等,但含义不同:p+i 的增值将以行长为单位,而*(p+i)增值将以元素长度为单位。 null即:p+i+1将指向第i行再下一行的首地址,而*(p+i)+1将指向第i行首元素的下一个元素地址。(见下图) 设 int a[3][4],(*p)[4]; p=a; 如果p先指向a[0],则p+1不是指向a[0][1],而是指向a[1]null 例8.7 main( ) {static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4],i,j; p=a; scanf("i=%d,j=%d",&i,&j); printf("a[%d][%d]=%d\n", i, j, *(*(p+i)+j) ); }上节回顾上节回顾1、指向数组的指针的数据类型; 2、指向数组的指针的初始化; 3、利用指针法进行数组元素的引用; 4、指向数组的指针的移动; 5、数组越界问 题 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 。8.4 指针与字符串8.4 指针与字符串8.4.1 字符串的指针表示法 回顾:利用字符数组完成字符串编程。 1、字符数组的定义及初始化:例如:char str[]=“student”; 2、字符数组的输入/输出:A)逐个字符的输入/输出; B)字符串整体的输入/输出。3、字符串中各个字符的引用。null1、字符指针变量的定义:char *指针变量 ; 如:char *p; 2、字符指针的初始化: A)在定义时初始化指针变量使指针指向一个字 符串。 例如:char *string=“I Love China!” ; B)先定义后初始化。 例如:char *str ; str=“I love china” ; 思考:能否char str[20]; str=“I love china”; 以上语句的含义: 定义str为指针变量,它指向字符型数据,且赋值语句把字符串“I love china ”的首地址赋给了指针变量str。 null3、利用字符指针实现字符串的整体输入/输出 A)输入: char a[20],*str=a; 注意:char *str; scanf(“%s”,str); scanf(“%s”,str); × B )输出: char *str=“student”; printf( “%s\n”, str );(为何不用*str?如用*str, 程序应如何变化?) 对字符串的整体输出实际上还是从指针所指示的字符开始逐个显示,直到遇到字符串结束标志符‘\0’为止。而在输入时,亦是将字符串的各字符自动顺序存储在p指示的存储区中,并在最后自动加上 ‘\0’。 4、用指针变量来实现对字符串的访问 利用字符指针对字符串的访问与利用指向数组的指针来访问各个数组元素的方法相同,需要注意的是字符串结束标志‘\0’的使用。4、用指针变量来实现对字符串的访问 利用字符指针对字符串的访问与利用指向数组的指针来访问各个数组元素的方法相同,需要注意的是字符串结束标志‘\0’的使用。 例:将一字符串中大写英文字母转换成小写字母。main() {char a[80],*str=a; scanf("%s",str); for(;*str!='\0';str++) if(*str>='A'&&*str<='Z')*str+=32; printf("%s\n",a);}思考:为什么不用str输出? null小 结 1、掌握字符指针的定义及初始化; 2、理解字符指针与字符串的指向关系; 3、利用字符指针引用字符串或其中某个字符; 4、注意字符指针移动后指向的变化; 5、注意‘\0’标志的作用。null思考题: 将一已知字符串第n个字符开始的剩余字符复制为另一新的字符串。 分析:1、不要求利用函数调用进行程序设计; 2、由编程要求可以分析出所用的数据结构; 3、算法: 1)利用一字符指针p指向原始字符串首地址; 2)根据输入的数字决定p的移动位移; 3)利用循环从p所指向字符开始复制。 nullmain() {int i,n; char a[]="computer"; char b[10],*p,*q; p=a; q=b; scanf("%d",&n); if (strlen(a)>=n) p+=n-1; /*指针指到要复制的第一个字符 */ for (;*p!='\0';p++,q++) *q=*p; *q='\0'; /* 字符串以’\0’ 结尾 */ printf("String a : %s\n",a); printf("String b : %s\n",b); } null输入: 3↙ 输出: computer mputer 考虑一下,若输出语句改为如下语句会如何? printf(“string a is :%s\n”,p); printf(“string b is %s\n”,q); 8.6 程序举例8.6 程序举例例1 编写一个交换两个变量的函数,在主程序中 调用,实现两个变量值的交换。(假定a和b两个变量) 分析: 此程序要求进行模块化程序设计,其中main函数完成数据的输入及结果的输出并进行交换函数(swap)的调用,swap函数完成两个变量值的交换。 编程的重点在于考虑如何从swap函数中得到两个返回值及main函数和swap函数参数的传递方法。 null void swap(int x,int y) {int temp; temp=x; x=y; y=x;} main() {int a,b; scanf(“%d%d”, &a,&b); swap(a,b); printf(“%d%d”,a,b);} void swap(int *x,int *y) {int *temp=NULL; temp=x; x=y; y=x;} main() {int a,b,*p,*q; scanf(“%d%d”, &a,&b); p=&a;q=&b; swap(p,q); printf(“%d%d”,a,b);} void swap(int *x,int *y) {int temp; temp=*x; *x=*y; *y=temp;} main() {int a, b,*p,*q; scanf(“%d%d”, &a,&b); p=&a;q=&b; swap(p,q); printf(“%d%d”,a,b);} 两点说明:两点说明: (1) 若在函数体中交换指针变量的值,实参a、b的值并不改变,指针参数亦是传值。 例如: int *p; p=p1; p1=p2; p2=p; 不要希望如此完成处理。 (2) 函数中交换值时不能使用无初值的指针变量作临时变量。 例如: int *p; *p=*p1; *p1=*p2; *p2=*p; p无确定值,对 p的使用可能带来不可预期的后果。例2:编制求一个整型数组中所有偶数元素和及奇数元素和的函数并在main函数中输出。 例2:编制求一个整型数组中所有偶数元素和及奇数元素和的函数并在main函数中输出。 分析: 1、数组元素的输入; 2、程序结果的输出; 3、函数调用时参数传递的方式; 4、算法:遍历所给数组的全部元素; 如果某个数组元素为偶数则求和并将结 果存入偶数和变量sum1,反之求和后存 入奇数和变量sum2中。null main() {int a[10]={1,21,10,34,45,23,11,9,22,18}, sum1=0,sum2=0; sum(&sum1,&sum2,a); printf(“%d%d\n”,sum1,sum2);} void sum(int *x,int *y,int *z) {int i; for(i=0;i<10;i++) {if(*(z+i)%2= =0)*x+=*(z+i); else *y+=*(z+i);} }null例3:编制一函数,其功能是将一个字符串中所有数字符号删除形成一个新字符串并在main函数中输出。分析: 1、在main函数中完成待处理字符串的输入及结果输出; 其中新产生的字符串用另外的字符数组存放; 2、考虑函数调用时参数的传递方式; 3、算法:在函数del中遍历待处理字符串,找到非数字 符号即存入结果数组中。null main() {char str1[20],str2[20]; gets(str1); del(str1,str2); puts(str2);} void del(char *p,char *q) {for(;*p!=‘\0’;p++) if(!(*p>=‘0’&&*p<=‘9’))*q++=*p; *q=‘\0’; }null本 章 小 结1、指针是一种存放地址的变量,像其他的变量一样,须在使用前定义。指针变量的命名遵循与其他变量相同的命名规则。指针变量定义格式如下:类型名 *指针变量名; 2、两个与指针变量有关的运算符:&(取地址运算符)和 *(指针运算符)。其中:&运算符只能作用于变量,包括基本类型变量和数组的元素,不能作用于数组名和常量,其结果是取运算对象的存储地址。*运算符也称间接访问运算符,其操作对象是地址,通过对该地址的间接访问达到存取变量值的目的。 null 4、可以利用指针变量方便地对数组进行存取。如果指针 变量p的初值为数组a的首地址,则p+i和a+i就是a[i] 的地址,*(p+i)和*(a+i)既是数组元素a[i]的值。 3、C语言规定向被调函数进行参数传递只能是单向值传 递即传值的方式,采向被调函数传指针变量的方式则 变相地实现了参数传递中非常重要的一种方式——传 地址,从而使得主调函数与被调函数发生了某种意义 上的关联,这样使得程序的编制更加灵活,比如我们 可以通过这种参数传递方式从被调函数返回多个值。 null● 5、指针指向字符串时,是把字符串的首地址赋 给字符指针。可以通过*运算符对字符指针进行操 作以达到操作字符串各个字符的目的,具体操作方 法与利用指向数组的指针相同。