一道关于C++的笔试题目,题目是这样的:
int szNum[5] = { 1, 2, 3, 4, 5 };
int *ptrA = (int*)(&szNum+1);
int *ptrB = (int*)((int)szNum + 1);
std::cout << ptrA[-1] << *ptrB << std::endl;
问打印结果是什么?
拿到题当然不能上机拿结果了:),不然就没有思考的乐趣了,首先就得自己分析一下了,对于ptrA,看到等式的右边就不难发现是考察的指针算术的概念,就是说对指针做算术不是简单的加一或者减一,而是要看其指针类型来做决定,这里szNum是一个指向整型数组第一个元素的指针,则其做取地址的操作的时候得到的是指向含有5个元素的整型数组的数组指针,也就是说其+1的时候数组指针应该会向后移动1个含有5个int元素的数组单位,也就是就是指向移动5个int单位之后的那个地方,而要打印的结果是ptrA[-1],表示是要回退一个int单位,所以结果应该是指向5的,应此ptrA[-1]打印的结果就应当是5,后面一个ptrB指向的答应结果又是什么呢,我们接下来继续分析,首先把数组的首地址转换成int类型,而后再加1,这里的加1就和上面的那个不一样了,这里考察的是Address Arithmetic(地址算术),这次的对象变成了一个首地址值,因此加一的话,应该是意味着加上一个字节,也就是说,这个首地址+1个字节的offset(偏移量),该地址里面存的结果又该是多少呢?这里我想到了一个叫做Big Endian&Little Endian的问题,也就是说
Big endian machine: It thinks the first byte it reads is the biggest.
大尾端机器:认为其读的第一个字节为最大的那位上的数。
Little endian machine: It thinks the first byte it reads is the littlest
小尾端机器:认为其读的第一个字节为最小的那位上的数。
而X86系列的机器据我所知都是Little endian的机器,因此在其存储的过程中就应当是如下的图景:(为了说明问题,地址乃自己构造的,内容是数组首地址指向的元素值,以及第二个元素值)
0X0001 : 01
0X0002 : 00
0X0003 : 00
0X0004 : 00
0X0005 : 02
0X0006 : 00
0X0007 : 00
0X0008 : 00
(int)szNum + 1 因此这个结果就为0X0002了,而其指向的结果是一个int型的元素,大小为四个字节,因此按照little endian机器的读法就是02 00 00 00了,结果的十六进制数就是0X2000000,转换为十进制数就为33554432(2*16^6),因此整个题目的打印结果是533554432。
OK,现在可以在电脑上去验证一下了:
int szNum[5] = { 1, 2, 3, 4, 5 };
004124BE mov dword ptr [ebp-18h],1
004124C5 mov dword ptr [ebp-14h],2
004124CC mov dword ptr [ebp-10h],3
004124D3 mov dword ptr [ebp-0Ch],4
004124DA mov dword ptr [ebp-8],5
首先是这个数组初始化后5个元素所存储的位置,可以发现这里int型占四个字节,
int *ptrA = (int*)(&szNum+1);
004124E1 lea eax,[ebp-4] //加载移动后地址里的数据到EAX(好奇的人会问移动之前是那个地方呢?其实算一算就知道了(不用算也知道哈哈,肯定是首地址的位置,为了证实一下算一算熟悉过程),移动以后是ebp-4,移动了5个int元素的空间也就是20个字节,20 == 0X14h,那么移动前就是ebp-4h-14h == ebp - 18h)
004124E4 mov dword ptr [ebp-24h] //把指向int元素的指针地址赋给ptrA
int *ptrB = (int*)((int)szNum + 1);
004124E7 lea eax,[ebp-17h] //地址算术原来是ebp-18h移动一个字节后变成ebp-17h
004124EA mov dword ptr [ebp-30h],eax // 赋值
好了分析完毕了,我想说的是在c++编程过程中还是了解点底层好,特别是汇编,这样很容易分析出结果,对于指针的一些复杂操作通过对底层的了解,看看汇编出来的结果就很容易理解了,有空的时候我会分析一下 ++ptr和ptr++以及 A+=B 是否真的是 A = A + B的问题(我记得上学的时候就有人讲过不过不幸的是误导居多),以及c++程序的汇编结构问题,这些问题同样是通过汇编看底层很容易理解的。
这个题目中数组svNUm[1]最大为127,超过127,第二个输出会溢出,变成负数。