当且仅当在函数头或者函数原型中:
例如:int *a和int 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 **b,int *a,int 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 a,a是地址)。
③除此之外,还有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指针指向的变量名,因此也不能通过变量名来修改。
于是一般情况下,该指针指向的地址的变量不会被修改。