为了题目的准确性和我们一般学习过程中的习惯,这里所有的题目代码都是在 X86 环境(32 位平台)下运行的。
题目一:
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//程序的结果输出什么?
我们先研究 ptr 指针变量里面存储的是什么。&a+1 表示取出整个地址并向后跳跃一个数组类型的大小,也就是指向了数组最后一个元素 5 的后面一块数组类型大小的空间。
但是我们想要把这个地址交给指针变量 ptr 是不行的,因为类型不一样(&a+1 是一个数组指针类型,prt 是一个整形指针类型),所以我们在 (&a+1) 之前 (int*) 来强制类型转换。这样我们搞清楚了 ptr 指针变量里存的是什么。
那么现在来看输出部分。*(a+1) 是比较简单的,a 是首元素地址,a+1 是第二个元素地址,对其解引用得到第二个元素 2 ,这是毋庸置疑的答案。对于 *(ptr-1) ,我们知道 ptr 里面存放的地址位置,并且知道 ptr 是一个整形指针,ptr-1 相当于向前跳跃一个整形类型的大小,即指向数组最后一个元素 5 的位置。对其解引用就能得到元素 5 。
故最后的输出结果为: 2,5 。
题目二:
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//如下表表达式的值分别为多少?
//已知,结构体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;
}
//程序最后输出什么?
我们注意观察结构体的定义,定义了一个结构体指针变量 p 。
p+0x1 非常简单,0x1 就是 1,只不过是以十六进制的格式书写的。这与我们经常习惯使用的指针计算一样,p 是一个结构体指针,那么 p+1 理当向后跳跃一个结构体类型的大小,即指向了结构体空间的后一个结构体空间的位置。并且我们知道 p 是一个全局变量,全局变量默认初始化为 0 ,那我们便可以知道,p=0 ,p+1=20 。 而 %p 是一个十六进制的打印格式,所以结果输出:00000014 。
(unsigned long)p + 0x1 似乎更简单一些。p 类型转换成了一个长整形,那么就代表 p 不再是一个指针,它存放的 0 就是单纯意义上的数字。所以 0 + 0x1 就等于 00000001 。
(unsigned int*)p + 0x1 也非常简单。p 本是一个结构体指针,现在强转为整形指针,这就代表着 p 进行加减整数的时候步幅由 20 个字节 变成了 4 个字节。那么 p=0 ,p+1=4 是毋庸置疑的。输出的结果为:00000004 。