(九十三)函数与数组

当且仅当在函数头或者函数原型中:

例如:int *aint a[] 这两个的含义是相同的,指的是a是一个int指针。

 

但是在函数调用和函数内部,这两个的含义并不相同。

 

 

指针算数:

在之前的指针算数中说过,指针实际上就是一个地址,是指针指向的那个地址;

指针可以直接和一个int值相加,结果是指针指向的地址偏移。

 

在使用函数时,传递参数可以是一个地址,通过传递地址、以及成员数量,然后变相传递一个数组。

 

例如代码:

//关键词:随机受到多次伤害,并通报每次伤害的值。
#include<iostream>
#include<ctime>	//函数clock()使用
#include<Windows.h>	//函数Sleep()使用
using namespace std;
const int number = 5;	//常量number,方便修改次数
int dam(int*str, int cishu);	//总伤害=每次的伤害之和

int main()
{
	int abc[number];
	cout << "你受到了" << number << "次伤害,系统将随机计算这" << number << "次的伤害值:" << endl;
	for (int i = 0;i < number;i++)	//为数组循环赋值,利用循环数
	{
		double delay = rand() % 1000;	//随机一个rand值,范围为0~999。注意,这是伪随机数,所以每次随机的结果似乎都是一样的。
		cout << "等待" << delay / 1000 << "秒,计算下次伤害" << endl;
		Sleep(delay);	//等待rand值的毫秒
		abc[i] = clock() % 100;	//利用时钟除以100求余,计算0~99的随机值,并为数组成员赋值
		cout << "你受到的第一次伤害为:" << abc[i] << endl;	//通报数组成员的值
	}
	int total = dam(abc, number);	//定义total,传递的函数为数组abc(的地址),以及数组成员的个数number
	cout << "你总共受到了" << total << "点伤害。" << endl;
	cout << "sizeof总伤害的数组: " << sizeof(abc) << endl << endl;

	//计算前3次伤害
	total = dam(abc, 3);	//计算前3次伤害
	cout << "你前三次总共受到了" << total << "点伤害。" << endl;

	//偏移指针
	total = dam(abc + 2, 3);	//计算第3~5次伤害
	cout << "你第三次~第五次总共受到了" << total << "点伤害。" << endl;

	system("pause");
	return 0;
}

int dam(int*str, int m)
{
	int total = 0;
	for (int i = 0;i < m;i++)
		total = total + str[i];	//等于之前的和加现在的这个
	cout << "dam函数中的指针的地址:"<<str << endl;
	cout << "sizeof dam函数中的指针 = " << sizeof(str) << endl;
	return total;
}

输出:


你受到了5次伤害,系统将随机计算这5次的伤害值:
等待0.041秒,计算下次伤害
你受到的第一次伤害为:10
等待0.467秒,计算下次伤害
你受到的第一次伤害为:80
等待0.334秒,计算下次伤害
你受到的第一次伤害为:16
等待0.5秒,计算下次伤害
你受到的第一次伤害为:18
等待0.169秒,计算下次伤害
你受到的第一次伤害为:89
dam函数中的指针的地址:003FFBEC
sizeof dam函数中的指针 = 4
你总共受到了213点伤害。
sizeof总伤害的数组: 20

dam函数中的指针的地址:003FFBEC
sizeof dam函数中的指针 = 4
你前三次总共受到了106点伤害。
dam函数中的指针的地址:003FFBF4
sizeof dam函数中的指针 = 4
你第三次~第五次总共受到了123点伤害。
请按任意键继续. . .

 

总结:

①想给函数传递一个数组,那么就先传递数组的地址,再传递数组成员的个数。——相当于把一个数组分成两部分。

 

②数组名,实际上就是数组的地址,也是一个指针;

 

③对指针加减实际上是使指针指向地址的偏移(若干个指针类型的宽度);

 

④由于传递给函数的参数是地址,且是int或者其他类型的指针变量,所以,sizeof这个形参,值为类型的宽度,而不是数组的宽度。

而实参的sizeof是数组的宽度,一般要比形参大。

 

⑤给数组名加上“&”则显示的是指针地址;

直接输出指针,显示的也是地址;

如果不偏移指针的话,显示的是偏移前的指针地址;

偏移指针的话,显示的是偏移后的指针地址。

 

不能函数头中使用例如int a[5]来传递数组,只能使用int*a来传递地址;

 

 

将传递的参数设为只读:

在不用指针的情况下,传递给函数的是一个副本变量(形参),形参的改变不影响实参。

 

在使用指针的情况下,传递的参数是指针,因此可以通过操作指针来对值进行修改,这在某些时候是有用的。

 

然而,在某些情况下,我们希望不要修改传递给函数的值,这个时候,我们需要使用的是关键字const,例如void abc(const int a);这样

如代码:

#include<iostream>
using namespace std;

void abc(const int*,int);
void def(int*, int);

int main()
{
	const int m = 2;
	int a[m] = { 5,10 };
	abc(a, m);
	cout << a[0] << endl;
	cout << a[1] << endl;
	def(a, m);
	cout << a[0] << endl;
	cout << a[1] << endl;
	system("pause");
	return 0;
}

void abc(const int*x, int y)	//假如这里不使用const,那么这个函数里面的两句代码就可以执行
{
	//x[0] = 1;
	//x[1] = 15;
}

void def(int*x, int y)	//这里没有使用const,于是可以修改值
{
	x[0] = 1;
	x[1] = 15;
}

输出:


5
10
1
15
请按任意键继续. . .

总结:

①使用const的情况下,函数abc内的两行代码是无法执行的,数组也无法被修改。

 

②不使用const的时候,函数def内的两行代码可以执行,于是可以通过修改指针指向的地址的值,来修改原有的值。

 

③假如使用const来传递参数,那么如果函数想修改传递的参数,如函数abc那样,编译器会警告错误。

 

④使用const并不意味着数组成员是常量(在def中被修改),只是表示在当前函数(如abc)中不能被修改;

 

 

 

使用数组区间的函数:

简单来说,就是一个数组,然后传递给函数两个参数:

第一个是他的起始地址(通过指针1);

第二个是终止地址(通过指针2)。

然后在函数里,例如通过for的循环更新部分,指针每次+1(假如需要偏移一个位置的话,有必要也可以加2或者其他);

直到函数的条件判断部分遇见属于终止地址(指针2)。

 

如代码:

#include<iostream>

using namespace std;
int sum(int*第一个指针, int*指向最后一个值的指针,int 数组中隔几位相加);	//用于计算和
void shuzu(int*, int);	//用于生成数组

int main()
{
	int number;
	int in;
	cout << "请输入你需要数组的成员个数:";
	cin >> number;
	cout << "请需要你跳着计算他们的和么?例如从第一个开始,连续相加则输入0,只加第一、三、五个等这样(即隔1个)请输入1,依次类推。" << endl;
	cout << "请输入:";
	cin >> in;
	int *a = new int[number];	//new一个动态数组,数组成员个数为number(非char数组,所以不用最后一位为空字符\0)
	shuzu(a, number);	//调用shuz函数,为数组填充数字
	int total = sum(a, a + number, in);	//调用计算和的函数
	cout << "total = " << total << endl;
	system("pause");
	return 0;
}

void shuzu(int*x, int y)	//数组创建函数
{
	cout << "现在生成数组中的成员的值ing..." << endl;
	for (int i = 0;i < y;i++)
	{
		x[i] = rand() % 20;	//这是伪随机,因为未更改随机数种子,所以每次结果都一样
		cout << "第" << i + 1 << "个数为" << x[i] << endl;
	}
}

int sum(int*start, int*end, int number)	//数组隔若干位相加,并求和
{
	int total = 0;
	int n = end - start;	//n是两个指针之间的距离
	for (int i = 0, j = 0;start != end&&j < n;start = start + 1 + number, j = j + 1 + number)	//每次指针start偏移1+nummber距离,number是额外偏移量。j是用来避免超出end指针范围的
	{
		total = total + start[i];	//因为指针移动,所以每次加当前位置即可(当前位置i=0)
	}
	return total;
}

输出:


请输入你需要数组的成员个数:10
请需要你跳着计算他们的和么?例如从第一个开始,连续相加则输入0,只加第一、三、五
个等这样(即隔1个)请输入1,依次类推。
请输入:2
现在生成数组中的成员的值ing...
第1个数为1
第2个数为7
第3个数为14
第4个数为0
第5个数为9
第6个数为4
第7个数为18
第8个数为18
第9个数为2
第10个数为4
total = 23
请按任意键继续. . .

总结:

①两个指针之间的距离是两个指针之差;

 

②可以使用start != end作为判断条件。

虽然在代码中加了一个新的,但这个是为了可以隔几个数字计算数组成员之和,假如需要连续相加,则可以只使用这个判断条件。

 

③可以通过new来自行设定数组的成员个数。

 

sum函数中的start[i]可以用*start替代。

 

 

 

const和指针:

const用于指针,有两种用途:

①让指针指向一个const常量,这样的话,指针就不能修改这个值了。如const int a=10;int*b=&a;这样;

 

②直接将const给在指针上(如 int a=5;const int*b=a;),这样就不能通过指针来修改这个值(但可以通过直接修改变量a,来修改其值)(也可以移动指针位置)。

例如:

int a[3] = { 5,10,15 };
const int*b = a;
b = b + 1;	//可以,因为是移动指针位置
//*b = *b + 1;	//不可以,不能通过指针来修改指针指向的值
a[1] = a[1] + 1;	//注意,这个时候指针b已经指向a[1]的位置了
cout << *b << endl;	//输出11

const指针之后:(1)通过指针不能改值,(2)指针位置可变,(3)可以通过其他变量来改值。

 

 

变量与指针,在加上const限定的情况下,有几下几种情况:

普通变量的地址赋值给普通指针——可以,无影响。

 

普通变量的地址赋值给const指针——可以,变量可以通过变量名修改,不能通过指针修改;指针可以移动。

 

const变量(即常量)的地址赋值给普通指针——不可以这么做。例如const int a = 10;int *b = &a;是不行的,编译器会提示const int*的值不能用于初始化int*类型的实体。

 

const变量(即常量)的地址赋值给const指针——可以。常量不能被修改。如:const int a = 10;const int *b = &a;是没问题的;

 

 

指针与指针,在加上const后:

①非const指针赋值给const指针是可以的。比如代码:

int m = 10;
int *a = &m;
const int *b = a;	//非const int指针a被赋值给const int指针b


是可以运行的。(const指针只能指向普通变量

 

const指针不能赋值给非const指针。如代码:

int m = 10;
const int *a = &m;
int *b = a;


是不行的。(因为const指针指向的地址可能是普通变量,但也可能是const变量(实为常量)const变量(常量)只能指向const指针

 

 

插入:关于有int **bint *aint m的关系,如代码:


#include<iostream>

int main()
{
	using namespace std;
	int m = 10;
	int *a = &m;
	int **b = &a;
	cout << "m = " <<m << endl;
	cout << "&m = " <<&m << endl;
	cout << "a = " << a<< endl;
	cout << "*a = " << *a<< endl;
	cout << "&a = " << &a<< endl;
	cout << "b = " << b<< endl;
	cout << "*b = " << *b<< endl;
	cout << "**b = " << **b<< endl;
	cout << "&*b = " << &*b<< endl;
	system("pause");
	return 0;
}

输出:


m = 10
&m = 0047F92C
a = 0047F92C
*a = 10
&a = 0047F920
b = 0047F920
*b = 0047F92C
**b = 10
&*b = 0047F920
请按任意键继续. . .

解释:


内存地址和其往右再往下移动一位的值是相等的。

加解除运算符*后,相当于平行左移(←)一位,加&相当于垂直上移(↑)一位。

变量m的值为10,其地址为&m;(

指针a指向变量m,则a(指针a的值)为m的地址&m*a(对指针a的值解除引用)为m的值10

指针b指向指针a,则b(指针b的值)为指针a的地址&a*b(对指针b解除引用)为指针a的值a&m**b(对指针b解除2次引用)为10

 

以下概括为:

同有或者同没const的指针a可以指向指针b

一有一无const的指针a不能指向指针b

③非const指针不能赋值给带2*(比如int **a;const指针。比如代码:

int m = 10;
int *a = &m;
const int **b = &a;


也是不行的。

推测 const指针指向的内容被限定,即*b=a被限定,但是非const指针a可以移动位置比如a+1,于是其值a改变,限定失败,故不可以

 

 

const指针不能赋值给带2*(比如int **a;)的非const指针。比如代码:

int m = 10;
const int *a = &m;
int **b = &a;


是不行的。

const指针有可能指向const常量,而const常量不能被非const指针所指向——被指针修改,所以是禁止的)

 

 

const指针可以赋值给带2*(比如int **a;const指针。比如代码:

int m = 10;
const int *a = &m;
const int **b = &a;


是可以的。

 

 

 

尽可能使用const的理由:

将指针(如int*a)声明为指向常量数据的指针(const int*a)的好处:

①避免因为操纵指针等原因,将数据修改而导致程序错误(特别是某个程序理应全程不会被修改)。

 

②使用const能使得函数,既能处理const实参,也能处理非const实参。而不使用const则只能接受非const数据。

ps:没,看,懂——也许是因为缺乏经验。

 

 

 

const在指针不同位置:

之前说的是:const int*a这样类型的,除此之外,还有int*const a;这样类型的。

①对于const int*a;限定的是指针的值,不限定指针指向的地址;(想象成const *a*a是值)

②对于int*const a;限定的是指针指向的地址,不限定指针的值。(想象成const aa是地址)。

③除此之外,还有const int*const a;这个时候,即限定地址,也限定值。

 

如代码:

#include<iostream>

int main()
{
	using namespace std;
	int a = 10;
	int m = 20;
	const int*b = &a;
	cout << b << endl;
	b = &m;
	//*b = 50;	//不允许
	cout << b << endl;
	
	int*const c = &a;
	//c = &m;	//不允许
	cout << *c << endl;
	*c = 50;
	cout << *c << endl;

	const int*const d = &a;
	//d = &m;	//不允许
	//d = 50;	//不允许

	system("pause");
	return 0;
}

 

即观察const后有什么:

若有指针,那么就限定指针;

若有解除运算符*,则限定指针的值。

 

 

函数头使用const来限定传递的指针参数时:

当通过函数传递指针时,加上const,如void abc(const int*a, int b),则可以保护指针数据不被修改——如果有必要的话。

①因为const指针只能赋值给const指针,这样即使传递给另外一个函数,另外一个函数也不能修改值(如int a=10;const int*b=&a;int*c=b;这样的最后一行代码是错误的)

 

const指针限定了不能修改值(因为是*a);

 

③因为是通过函数,不能知晓const指针指向的变量名,因此也不能通过变量名来修改。

 

于是一般情况下,该指针指向的地址的变量不会被修改。

 

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值