本章来了解一下关于指针和数组的一些例题,回顾之前所学的数组和指针,并作出一些总结。
1.一维数组
例题:
int a[] = {1,2,3,4}; printf("%d\n",sizeof(a));//1 printf("%d\n",sizeof(a+0));//2 printf("%d\n",sizeof(*a));//3 printf("%d\n",sizeof(a+1));//4 printf("%d\n",sizeof(a[1]));//5 printf("%d\n",sizeof(&a));//6 printf("%d\n",sizeof(*&a));//7 printf("%d\n",sizeof(&a+1));//8 printf("%d\n",sizeof(&a[0]));//9 printf("%d\n",sizeof(&a[0]+1));//10
我们一个个分析:
第一个:我们首先知道a表示首元素的地址,只有两个情况下除外。
1.sizeof(数组名)
2.&数组名
而我们这里刚好是sizeof(数组名),那么它表示整个数组的元素,那么可以得到结果为16
第二个:
a + 0 b不是一个单独的数组名,所以它并不表示整个数组的元素,a表示首元素的地址,往后移0元素类型位,还是首元素的地址。那么可以得到结果为 4 / 8 。
得到4是因为在 ×86 的环境下 ,而得到 8 是在 × 64环境下。
第三个:
*a 表示取出首元素的地址,也就是1,1的类型为int 所以得到结果为4
第四个:
a表示首元素的地址,向后移一个元素的大小,得到的还是一个地址,那么结果还是4 / 8
第五个:
a[1]找到第二个元素,结果为4
第六个:
&a是一个地址,结果为 4 / 8
第七个:
先取出a的地址,得到了一串地址,在对地址解引用找到了a,相当于& 和 * 相互抵消了,得到的还是一个 a 任然表示整个数组的元素。我们可以认为 a == *&a是一个东西。
第八个:
&a + 1,我们找到,这样是跳过了一整个数组,我们要是去访问的话是一定会越界的。但我们这里没有去访问后面的地址,只是在计算,它仍是个地址,所以结果为 4 / 8
第九个,第十个:
取出第一个元素的地址,是个地址,哪怕是再加上一个1,向后移动4个字节,也是一个地址,结果还是4 / 8.
2.字符数组
我们将分为两段解决
char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
我们首先来确认一下,这个数组有几个元素。答案是6个,千万别说7个,这里没有 /0 只有字符串中才有 /0。 我们根据上面所作出的一些解释可以很快知道第一个的答案:6,我就不再重复解释了。
第二个:地址向后移动0位还是一个地址,所以结果为 4 / 8。
第三个:*arr得到首元素,char为一个字节,结果为1。
第四个:与第三题一样,结果也是1。
第五个:&取出一个地址,结果为地址,所以得到4 / 8。
第六个:取出地址后向后移一个字节,还是地址,所以得到4 / 8。
第七个:与第六个只是表示不一样,但是表示的都是同一个东西,所以得到4 / 8。
char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
strlen和sizeof并不是一个东西,sizeof是操作符,strlen是一个库函数,strlen只有遇到/0才停止。arr数组只有6个元素,其中并不包含/0。
那么对于第一个printf可以知道arr是首元素的地址,从首元素开始去找/0,这道题的结果是个随机数,我们并不知道/0究竟在什么地方。
第二个:同理,向后移动0个字符,结果也是个随机值
第三个:我们传进去了 ' a '的ASCII码值,对应的是97,strlen去找对应97的地址处,强行计算它的大小。对于计算器给出的地址储存元素,都是由计算机来决定的,我们不能自己去抢地址来使用,有些地址我们是不能用的,所以这里会报错error。
第四个:和第一个的表达形式不同,也是取首元素的地址,结果也是个随机值。
第五个:哪怕移动了一个整个数组,也是个随机值,并且二者没有任何关系,谁都不知道/0会出现在哪。
第六个:与第五个相比,它只移动了一个字节,其他的都一样。
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
这一段很简单,结合之前讲过的我们快速解决。
sizeof(arr)表示整个数组,数组共有六个元素结果为6.
第三、四个都是取出第一个元素,类型为char类型,结果为1。
剩下的都表示地址,结果均为 4 / 8。
char arr[] = "abcdef"; printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
我们strlen只计算到/0之前的元素,所以第一个的结果为6。
第二个:向后移动了0个字节,还是第一个元素开始,结果依然为6。
第三个:把a传进去,相当于传97,又和上面一样,会报错。
第四个:将第二个元素b传进去了,也就是98,也会报错。
第五个:和第一个一样,传首元素,结果为6。
第六个:+1跳过了整个数组,所以结果是个随机数。
第六个:从第二个元素开始,结果为5。
三:二维数组
int a[3][4] = { 0 }; printf("%d\n", sizeof(a));//单独的sizeof(数组名)等于整个数组 printf("%d\n", sizeof(a[0][0]));//表示二维数组的第一个元素,int类型 printf("%d\n", sizeof(a[0]));//二维数组我们可以看成是由多一维数组组成 //此时a[0],a[1]都是这一行的数组名,那么单独把数组名放在sizeof内部就是这一行的所有元素 //那么结果就为16 printf("%d\n", sizeof(a[0] + 1));//a[0]表示第一行首元素的地址,a[0] --> &a[0][0]—int* //a[0]+1 表示跳过一个字节,则为a[0][1]的地址 printf("%d\n", sizeof(*(a[0] + 1)));//在上一题的基础上解引用,结果为一个元素类型int //答案为4 printf("%d\n", sizeof(a + 1));//a表示首元素的地址,二维数组首元素的地址是第一行的地址 //a+1 也就是第二行的地址,是地址结果就为4/8 printf("%d\n", sizeof(*(a + 1)));//结合上一行进行解引用,找到的就是第二行,第二行的大小为16 printf("%d\n", sizeof(&a[0] + 1));//&a[0]表示第一行的地址,+ 1就是第二行的地址 //是地址就为4/8 printf("%d\n", sizeof(*(&a[0] + 1)));//解引用拿到第二行,结果就为16 printf("%d\n", sizeof(*a));//a表示首元素的地址,解引用拿到第一行,结果就为16 printf("%d\n", sizeof(a[3]));//a[3]找到第四行,虽然超出了我们所定义的范围,但是sizeof //不会参与实际运算,也就不会去访问第四行,所以代码没有问题。 //结果为16
总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。