//请问输出什么?
#include
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
return 0;
}
我们有必要搞清楚整形指针 ptr 是什么来头。
这个图我们可以明显看到 &a+1 之后指向哪个位置。我们要注意的是,我们有 & 这个取地址符号,即代表我们取出的是整个数组的地址,所以是一个数组指针类型的地址。再通过强制类型转换得到整型指针 ptr 。
*(ptr-1),同样也不难理解,得到数组的第五个元素,即 5 。
故本题输出为: 2,5
#include
//这里告知结构体大小为20个字节
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
注意我们的程序,定义了一个全局结构体指针变量 p ,那么这个指针变量的值默认初始化为0。
那么 p+0x1 就可以理解为:指向结构体指针 p 向后移动一个结构体大小的地址。
那就应该得到答案: p+0x1 = 20 ,需要注意的是,打印格式为 %p 。
这里普及一下:%p 是以地址的形式来打印数字的。
那么 (unsigned long)p+0x1 更简单了,即把结构体指针变量转化为无符号长整型数字了,数学上如何计算加法,那么这里就怎么计算加法,所以这里输出 1 。
对于(unsigned int*)p+0x1 ,就是把结构体指针变量强制类型转换为了无符号整型指针了,那么整型指针+1,无非就是向后跨越4(整型大小)个字节。故答案为 4 。
一定要注意是以 %p 形式打印的。
#include
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 与第一题类似,只不过输出的时候 将 *(a-1) 写为了 a[-1] 。
重点是如何理解整型指针 ptr2 ,假设我们的机器是小端存储模式,那么数组在内存中的存储形式应该是这样:
题目将数组首元素地址强转为了整型变量并+1,即指针变成了数字+1,再将其强转为整型指针。现在我们假设数组首元素的地址为 0 ,即a的地址为0,那么有:
通过这个图,就可以明显看出 ptr2 内存放的是 a+1 的地址,但因其是整型指针,所以它会包括四个字节:
那么输出结果为:
需要注意,本题若是使用比较高版本的VS编译器,需要再x86环境下才能正常输出结果。
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
这个题的重点在于逗号表达式。
一定要注意,数组中经过逗号表达式运算后,存储的应该是:
内存布局应该是:
整型指针 p 等于 a[0] ,a[0] 是指针操作的简写,那么完整的指针形式为:*(*(a+0)+0) ,即二维数组的第一个数组元素的第一个元素 ,即 1 。
#include
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;
}
这个题乍一看是又一些难度的,那我们仔细分析:
二维数组的大小为 5*5 ,但数组指针指向的数组大小为 n*4 ,并且把二维数组的数组名赋给了指针p,这就导致了指针 p 与数组 a 的类型不同,但数据的存放顺序都是一样的。
我们来看图:
明显直观地看到 p[4][2] 与 a[4][2] 之间相差四个元素,正巧的是,指针相减得到的正是两个指针中间的元素个数。那么 p[4][2] - a[4][2] 得到的就是 -4 。
但是,-4 是个负数,而%p是一个无符号的格式,所以-4的原反补为:
-4存储在内存中的是补码,那么以 %d 形式打印的话,会将补码翻译成原码,打印:-4
以 %p 打印的话,是以无符号形式打印的,所以会直接以无符号十六进制打印补码。
故输出为:
#include
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;
}
整型指针变量 ptr1 想必不用多说,打印的结果也就为 10 。
对于整型指针变量 ptr2 ,*(aa+1),找到的也就是二维数组的第二个数组元素的首元素地址,所以前面的强制类型转换是为了混淆我们的。
那么 ptr2 也就保存元素 6 的地址,这个地址 -1 得到 5 的地址,所以输出5。
#include
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
这段代码非常简单。
首先是一个指针数组,指针数组里面放的是三个字符串常量(字符串常量不做介绍了),二级指针 pa 放的是数组名,即首元素地址,通过 pa++ 得到了第二个元素的地址,故打印 at 。
#include
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;
}
我们先看一副关系图:
那么 **++cpp ,我们按照运算符优先级来分析:
++cpp:
**++cpp:
所以这里输出:POINT ,并且要注意,++cpp 可以表示为 cpp=cpp+1,也就是说,cpp的值是改变了的。
我们来看 *--*++cpp+3 如何分析:
++cpp:
*++cpp得到 c+1 ,--*++cpp 得到 c
*--*++cpp得到:
即得到 ENTER 的首元素地址。
*--*cpp+3得到 E 的地址,往后打印即为 ER。
我们再看如何理解 *cpp[-2]+3 ,这个表达式可以化为: *(*(cpp-2))+3
那么 *(cpp-2) 得到 c+3:
*(*(cpp-2))为:
即得到 F的地址。
*(*(cpp-2))+3即为 S 的地址,故打印 ST。
对于cpp[-1][-1]+1 这个表达式可以化为 *(*(cpp-1)-1)+1
则*(*(cpp-1)-1)表示为:
即得到 N 的地址,那么 *(*(cpp-1)-1)+1 得到 E的地址 ,向后打印得 EW 。