自学C语言-10

第10章 指针

	指针是C语言的一个重要组成部分,是C语言的核心、精髓所在。用好指针,可以在C语言开发中起到事半功倍的效果。一方面,可以提高程序的编译效率、执行速度,以及动态存储分配;另一方面,可使程序更加灵活,表示和操作各种数据结构更便捷,编写出高质量的应用程序。
	本章的知识架构及重难点如下:
	![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f4e20eaa276842cb858064652fabaff1.png#pic_center)

在这里插入图片描述

10.1 指针的相关概念

	指针是C语言的独特之处,其使用起来十分灵活。使用得当,能显著提高某些程序的效率;使用不当,则很容易造成系统错误。许多程序“挂死”往往都是错误地使用指针造成的。

10.1.1 地址与指针

要想弄明白什么是指针,就必须弄清楚数据在内存中是如何存储的,又是如何被读取的。
通常来说,系统会按字节对每个内存单元进行编号,这些内存单元就好比是许多带有编号的小房间,要想使用内存,就需要知道房间编号。例如,定义一个整型变量i需要4个字节,编译器就会为其分配4个内存单元,如图10.1所示,1000~1003就是对应变量i的4个内存单元地址,其内存放的数值是0。假设又定义了一个整型变量j,则j在内存中的起始地址是1004,因为j同样占4个字节,所以1004~1007单元中保存的是j,其值是1。同理,1008~1012、1012~1016、1016~1020也同样是3个整型变量对应的内存空间,如图10.2所示。
	可见,编译系统为每个变量都分配了一个能满足其类型大小的内存单元地址,访问该地址就能找到对应变量。那么,程序设计中,又该如何访问变量的地址呢?
	前面我们学习过取地址符“&”,其作用是获取变量在内存中的地址。例如,&i表示变量i的内存起始地址1000。通过&i可以访问变量i指向的内存单元,进而读取变量i。如图10.3所示,“指向”某个变量地址用箭头表示,就好像旅店住宿时某个房间号牌指向某个房间一样。
	![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/3aae38d6428a405b9ece5b93c0712604.png#pic_center)

在这里插入图片描述为了好记忆,我们将变量的地址形象化地称为该变量的“指针”,意思是通过它能访问以它为地址的内存单元。例如,通过地址1000可以访问变量i的地址,进而读取4个字节得到i的值。

10.1.2 变量与指针

	变量的地址是变量和指针之间的连接纽带。所谓“指向”,是通过地址来体现的。因为指针变量通常指向一个变量的地址,所以将一个变量的地址赋给指针变量后,这个指针变量就“指向”了该变量。例如,将变量i的地址&i存放到指针变量p中,p就指向i,其关系如图10.4所示。
	![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a0f16b11efcf43a2ae369b001d74a6aa.png#pic_center)

在这里插入图片描述

	程序代码中一般通过变量名对内存单元进行存取操作,但代码编译后,会将变量名转换为该变量在内存中的存放地址,后续对变量值的存取都是通过该地址进行的。例如,对图10.2中的变量i和变量j进行如下求和操作:
i+j;
计算机内部的存取流程是:根据变量名与地址的对应关系,找到变量i的地址1000,然后从1000开始读取4个字节数据放到CPU寄存器中,再找到变量j的地址1004,从1004开始读取4个字节的数据放到CPU的另一个寄存器中,通过CPU的加法中断计算出结果。
低级语言(如汇编语言)中一般直接通过地址来访问内存单元,高级语言中一般使用变量名来访问内存单元。C语言作为高级语言,仍然提供了通过地址来访问内存单元的方式。

10.1.3 指针变量

一个变量的地址称为该变量的指针。如果有一个变量专门用来存放另一个变量的地址,它就是指针变量。在C语言中有专门用来存放内存单元地址的变量类型,即指针类型。

1.指针变量的定义

定义指针变量的一般形式如下:

类型说明   * 变量名

其中,“*”表示该变量是一个指针变量,“变量名”即为定义的指针变量名,“类型说明”表示本指针变量所指向的变量的数据类型。

2.指针变量的赋值

	指针变量同普通变量一样,使用前需要先进行定义并赋予具体的值。未经赋值的指针变量不能使用。与其他变量赋值不同,给指针变量赋值只能赋予地址,而不能是其他数据,否则将引起错误。C语言一般用“&变量名“表示某个变量的地址,如&a表示变量a的地址,&b表示变量b的地址。
	给一个指针变量赋值可以有以下两种方法。
	**(1)定义指针变量的同时进行赋值。例如:**
int a;
int *p=&a;

(2) 先定义指针变量,之后再赋值。例如:

int a;
int *p;
p=&a;
注意:
注意这两种赋值语句的区别。如果先定义指针变量之后再赋值,赋值时指针变量前不再加"*"

【例10.1】输出某个数的地址 从键盘中输入一个数,利用指针将这个数的地址以十六进制形式输出。实现代码如下:
在这里插入图片描述
程序运行结果如上图所示。
**

3.指针变量的引用

**
引用指针变量是对变量进行间接访问的一种形式。引用指针变量的形式为“ *指针变量",其含义是引用指针变量所指向的值。

误区警示
	没有初始化的指针变量俗称“野指针”,使用时容易产生错误(导致不合法的内存空间)。良好的编程习惯是在定义指针变量时就将其初始化为NULL,由于NULL处禁止写入,所以一旦有错误,可以将错误造成的危害降到最小。

【例10.2】比较两个数的大小
使用指针比较两个数的大小,实现代码如下:
在这里插入图片描述
程序运行结果如上图所示。

4.“&”和“*”运算符

运算符"&"和”*"都是单目运算符。”&“是取地址运算符,用于返回一个操作数的地址。”*”是指针运算符,用于返回指定地址内保存的变量值。
例如,“p=&i;"是将变量i的内存地址赋给指针变量p。”q=*p;"是将指针p指向的变量,即变量i的值赋给q,假如变量i的值是5,则q的值也是5。

5.“&”和"&"的区别

下面通过两条语句来分析“&”和“&”运算的区别。如果有如下语句:

p=&a;

因为“&”和“”的优先级相同,按自右而左的方向结合,因此“&p"先进行”“运算,"p"相当于变量a;再进行“&”运算,“&p"就相当于取变量a的地址。”&a"先进行“&”运算,“&a"就是取变量a的地址,然后执行”“运算,”&a"就相当于取变量a所在地址的值,实际就是变量a。
【例10.3】输出i,j, c的地址 本实例定义了3个指针变量,使用”&
"计算c=i+j, 计算后输出变量i,j, c的地址值。
在这里插入图片描述
程序运行结果如上图所示。
【例10.4】求水对杯子产生的压强 杯子的底面积是60平方厘米,杯中装上8厘米高的水,杯子和水的总质量为0.6千克(水的密度是每立方米10^3千克,重力加速度g的值为每千克10牛顿),利用”*&“计算水对杯子产生的压强。
在这里插入图片描述
程序运行结果如上图所示。

10.1.4 指针的自增、自减运算

	指针的自增自减运算不同于普通变量的自增自减运算,也就是说,并非简单地加1减1,而是按照它所指向的数据类型的直接长度进行增减。下面通过一个例子,读者仔细揣摩一下。
	【例10.5】输出整型变量地址			定义指针变量和整型变量,并将整型变量的地址赋值给指针变量,再进行指针自增,输出结果,比较自增前后的数值,看看能得出什么结论。代码如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d795b56347914030b144f5aded1e8709.png#pic_center)

在这里插入图片描述

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1f016dfc7d42402e9b2a50749a1ce4b6.png#pic_center
程序运行结果如上图10.9所示。若将上例代码改成如下:
在这里插入图片描述
程序运行结果如上图10.10所示。可以看到,指针自增后,两段代码输出的地址并不相同。这是因为:基本整型变量i在内存中占4个字节,指针p是指向变量i的地址的,这里的p++不是简单地在地址上加1,而是指向下一个存放基本整型数的地址。图10.9所示的结果是因为变量i是基本整型,所以执行p++后,p的值增加4(即4个字节); 图10.10所示的结果是因为i被定义成了短整型,所以执行p++后,p的值增加了2(即2个字节)。
指针自增自减运算,是按照它所指向的数据类型的直接长度进行增或减。可以将例10.5第1段代码用图10.11来形象地表示出来。
在这里插入图片描述

编程训练

训练1:答案究竟在哪儿				小Z是班里成绩最好的同学,大家都喜欢以他的作业为范本核对答案。一天A找Z对答案,Z说答案在Y那里,Y说答案是10。利用指针模拟这种情况,并将答案输出。

训练2:展示彩灯点亮情况 利用指针自减运算,模拟彩灯的亮灭情况。假设数字0表示灯泡没亮,数字1表示灯泡亮着,有6个灯泡排列成一行组成一个一维数组a={1,0,0,1,0,0},查找倒数第一个亮着的灯泡位置,并显示灯泡前一个灯泡是否亮着。运行结果如下:

0表示灯灭了,1表示灯亮
倒数第一个亮着的灯泡是:倒数是第3个
前一个灯泡没亮

10.2 数组与指针

使用数组时,系统需要提供一段连续的内存来存储数组中的各元素,如果把数组的地址赋给指针变量,就可以通过指针变量来引用数组,下面就介绍如何用指针来引用一维数组及二维数组元素。

10.2.1 一维数组与指针

定义一维数组时,系统会在内存中为其分配一段存储空间,数组名就是数组在内存中的首地址 。若再定义一个指针变量,并将整数组的首地址传给指针变量,则该指针就指向了这个一维数组。例如:

int *p,a[10];
p=a;

这里a是数组名,也就是数组的首地址,将它赋给指针变量p,也就是将数组a的首地址赋给p。也可以写成如下形式:

int *p, a[10];
p=&a[0];

上面的语句是将数组a中首个元素的地址赋给指针变量p。由于a[0]的地址就是数组的首地址,因此上述两条赋值语句的操作效果完全相同。
【例10.6】输出100以内开始5个数和后5个数 定义两个指针变量,再定义两个一维数组,将两个数组元素分别赋给两个指针。利用for循环输入两个数组的值(100以内开始5个数和后5个数),再用for循环和指针输出数组值。代码如下:
在这里插入图片描述
程序运行结果如上图所示。
如下两条语句,都表示将数组首地址赋给指针变量。

p=&a[0];
q=b;

☑p+n与a+n都用于表示数组元素a[n]的地址,即&a[]。对整个a数组来说,共有5个元素,n的取值为0~4,则数组元素的地址就可以表示为p+0到p+4或者a+0到a+4。
☑*(p+n)和*(a+n)都用于表示数组元素a[n]。语句“printf(“%5d”,(p+i));“和“printf(”%5d",(q+i);"表示输出数组a、b中对应的元素。
例10.6中使用指针指向一维数组及通过指针引用数组元素的过程可以通过图10.13和图10.14来表示。
在这里插入图片描述
用a+n表示数组元素的地址,*(a+n)表示数组元素,可以将例10.6中输出数组部分的程序代码改成如下形式(运行结果不变)。

printf("array a is:\n");
for(i=0;i<5;i++)
	printf("%5d",*(a+i));					/*利用*(a+i)形式输出数组a*/
	printf("\n");
	printf("array b is:\n");
	for(i=0;i<5;i++)
		printf("%5d",*(b+i));				/*利用*(b+i)形式输出数组b*/
	printf("\n");

☑表示指针的移动可以使用“++”和“–”这两个运算符。
例如,利用“++”运算符可将数组输出部分的代码改写成如下形式:

printf("array a is:\n");
for(i=0;i<5;i++)
	printf("%5d",*p++);						/*利用*p++形式输出数组a*/
printf("\n");
printf("array b is:\n");
for(i=0;i<5;i++)
	printf("%5d",*q++);						/*利用*q++形式输出数组b*/
printf("\n");

还可对例10.6程序再进一步改写,将数组的输入、输出都采用指针自增运算实现。改写后的程序代码如下:
在这里插入图片描述
比较上面的程序会发现,如果在给数组元素赋值时使用了指针自增运算,而且在输出数组元素时也使用指针变量,则需要加上如下语句:

p=a;
q=b;

这两行语句的作用是将指针变量p和q重新指向数组a和数组b在内存中的起始位置。若没有该语句,直接使用"*p++" "*q++"的方法进行输出,将会产生错误。

10.2.2 二维数组与指针

		定义一个3行5列的二维数组,其在内存中的存储形式如图10.15所示,从中可以看到多种表示二维数组中元素地址的方法。
		对于一个m行n列的二维数组,其元素地址的表示方法如下。
		☑a表示二维数组的首地址,也表示数组第1行的首地址,a+1表示第2行的首地址,a+m表示第m+1行的首地址。
		☑a[0]+n表示数组第1行第n+1个元素的地址,a[m]+n表示第m+1行第n+1个元素的地址。
		☑&a[0]表示数组第1行的首地址,&a[m]表示第m+1行的首地址。
		☑&a[0][0]既可以表示数组第1行1列的首地址,也可以看作整个数组的首地址。&a[m][n]就是第m+1行n+1列元素的地址。
		指针也可表示地址,因此通过指针可以引用二维数组中的元素。
		☑*(*(a+m)+n)和*(a[m]+n)含义相同,都表示数组第m+1行第n+1列元素。
		【例10.7】输出3*5矩阵         利用指针对二维数组进行输入和输出,具体代码如下:
		![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a071bce766314522b1866c9c06ab53c4.png#pic_center)

在这里插入图片描述
程序运行结果如上图所示。
在这里插入图片描述
在运行结果相同的前提下,可将程序改写成如下形式:
在这里插入图片描述
【例10.8】输出第3行元素 将一个3行5列二维数组的第3行元素输出,具体代码如下:
在这里插入图片描述
程序运行结果如上图所示。
也可以采用*(a+i)+j形式输入二维数组元素,代码如下:

scanf("%d",*(a+i)+j);				/*使用*(a+i)+j形式输入二维数组元素*/

同样可以采用*(a+i)+j形式输出二维数组元素,代码如下:

printf("%5d",*(*(a+i)+j);		  /*使用*(a+i)+j形式输出二维数组元素*/
技巧:
利用指针引用二维数组的要记住*(a+i)与a[i]是等价的。

10.2.3 字符串与指针

可以通过两种方式访问一个字符串,一是使用字符数组,二是使用字符指针。
【例10.9]使用指针输出字符串“hello Microsoft ",具体代码如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/05c4180029c04c37a7b0f8fa3b5867e5.png#pic_center)

在这里插入图片描述
程序运行结果如上图所示。首先定义了字符型指针变量string,用字符串常量”Hello Microsoft"为其赋初值。注意,这里并不是把“Hello Microsoft"中的所有字符存放到string中,只是把该字符串中第一个字符的地址赋给指针变量string,如图10.19所示。
在这里插入图片描述

语句"char *string="Hello Microsoft";"等价于下面两条语句:
char *string;
string="Hello Microsoft";

【例10.10】实现字符串复制功能 不使用string.h函数库中的函数,利用指针实现字符串复制功能,具体代码如下:
在这里插入图片描述
程序运行结果如上图所示。自定义函数中,参数为两个指向字符型数据的指针变量。利用while循环,先将str1指向的内容赋给str2指向的元素,然后str1和str2分别加1,指向下一个元素,直到*str1的值为“\0"为止。
这里有一点需要注意,就是str1和str2的值是同步变化的,如下图10.21所示。若str1处在str11的位置,str2就处在str21的位置;若str1处在str12的位置,str2就处在str22的位置。
在这里插入图片描述

10.2.4 指针数组

字符串数组有别于8.3节中学过的字符数组。字符数组是一个一维数组,而字符串数组是以字符串作为数组元素的数组,可以将其看成一个二维字符数组。下面定义一个简单的字符串数组:

char country[5][20]=
{
	"China",
	"Japan",
	"Russia",
	"Germany",
	"Switzerland"
}
	

上述代码中,字符型数组变量country被定义含有5个字符串的数组,每个字符串的长度都必须小于20(这里要考虑字符串最后的“\0")。
通过观察上面定义的字符串数组可以发现,像”China"和“Japan"这样的字符串的长度仅为5,加上字符串结束符也仅为6,而内存中却要给它们分别分配一个20字节的空间,空间浪费较大。为了解决这个问题,可以使用指针数组,使每个指针指向所需要的字符常量。这种方法需要在数组中保存字符指针,虽然也占用空间,但要远少于字符串数组占用的空间。
什么是指针数组呢?一个数组,如果其元素均为指针类型数据,则该数组称为指针数组。也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式如下:

类型名		数组名[数组长度]

【例10.11】汉译英填空 英语小测,有一题是根据汉语填写英语。语文、数学、英语、化学、生物、物理的英语分别为Chinese, math、English、chemistry、biology和physics,用指针数组输出对应的英文。
在这里插入图片描述
程序运行结果如上图所示。
编程训练
训练3:最受欢迎的座位号 某班级有5*5个座位,输出位置最好的一行座位号。(提示:第2行最受欢迎)。
训练4:输出12个月份 定义指针数组并赋初值,将12个月份输出。运行结果如下:

January
February
March
April
May
June
July
August
September
October
November
December

10.3 指向指针的指针

	一个指针可以指向整型变量、实型变量、字符型变量,当然也可以指向另一个指针,此时称其为指向指针的指针。如图10.23所示,整型变量i的地址是&i,将其值传递给指针变量p1,则p1指向i; 同时,将p1的地址&p1传递给p2,则p2指向p1。这里的p2就是指向指针变量的指针。
	指向指针的指针定义如下:
类型标识符   **指针变量名;

例如,”int **p; “的含义为定义一个指针变量p,它指向另一个指针变量,该指针变量又指向一个基本整型变量。由于指针运算符“”自右至左结合,所以上述定义相当于“int(p); "。
知道如何定义指向指针的指针后,可以将图10.23用图10.24更形象地表示出来。
在这里插入图片描述
【例10.12】输出化学周期表中的金属元素 利用指向指针的指针,输出化学周期表前20个元素中的金属元素,具体代码如下:
在这里插入图片描述
程序运行结果如上图所示。
【例10.13】统计偶数个数 利用指向指针的指针输出一维数组中其值为偶数的元素,并统计偶数的个数,具体代码如下:
在这里插入图片描述
程序运行结果如上图所示。
程序中,将数组a的首地址赋给指针变量p1,又将指针变量p1的地址赋给p2,要通过这个双重指针变量p2访问数组中的元素,就要一层层地来分析。首先看
p2的含义,p2指向的是指针变量p1所存放的内容,即数组a的首地址,要想取出数组a中的元素,就必须在p2前面再加一个指针运算符”*"。
根据前面讲过的指针的用法,还可以将例10.14改写成如下形式:
在这里插入图片描述
编程训练
训练5:模拟网上买衣服的场景 小红想要在淘宝上买件衣服,因为她有某卖家的优惠券,所以联系客服找她想要买的衣服。于是客服给她一个链接,找到衣服的价格是559元。模拟场景,找到衣服价格。(提示:使用int **p)。运行结果如下:

衣服的价格是:
559

训练6:给名字排序 自定义一个排序的函数,在主函数中调用这个函数同时采用指针的指针输出排序后的名字,运行结果如下:

一共有5个名字,分别是:
赵小平
周芷若
宇文玥
南乔
楚小乔
按照姓名排序之后分别是:
楚小乔
南乔
宇文玥
赵小平
周芷若

10.4 指针变量作函数参数

通过前面的介绍可知,整型变量、实型变量、字符型变量、数组名和数组元素等均可作为函数参数。此外,指针型变量也可以作为函数参数,这里具体进行介绍。
【例10.14】交换两个变量的值			在本实例中,用指针做函数参数,交换两个变量的值。在主函数中,调用自定义函数,利用指针将两个数交换,具体代码如下:

在这里插入图片描述
程序运行结果如上图所示。swap函数 用户自定义函数,在main函数中调用该函数交换变量a和b的值,swap函数的两个形参被传入了两个个地址值,也就是传入了两个指针变量。在swap函数的函数体内使用整型变量tmp作为中间变量,将两个指针变量所指向的数值进行交换。在main函数内首先获取输入的两个数值,分别传递给变量x和y,调用swap函数将变量x和y的数值互换。
如果swap函数的形参不是指针而是普通整型,调用swap函数后能实现两数互换吗?将前述程序改成如下形式:
在这里插入图片描述
【例10.15】将3个数按降序输出 首先定义交换函数swap,参数是指针变量;然后再定义比较大小的函数exchange,参数也是指针变量,并且在exchange函数中嵌套交换函数swap。在主函数中,调用exchange函数将输入的3个数按降序输出,具体代码如下:
在这里插入图片描述
程序创建了一个自定义函数swap,用于交换两个变量的值。还创建了一个exchange函数,其作用是将3个数由大到小排序,在exchange函数中调用swap函数。这里,swap和exchange函数都是以指针变量作为形参。程序运行时,通过键盘输入3个数a,b,c,分别将a、b、c的地址赋给q1、q2、q3,调用exchange函数,将指针变量作为实参,将实参变量的值传递给形参变量,此时q1和pt1都指向变量a,q2和pt2都指向变量b,q3和pt3都指向变量b,q3和pt3都指向变量c;在exchange函数中又调用了swap函数,当执行swap(pt1,pt2)时,pt1也指向了变量a,pt2指向了变量b,这一过程如下图所示。
在这里插入图片描述
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式。指针变量作函数参数也是如此,调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
下面介绍如何使用指向数组的指针变量作为函数参数。这里,形参和实参均为指针变量。
【例10.16】计算新生总人数 利用指针变量作函数参数编写程序,根据输入的各班人数求刚入学的初中新生的总人数。
在这里插入图片描述
程序运行结果如上图所示。在自定义函数SUM中使用指针变量作形式参数,在主函数中实际参数pointer是一个指向一维数组a的指针,虚实结合,被调用函数SUM中的形式参数p得到pointer的值,指向了内存中存放的一维数组。
冒泡排序是C语言中比较经典的例子,也是学习要应该牢牢掌握的一种算法,下面分析如何使用指针变量作为函数参数来实现冒泡排序。
【例10.17】实现冒泡排序 冒泡排序的基本思想:如果要对n个数进行冒泡排序,则要进行n-1轮比较,在第二轮比较中要从后到前进行n-1次两两比较,在第j轮比较中要进行n-j次两两比较。使用指针实现冒泡法排序,代码如下:

在这里插入图片描述
程序运行结果如上图所示。
前面两个实例都是用一个指向数组的指针变量作函数参数。在10.3节中介绍过指向指针的指针,这里就来通过一个实例介绍如何用指向指针的指针作函数参数。
【例10.18】水果首字母升序排名 大福超市员工布置水果区,店长要求按照水果名称升序的顺序摆放。各水果的名称及对应单价如下:
苹果(apple): 3.50 橘子(tangerine):2.50 柚子(grapefriu):3.00 香蕉(banana):2.00
橙子(orange):2.99 菠萝(pineapple):4.99 葡萄(grape): 5.00 火龙果(pitaia):6.80
编写程序,按照水果名称的首字母将水果升序排序。
在这里插入图片描述
程序运行结果如上图所示。
下面将通过一个二维数组使用指针变量作为函数参数的实例来加深对学习该部分的知识理解。
【例10.19】找出数组每行中最大数并求和 利用指针作函数参数,找出一个数组每行最大的数,并将这些数相加求和。具体代码如下:
在这里插入图片描述
程序运行结果如上图所示。现在来总结一下:
(1)数组名就是数组的首地址,因此例10.17中可以将数组名作为实参传递给指针型形式参数。
(2)当形参为数组时,实参也可以为指针变量。可将例10.17改写成如下形式:
在这里插入图片描述
上述程序中,形参是数组,实参是指针变量。注意倒数第3行语句:

p=a;											/*再次定义p为数组a的首地址*/

该语句不可少,如果将其省略,则后面调用order函数时,参数p指向的就不是数组a的首地址,从而导致程序运行出错,这点需要读者加以注意。
编程训练:
训练7:汇总一周收纳的钱数 自定义一个用指针变量作参数的函数,功能是将银行每周入账的钱数相加,然后在主函数中调用这个函数,运行结果如下:

请输入一周收纳的钱数:
2345000 677854 78959 567844 356789 56785 678458
面值的总数是:4761689

训练8:求长方体的侧面积 输入长、宽、高,求长方体的体积及正面、侧面、顶面面积。要求设计一个函数,参数为长方体长、宽、高的指针。运行结果如下:

正面面积是:20
侧面面积是:12
顶面面积是:15

10.5 返回指针值的函数

指针变量也可以指向一个函数。函数在编译时会被分配一个入口地址,该入口地址就被称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与之前介绍的类似,只是带回的值的类型是指针类型而已。返回指针值的函数简称为指针函数。
定义指针函数的一般形式如下:
类型名   *函数名(参数表列);

下面的定义中,fun是函数名,调用它以后能得到一个指向整型数据的指针。x和y是函数fun的形式参数,这两个参数均为基本整型。函数名前面有一个"*",表示此 函数是指针型函数。类型说明是int,表示返回的指针指向整型变量。

int *fun(int x, int y)

【例10.20】求长方形周长 输入长和宽,求长方形的周长,使用返回值为指针的函数解决。代码如下:
在这里插入图片描述
程序运行结果如上图所示。
注意,例10.20完全可以用前面学过的更简单的形式来实现,这里只是为了帮助大家更好地理解返回值为指针的函数及其应用。
编程训练:
训练9:计算零件总数 师徒两人合做一批零件,徒弟做了9个比师傅少做了21个,问这批零件共有多少个。将数据利用指针返回。运行结果如下:

请输入徒弟比师傅少做数值:
21
请输入徒弟做了零件的数值:
9
这批零件总数是:30

训练10:植树 三、四年级共植树128棵,四年级比三年级多植20棵,求两个年级各植树多少棵。
运行结果如下:

请输入总共植树的数量:
128
请输入四年级比三年级多植树的数量:
20
三年级植树的数量是:54
四年级植树的数量是:74

10.6 指针数组作main函数的参数

	main函数是运行所有程序的入口,由系统调用。当处于操作命令状态下,输入main所在的文件名,系统即调用main函数。
	main函数一般是没有参数的。但实际上,main函数也可以是有参函数,其带参形式如下:
main(int argc,char *argv[])

有两个参数,一个整型和一个指针数组。当一个C源程序经过编译、链接后,会生成扩展名为.exe的可执行文件,该文件可直接在操作系统下运行。对于main函数来说,其实际参数和命令是一起给出的,也就是一个命令行包括命令名和需要传给main函数的参数。命令行的一般形式如下:

命令名			参数1  		参数2   ......参数n

其中,“命令名”就是可执行文件的文件名,命令名和其后参数之间须用空格分隔。
命令行与main函数的参数间存在一定关系。假设命令行为:

file1 happy bright glad

这里,file1为文件名,就是file1.c经编译、链接后生成的可执行文件file1.exe,其后跟3个参数。以上命令行与main函数中的形式参数关系如下:参数argc记录了命令行中命令与参数的个数,这里包括file1、happy、bright和glad,共4个,指针数组的大小由参数的值决定,这里为char *argv[4],该指针数组的取值情况如下图所示。
在这里插入图片描述
利用指针数组作main函数的形参,可以向程序传送命令行参数。

说明:参数字符串的长度是不定的,参数数量也是任意的,并不规定具体个数。

【例10.21】输出main函数的参数形式
在这里插入图片描述
程序运行结果如上图所示。
编程训练
训练11:参数的接受度 设计一个程序,该程序接受命令行参数,输出“您好,[输入参数]“,如果没有参数,则输出”请输入你的名字:”,然后退出程序。
训练12:给程序加权限 利用main函数参数给程序添加权限判断,用户运行程序时需要输入账号和密码,并输出相应的提示。(提示:生成.exe文件,然后使用cmd运行)

10.7 实践与练习

综合练习1: 2022冬奥会口号 利用两种方式输出字符串:第一种是采用for循环遍历字符数组,输出字符串;第二种是利用指针直接输出字符串。运行结果如下:

***********************************************************	
2022年重大项目是:2022年北京冬季奥运会
2022年奥运会口号是:纯洁的冰雪,激情的约会
************************************************************

综合练习2:古诗词填空 春眠不觉晓,处处闻啼鸟。________________,花落知多少。利用指针将缺少的诗句输出在控制台上。运行效果如下:

答案是:
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
	夜来风雨声
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲

综合练习3:小猪过河 9只小猪渡河,只有一个木筏,一次能载3只猪,只有一只猪会划船。问至少往返几次,9只小猪才能全部渡过河。利用“*&”输出的结果。运行效果如下:

会划船的猪每次只能载2只猪过去
会划船的猪一直在船上
因此至少4次能全部渡过河

综合练习4:计算公路长度 某工程队修路,第一天修了600米,第二天修了全长的20%,第三天修了全长的25%,三天共修了全长的75%,求这条公路全长多少米,同时输出此公里数变量的内存地址。运行结果如下:

------------------------------------------
	公路全长是2000------------------------------------------
******************************************
	它的地址是:1703724
******************************************

综合练习5:统计单词数量 利用指针,统计“I have a dream".中的单词个数。运行结果如下:

¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤
	一共有4个单词
¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤

综合练习6:倒序输出数据 输入10个数,存放在数组中。编写程序,将数组中的元素值按照相反的顺序存放,运行结果如下:

请输入10个元素(用空格隔开):
88 56 45 25 66 95 86 33 16 2

正序输出结果:
	88 56 45 25 66
	95 86 33 16 2
倒序输出结果:
	2 16 33 86 95
	68 25 45 56 88

综合练习7:寻找逗号 使用指针寻找字符串”Life is brief, and then you die,you know?"中“,"的位置。运行结果如下:

,的位置是:13
 ,的位置是:31

综合练习8:模拟影院售票 某家电影院售票情况如图10.38所示,用指针求出剩余的电影票数。其中,1表示有座,0表示没座,统计1的数量就是统计剩余的电影票数。运行结果如下:

请输入电影院售票情况:
0 1 1 0
1 1 1 1
1 0 1 1
1 1 1 1
剩余的票数是:13

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

manyoftenvictory

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值