目录
1.
2.
3.
4.
5.
6.
7.
8.
1.
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
【答案】
2,5
【解析】
定义了一个指向整数的指针
ptr
,并将其初始化为&a + 1
。这里&a
表示整个数组a
的地址,而&a + 1
表示数组a
之后的位置,也就是数组a
的末尾之后的位置。由于
ptr
指向a
的末尾之后的位置,所以*(ptr - 1)
表示ptr
指向的位置向前偏移一个int
的大小,即指向了数组a
的最后一个元素。然后在
printf
语句中使用*(a + 1)等价于a[1]
表示数组a
中的第2个元素,也就是2。使用*(ptr - 1)
表示数组a
中的最后一个元素,也就是5。因此,最终输出结果为
2,5
。图解:
2.
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
//0x开头的数组是16进制的数字
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
printf("%p\n", p + 0x1);
这个表达式中,p是一个指针,+ 0x1表示将指针向后偏移1个Test结构体的大小。由于Test结构体的大小是20字节,所以偏移后的地址为0x100014。在printf中使用%p格式化字符串输出这个地址
printf("%p\n", (unsigned long)p + 0x1);
这个表达式中,首先将指针
p
强制转换为unsigned long
类型,然后再加上0x1
。这样会将指针的值视为无符号长整型进行计算。假设在你的系统中,unsigned long
类型和指针类型都占用4个字节,那么指针的值将会被当做无符号长整型的值来处理。也就是说现在是无符号数了不是指针了。在此基础上,再加上0x1
。结果为0x100001
。在printf
中使用%p
格式化字符串输出这个地址。
printf("%p\n", (unsigned int*)p + 0x1);
这个表达式中,首先将指针
p
强制转换为unsigned int*
类型,这里假设unsigned int
类型占用4个字节,然后再加上0x1
。这样做会将指针的值视为unsigned int
类型的指针,并对指针值进行偏移。由于指针类型是unsigned int*
,每次偏移一个字节,所以结果为0x100004
。在printf
中使用%p
格式化字符串输出这个地址。
3.
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;
}
ptr1[-1]等价于*(ptr1-1),表示ptr1向前挪动一个整型指向4,结果就为4
*ptr2整型指针访问四个字节(即红框所框处),此时为内存存放的图解,需要转成真实值即0x02000000。
4.
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
题中的是小括号,( , )即逗号表达式。a[3][2]中实际存储的是{1,3,5}。将a[0]赋值给p指针,即p指向数组第一行地址,再通过p[0]找到第一行第一个元素即1。
5.
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;
}
【答案】
FFFFFFFC,-4
a的类型是int (*) [5] ——— 即a指针指向一个大小为5的数组
p的类型是int (*) [4] ——— 即p指针指向一个大小为4的数组
当把a所指的地址赋值给指针p之后,a p指向同一地址,但是由于a认为自己指向的是5个元素的数组,而p认为自己指向的是4个元素的数组,这就会导致它们就算下标相同时访问到的内容也是不一样的,如图所示。
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
随着数组下标的增长,地址是由低到高的变化,并且指针和指针相减的绝对值得到的是指针之间的元素个数,而当取出p[4][2]和a[4][2]的地址之后相减,就是小地址减去大地址,得到一个负数,就是-4。
%p是打印地址,会认为内存中存储的补码就是地址,所以就是打印-4的补码
原码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
以%p地址的形式打印补码转成16进制表示:FF FF FF FC
原码反码补码的转换在之前的博客有涉及原码、反码、补码。有符号整数的表示和运算。-CSDN博客
而-4以%d形式打印就是-4
6.
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;
}
【答案】10,5
【解析】
首先,
int aa[2][5]
声明了一个2行5列的二维数组,并初始化了其中的元素。
int* ptr1 = (int*)(&aa + 1);
中的&aa
是取二维数组aa
的地址,表示整个二维数组的起始地址。然后通过+ 1
将指针向后移动,指向数组的下一个位置。最后将结果强制转换为int*
类型,赋值给ptr1
。
int* ptr2 = (int*)(*(aa + 1));
中的*(aa + 1)
表示取二维数组aa
中的第2行的地址。然后将结果强制转换为int*
类型,赋值给ptr2
。
*(ptr1 - 1)
表示ptr1
所指向的元素的前一个元素的值。在这里,它指向二维数组aa
的最后一个元素。
*(ptr2 - 1)
表示ptr2
所指向的元素的前一个元素的值。在这里,它指向二维数组aa
的第一行的最后一个元素。所以,最后的输出结果为:
10, 5
,其中*(ptr1 - 1)
的值为二维数组aa
的最后一个元素 10,*(ptr2 - 1)
的值为二维数组aa
的第一行的最后一个元素 5。
7.
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
【答案】at
【解析】
将
a
的地址赋值给pa
。然后通过pa++
将pa
指向了a
数组中的第二个指针元素
8.
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
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;
}
【答案】
POINT
ER
ST
EW
【解析】
最初状态:
printf("%s\n", **++cpp);
** ++cpp,cpp先自增1,跳过一个char**类型指向下一个元素的地址,如图。
此时再对cpp进行解引用,找到cp[1],再解引用,找到c[2]即P的地址。此时%s打印出来就是POINT。
printf("%s\n", *-- * ++cpp + 3);
【* -- * ++cpp + 3】,加号优先级是最低的,所以最后算。cpp先自增1跳过一个char**大小指向下一个元素的地址,如图。
此时再对cpp进行解引用,找到的是cp[2]即指向了c+1这块空间,再自减1则指向了c这块空间,再解引用找到了c[0]即ENTER中第一个E的地址,此时+3跳过三个char,最后指向ENTER中第二个E的地址,此时用%s打印出来就是ER。
第一步++cpp也就是指向cp中的第三个元素c+1这块空间
第二步*++cpp,也就是得到cp[2]
第三步-- * ++cpp,c+1变为c
第四步* -- * ++cpp,找到了c中的第一个元素所指向的ENTER的空间指针指向E
第五步* -- * ++cpp + 3,从指向E变为指向E。
printf("%s\n", *cpp[-2] + 3);
【*cpp[-2] + 3】等价于【* *(cpp-2)+3】,与前面的cpp自增自减不同,cpp-2只是表达式并不会改变cpp的指向。
第一步cpp指向的位置向前移动两位之后解引用找到cp中的第一个元素也就是c+3,
第二步解引用找到c中的第4个元素也就是c[3]指向的是first。
第三步指针加三也就是从指向F变为指向S
所以答案是ST
printf("%s\n", cpp[-1][-1] + 1);
return 0;
【 cpp[-1][-1] + 1】等价于【*(*(cpp-1) -1) + 1】
*(cpp-1)找到c+2,c+2再-1就是c+1的地址,再进行解引用找到NEW中N的地址,再+1找到E的地址,此时用%s打印出来就是EW。