赞
踩
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
Result:
Analysis:
如图所示:arr是数组名且数组的首元素地址,对数组名取地址得到的是整个数组的地址,即&arr,
此时&arr的类型是(*)[] 数组指针类型,跨度为指向数组的大小。所以&arr+1指向的位置就如图,
再把整个数组指针强制类型转换为int * ,储存到 *ptr这个指针变量中,其实ptr储存的是&arr+1
最后的打印:
*(arr + 1):arr是数组名且数组的首元素地址,+1后得到的是第二个元素的地址,对第二个元素的地址解引用的到的就是2
*(ptr - 1):ptr储存的是&arr+1,也就是图中的位置对它-1后解引用,得到的就是5
struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
Analysis:
这里的0x1指的是16进制的1。
第一个printf,p是结构体指针,结构体的大小为20,这里理解为数组指针那样,跨度取决于数组大小,这里跨度取决于结构大小,所以这里的+1是地址+1,所以得到是下一个地址,又加上我们之前分析的这个指针跨度为结构体大小(20),所以应该是0x10000014,20的十六进制为14。
第二个printf,把p强制类型转换成unsigned long(无符号长整形),整形元素+1,得到是就单单的+1后的值,所以结果为0x10000001
第三个printf,把p强制类型转换成int* ,int*的跨度为4,所以+1得到的是0x10000004
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
Result:
Analysis:
首先,%x,打印的是16进制的数。
数组各元素在内存中的地址如图:
可知,在我的visual studio2022中是小端字节序(数据的低位放在内存的低地址,高位放在内存的高地址)
先分析ptr1[-1],可以写成 * (ptr1 - 1)这样去理解,
&a+1,对a(数组名)取地址得到的是 ( * ) [ ]类型的数组指针,+1的跨度为指向数组的大小,所以&a+1的指向的是数组的后一个位置
又把&a+1的值赋值给了 ptr,所以 prt - 1得到位置的是如图:
所以*(ptr-1)= 5
分析 ptr2, 可知 int * ptr2 = (int )((int)a + 1);
首先a是数组首地址且首元素,但是这里把a强制转换成了int类型,+1的跨度为1,所以这里的(int)a+1指向的位置是:
最终,再把(int)a+1强制转换成(int)赋值到 int ptr2,因为int* 的跨度为4个字节所以如图:
所以打印出来的是2000000
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
Result:
Analysis:
请注意这里的二维数组初始化的时候,用的是括号,所以这里为逗号表达式。
PS:
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果
所以这里的二维数组初始化为:(如图)
然后我们可以根据这图去看 p = a [0],首先a是二维数组名且二维数组的首元素的地址,二维数组的首元素是第一行一维数组 。然后a[0],我们可以看成是 ( a + 0),所以这里的p是a [ 0 ] [ 0 ]的地址
最后的打印同理,可以看成为(p+0),所以打印出来就是1
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
这里直接报错了,因为我的vs 版本比较新,所以比较严格,我们看这些错的程序,主要是为了观察数据在内存中是怎么运算且储存的
因为p的类型是 int(*)[4] 跨度为4个int ,而a的类型是 int ( * )[5]跨度为5个int,所以如图:
Analysis:
同一数组中,指针 - 指针得到的是中间间隔多少个元素,这里可以看到是4个,所以是 -4
那我们看看 -4,在内存中是如何存储的
补码 = 反码 + 1
10000000 00000000 00000000 00000100(原码)
11111111 11111111 11111111 11111011 (反码)
11111111 11111111 11111111 11111100 (补码)
FF FF FF FF FC
%d打印的话,因为是有符号整形,所以把它转换成原码打印出来: -4
%p打印的话,地址没有正负之分,所以直接将补码以16进制的方式打印出来:FF FF FF FF FC
PS:在内存中,负数是以补码的形式存储的,如果要打印出来的话,是要转换成原码打印。
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(aa + 1);
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
Result:
Analysis:
aa是一个二维数组,五个元素为一组,共两组。
int ptr1 = (int )(&aa + 1);
aa是一个数组名,对数组名取地址,得到的是整个数组的地址,类型是数组指针,跨度为数组大小,所以+1,会跳过整个数组指向这个数组的后一个位置,再强制转换成int赋值给ptr1。
因为ptr1 的类型是int ,所以 ptr1 - 1 得到的是这个数组最后一个元素的地址,再进行解引用得到的是 10。
int ptr2 = (int ) (aa + 1);
aa是一个数组名且是数组的首元素,而二维数组的首元素是第一行,所以aa是第一行的地址,类型是数组指针,跨度为第一行的大小,所以aa+1的+1是跳过了一行,aa+1指向的位置是第一行的后一个位置,然后再强制类型转换成int 赋值给ptr2。
因为ptr2 的类型是int ,所以 ptr2 - 1 得到的是第一行最后一个元素的地址,再进行解引用得到的是 5。
int main()
{
const char* a[] = { "work","at","alibaba" };
const char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
Result:
Analysis:
a是一个指针数组,里面存放3个const char类型的指针
a是数组名且数组的首元素地址,char的地址,也就是指针的地址,所以这里用一个二级指针去存储a
pa++ ,首先 pa = a, a是数组的首地址,所以pa++指向的是数组的下一个元素,也就是at的地址
再将at的首字符的地址由%s打印,得到的是at
int main()
{
const char* c[] = { "ENTER","NEW","POINT","FIRST" };
const char** cp[] = { c + 3,c + 2,c + 1,c };
const char*** cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *-- * ++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
Result:
Analysis:
c是一个指针数组,每个元素的类型为char*,储存的是字符串的首地址
cp是一个二级指针数组,每一个元素类型为char**,储存的是c中各个元素的地址
cpp是一个三级指针,存储的地址是二级指针数组的数组名,即cp数组的首元素地址
如图:
1.printf(“%s\n”, **++cpp);
cpp是cp数组的首地址,++后指向的是cp数组的第二个元素。对cp数组的第二个元素解引用得到的是c数组中第三个元素的地址,再对该地值解引用得到的是POINT的首地址,用%s打印得出POINT
2.printf(“%s\n”, *-- * ++cpp + 3);
因为1中的++cpp已经把cpp给改成了指向cp数组的第二个元素,在这个基础上又进行了一次++cpp,所以现在得到的是c+1的地址,对c+1的地址解引用得到的是c+1,即c数组中第二个元素,再对他进行–,所以改成了c数组中第一个元素,再解引用得到的是ENTER的首地址,首地址+3得到的是‘E’的地址,再用%s打印得出ER
3.printf(“%s\n”, *cpp[-2] + 3);
现在cpp的存储的地址是 cp + 2,cpp[-2]可以写成 (cpp - 2), 所以这里的 cpp[ -2 ] 可以写成(cp + 2 - 2)得到的是 c数组中的第四个元素,再解引用得到的是FORST的首地址,即F的地址,再对F + 3,得到就是‘S’的地址,再用%s打印得出ST
4.printf(“%s\n”, cpp[-1][-1] + 1);
cpp[-1] [-1]可以写成 * ( *(cpp - 1) - 1 ) ,可知,现在cpp储存的值为cp+2,所以可以写成 *(cp + 1)得到的是 c+2的地址,再 - 1得到的就是c+1的地址,再解引用得到的是NEW的首地址, 即N的地址, 又+1得到的就是E的地址,再用%s打印得出EW
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。