作者邮箱:z_zhanghaobo@163.com
github相关: https://github.com/HaoboLongway/simple_examples
指针提供一种以符号形式使用地址的方法。因为计算机硬件指令非常依赖地址,指针能够把想要传达的指令以更接近机器的方式表达。因此,使用指针的程序(一般地)更有效率。我们将会看到,指针能够有效处理数组,数组表示法其实是在变相使用指针。
关于数组及其指针间的联系,主要需要了解以下几个基础点
-
数组名是该数组首元素的地址
例如,如果flizny
是一个数组,那么有flizny == &flizny[0] //& 取地址运算符
成立
...
int main()
{
short dates[SIZE]; //short类型占用2字节
short *pt1;
short index;
double bills[SIZE]; //double类型占用8字节
double *pt2;
//由于硬件不同,上述字节占用可能如上所示
pt1 = dates;
pt2 = bills;
cout<<setw(23)<<"short"<<setw(10)<<"double"<<"\n";
for(index=0; index<SIZE; index++){
cout<<"pointer + "<<index<<": "<<setw(10)<<pt1 + index<<setw(10)<<pt2 + index<<"\n";
}
return 1;
}
...
/////////////
//输出结果
short double
pointer + 0: 0x6dfedc 0x6dfeb8
pointer + 1: 0x6dfede 0x6dfec0
pointer + 2: 0x6dfee0 0x6dfec8
pointer + 3: 0x6dfee2 0x6dfed0
如果从简单的加法考虑,应该有0x6dfedc + 1 == 0x6dfedd
, 0x6dfeb8 + 1 == 0x6dfeb9
成立,可是这里显示的地址又是怎么回事?
其实,指针加一指的是增加一个存储单元, 对数组而言,这意味着加一后的地址是下一个元素的地址,而非下一个字节的地址。
题目3-1-1:
给出下面代码
...
int main()
{
int test_arr[6] = {1,2,4};
cout<<test_arr<<endl;
cout<<sizeof (int)<<endl;
cout<<test_arr+3<<endl;
cout<<*(test_arr + 4)<<endl;
cout<<*test_arr + 4<<endl;
return 1;
}
已经知道上述部分输出为
0x6dfee8
4
...
求后三行输出结果
答案: 0x6dfef4
, 0
, 5
.
- 注意括号的优先级比*运算符高。
- 如前所述,
*test_arr
和test_arr[0]
是等价的。
-
多维数组的指针表示法
进行以下实验:
(关于格式化字符的输出具体参考https://www.runoob.com/cprogramming/c-function-printf.html)
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main(){
int zippo[4][2] = {{2,4}, {6, 8}, {1, 3}, {5, 7}};
//这里使用了printf函数作格式输出,在C++里面需要导入<stdio.h>以及<stdlib.h>才能使用
printf(" zippo = %p, zippo + 1 = %p\n", zippo, zippo+1);
printf(" zippo[0] = %p, zippo[0] + 1 = %p\n", zippo[0], zippo[0]+1);
printf(" *zippo = %p, *zippo + 1 = %p\n", *zippo, *zippo+1);
printf("zippo[0][0] = %d\n", zippo[0][0]);
printf(" *zippo[0] = %d\n", *zippo[0]);
printf(" **zippo = %d\n", **zippo);
printf(" --End--");
return 1;
}
该程序正常输出为:
(注意获取zippo
及其他有关获取地址的操作在不同机器上执行结果不同)
zippo = 0060FEF0, zippo + 1 = 0060FEF8
zippo[0] = 0060FEF0, zippo[0] + 1 = 0060FEF4
*zippo = 0060FEF0, *zippo + 1 = 0060FEF4
zippo[0][0] = 2
*zippo[0] = 2
**zippo = 2
--End--
可以发现其中的特点(先不要看下面的整理,你发现编译器是如何对个指针做出反应的吗?)
-
zippo
的地址与一维数组zippo[0]
的地址相同,当然也有*zippo == zippo[0]
成立 -
zippo与zippo + 1
相差的地址有八个字节,这是因为zippo
的元素是一维数组,而该数组占内存长度应当是其元素个数(即2)×数据类型所占字节数(即4)=8.
从指针的角度,解引用一个指针,得到引用对象代表的值,因为zippo[0]
是该数组首元素(zippo[0][0]
)的地址,所以*(zippo[0])
表示储存在zippo[0][0]
上的值。与此类似,*zippo
代表该数组首元素(zippo[0]
)的值,但是zippo[0]
本身是一个int
类型值的地址,该值的地址是&zippo[0][0]
,所以*zippo
就是&zippo[0][0]
。
简而言之,zippo
是地址的地址,必须解引用两次才能够获取原始值,这是双重间接(double indirection)的例子
那么,现在你能够分析出为什么*(*(zippo+2) + 1)
与zippo[2][1]
等价了吗?
-
zippo
二维数组首元素的地址 -
zippo + 2
二维数组第三个元素的地址 -
*(zippo+2)
二维数组第三个元素(是一个一维数组)首元素(是一个int
型)的地址 -
*(zippo+2)+1
二维数组第三个元素的第二个元素(这就是zippo[2][1]
了)的地址 -
*(*(zippo+2) + 1)
得到了zippo[2][1]
的值
所以,对于多维数组,指针表示法有时令人迷惑,当要获取值时,最好采用数组表示法.
题目3-2-1:
指出下面代码的输出结果:
char *a[] = {"I", "like", "C++"};
char **pa = a;
pa++;
cout<<*pa<<endl;
答案:like
- 注意这里字符串如
"C++"
相当于一个一维数组,而a
即是多维数组,这里运用上面的分析方法,不难分析出结果。 - 同时这里
char *a[]
的声明方式也值得注意
-
函数对数组的调用
我们知道数组名是该数组首元素的地址,作为实际参数的数组名要求形参是一个与之配套的指针。只有在这种情形下,C++才会把int arr[]
和int *arr
解释成一样的东西,也就是说,arr
是一个指向int
的指针。由于函数原型可以省略参数名,所以下面4种原型都是等价的:
int sum(int *arr, int n);
int sum(int *, int n);
int sum(int arr[], int n);
-
int sum(int [], int n);
简单了解了调用的形参之后,当一个函数需要用到一个数组中的数据时,我们通常需要将该数组(引用或指针)以及其元素个数传入,就像下面这个对所有元素求和的例子
int sum_one(int arr[], int n){
int sum=0;
for (int i=0; i<n; i++){
sum += *(arr + i);
}
return sum;
}
这个例子十分简单,也并非是仅有的方式,我们还可以向函数传递两个指针,如下例:
int sum(int * start, int * end){
int sum = 0;
while (start < end){ //保证循环最后处理的一个元素是end所指向位置的前一个元素
sum += *(start);
start ++;
}
return sum;
}
我们可以像是这样调用sum
函数:
...
int test_ar[10] = {1, 2, 3, 4, 5};
cout<<sum(test_ar, *(test_ar + 5));
...
这样会返回test_ar
的前五个元素的加和。此外,我们还可以把循环体压缩成一句 sum += *start++
注意这里一元运算符*
和++
优先级是同级的,但由于结合律是从右到左,所以start++
先求值(后缀模式,表达式值是递增前的),然后是对其的解引用。
如果用*(start++)
替换上述会更清楚些。
题目3-3-1
预测输出结果,并运行测试。
...
int test_ar[size] = {1, 2, 3, 4, 8, 3, 5};
int *p1, *p2, *p3;
p1=p2=p3=test_ar;
p3 += 2;
cout<<"*p1 = "<<*p1<<'\t'<<"*p2 = "<<*p2<<'\t'<<"*p3 = "<<*p3<<endl;
cout<<"*p1++ = "<<*p1++<<'\t'<<"*++p2 = "<<*++p2<<'\t'<<"(*p3)++ = "<<(*p3)++<<endl;
...
答案:
*p1 = 1 *p2 = 1 *p3 = 3
*p1++ = 1 *++p2 = 2 (*p3)++ = 3
可以看出*p1++
与*(p1++)
语义是相同的,*++p2
与*(++p2)
相同。
其余文章:
数组与指针基础内容:
网友评论