封面图:https://www.pixiv.net/artworks/95681846
曾经我以为没有递归的程序阅读题都是渣渣,直到……
题目描述
下面代码进行数组与指针的操作,已知&score[0]的值为 0x00FF2000。
|
|
(1) 请说明数组名score 的含义。
(2) 写出程序执行结果。
非官方答案:
|
|
注:在64位linux系统上时,第一个printf输出的第二个数应为0x00FF2008
题目详解
首先第一小题完全莫名其妙,不讲。
有意思的是第二小题,这个程序有两个printf语句,第一个printf主要考察指针运算。众所周知,假设指针的类型长度为n,那么指针加k,指针实际移动n*k。在本例中,指针p1的类型是short int *,short int的类型长度是2个字节,此处p1+1,实际地址增加2,即0x00FF2002。p3+1同理。但是这里有个小坑,long int 在64位linux系统上是8个字节,在windows上是4个字节。实际考试中应指定long int长度。
第二个printf语句是输出两个指针对应的内容。我们先忽略格式控制符,看后面的两个参数。第一个参数是*p1,又因为p1=score,p1和score都是short int *类型,所以毫无疑问这里是取score[0],也就是1。第二个参数是*p3,也有p3=score,但是p3是long int *,在它执行*运算的时候会一次取long int长度的内容,也就是取4个字节。然而,一个short int类型只占2个字节,所以这里发生了访问溢出。那么这里到底取出来了什么呢?这就需要从内存里分析了。
程序内存分配分析
相信大家都很熟悉下面这张图:
这张图展示了计算机内存分配的顺序。我们知道,对于局部变量,系统在栈区从高地址向低地址分配内存。
而在数组内部则是从低地址向高地址分配内存。因此变量相对位置如表:
| 内存内容 | 内存属主 |
|---|---|
| 0x00FF2000 | p1 |
| 0x00FF2000 | p3 |
| 未知 | score[9] |
| 9 | score[8] |
| 8 | score[7] |
| 7 | score[6] |
| 6 | score[5] |
| 5 | score[4] |
| 4 | score[3] |
| 3 | score[2] |
| 2 | score[1] |
| 1 | score[0] |
当进行*p3操作时,从低地址向高地址进行操作,取出的即为score[0]和score[1]占用的内存空间。
计算机中的整数
一般来说,整数以补码的形式存储在内存中。
正数的补码等于其原码,负数的补码等于模减去其绝对值的原码
考虑到short int 占两个字节,也就是16位二进制,4位十六进制,那么可以得到:
|
|
在常用家用计算机(小端机)中,数字高位存储在高地址,低位存储在低地址,在虚拟内存中,最小操作单位是字节,也即两位16进制。因此在虚拟内存中存储情况类似:
| 内存内容 |
|---|
| 高地址 |
| 00H |
| 02H |
| 00H |
| 01H |
| 低地址 |
回到*p3
当*p3取数时,取出的即为上面这一串十六进制。我们按我们习惯的高位在前进行拼接,最终结果就是00020001H,即131073。
除此之外,我们还需要关注格式控制符。幸运的是,这里的格式控制符是%d,在Windows平台下刚好能够兼容*p1和*p3,因此没有什么问题。于是我们能够得出最后的结果:
|
|
总结
其实这是一道非常简单的关于内存分配的题目,不过涉及到比较复杂的内存分配机制问题,所以记个笔记来记录一下。