引言
前文,介绍了在学习指针时容易感到混淆的点,结合一维数组和字符数组,指针,辨别分析了它们的常见含义,本文接着结合一些相关笔试题来分析二维数组名与结构体指针的含义。
二维数组名
题目实例:
#include<stdio.h>
int main()
{
int a[2][6] = { 0 };
printf("%zd\n", sizeof(a));
printf("%zd\n", sizeof(a[0][0]));
printf("%zd\n", sizeof(a[0]));
printf("%zd\n", sizeof(a[0] + 1));
printf("%zd\n", sizeof(*(a[0] + 1)));
printf("%zd\n", sizeof(a + 1));
printf("%zd\n", sizeof(*(a + 1)));
printf("%zd\n", sizeof(&a[0] + 1));
printf("%zd\n", sizeof(*(&a[0] + 1)));
printf("%zd\n", sizeof(*a));
printf("%zd\n", sizeof(a[3]));
}
运行结果:
此结果基于vs2022 x64环境得出
48
4
24
8
4
8
24
8
24
24
24
结果分析:
int a[2][6] = { 0 }; //二维数组可以看成特殊的一维数组,每个元素是一个一维数组,比如说这个2行6列的二维数组可以看成一个一维数组,包含2个元素,每个元素是一个包含6个元素的一维数组。
printf("%zd\n", sizeof(a));
//48,数组名a单独放在sizeof内部,表示整个数组,计算整个数组的大小,2*6*4=48,单位是字节。
printf("%zd\n", sizeof(a[0][0]));
//4,a[0][0]表示第一行第一个元素,计算一个元素的大小,类型是int,为4字节。
printf("%zd\n", sizeof(a[0]));
//24,a[0]是二维数组第一个元素,可以看成这个包含6个元素的一维数组的数组名,sizeof(数组名)计算的是这个数组的整个大小,而这个一维数组包含6个int类型的元素,6*4=24。
printf("%zd\n", sizeof(a[0] + 1));
//4/8,a[0]没有单独在sizeof内部,也没有&数组名,所以这里a[0]表示这个包含6个元素的一维数组首元素的地址,也就是第一行第一个元素的地址,a[0]+1也就是第一行第二个元素的地址,地址大小为4或者8个字节。
//在这里a[0]--&a[0][0] a[0]+1--&a[0][1]
printf("%zd\n", sizeof(*(a[0] + 1)));
//4,解引用第一行第二个元素的地址,就是a[0][1],第一行第二个元素,大小为4字节。
printf("%zd\n", sizeof(a + 1));
//4/8,a没有单独在sizeof内部,也没有&数组名,所以这里a表示二维数组首元素地址,也就是第一行的地址,类型是int(*)[6]数组指针,a+1就是二维数组第二个元素的地址,类型也是int(*)[6]数组指针,地址大小为4或8个字节。
printf("%zd\n", sizeof(*(a + 1)));
//24,a+1就是第二行一维数组的地址,是int(*)[6]类型的数组指针,*(a+1)就是第二行这个一维数组,算的是第二行的大小,4*6=24。也可以理解成*(a+1)==a[1],那么类比第三题就容易得出结论。
printf("%zd\n", sizeof(&a[0] + 1));
//4/8,a[0]是第一行的数组名,&a[0]表示第一行这个一维数组的地址,+1表示第二行的地址。
printf("%zd\n", sizeof(*(&a[0] + 1)));
//24,表示第二行,计算的是第二行的大小。
printf("%zd\n", sizeof(*a));
//24,a没有单独在sizeof内部,也没有&数组名,所以这里a表示二维数组首元素地址,也就是第一行这个一位数组的地址,*a就是第一行。*a--*(a+0)--a[0].
printf("%zd\n", sizeof(a[3]));
//24,sizeof()是根据类型来计算大小的,并没有访问这个空间,所以没有越界访问,这里的a[3]表示的是int(*)[6]数组指针类型,与用a[0]来计算没有区别,都是int(*)[6]数组指针类型。
可参考下图便于理解
结构体指针
示例:
假设p 的值为0x200000(16进制表达形式)。 如下表表达式的值分别为多少?
#include<stdio.h>
struct Test
{
int Num;
double* Name;
char str[8];
short ptr[2];
} * p;
int main() {
p = (struct Test*)0x200000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
运行结果:
x86环境
00200014
00200001
00200004
结果分析:
struct Test
{
int Num;
double* Name;
char str[8];
short ptr[2];
} * p;
//结构体Test类型的变量大小是20个字节。
int main() {
p = (struct Test*)0x200000;
printf("%p\n", p + 0x1);
// 结构体指针+1跳过一个结构体的大小,10进制+20,16进制+14,就是0x200014,以地址形式打印出来,那么就是00200014
printf("%p\n", (unsigned long)p + 0x1);
//强转为无符号整形,整形+1就是加1,0x200001,以地址形式打印出来就是00200001
printf("%p\n", (unsigned int*)p + 0x1);
//无符号int*类型,指针+1是跳过4个字节,0x200004,以地址形式打印出来就是00200004
return 0;
}