设为首页
优惠IDC
收藏本站
六狼博客
六狼论坛
开启辅助访问
切换到窄版
用户名
Email
自动登录
找回密码
密码
登录
立即注册
只需一步,快速开始
只需一步,快速开始
快捷导航
门户
首页
BBS
云计算
大数据
手机
移动开发android,ios,windows phone,windows mobile
编程
编程技术java,php,python,delphi,ruby,c,c++
前端
WEB前端htmlcss,javascript,jquery,html5
数据库
数据库开发Access,mysql,oracle,sql server,MongoDB
系统
操作系统windows,linux,unix,os,RedHat,tomcat
架构
项目管理
软件设计,架构设计,面向对象,设计模式,项目管理
企业
服务
运维实战
神马
搜索
搜索
热搜:
php
java
python
ruby
hadoop
sphinx
solr
ios
android
windows
centos
本版
帖子
用户
六狼论坛
»
首页
›
编程技术
›
C
›
一道C语言试题 ---转
返回列表
查看:
34
|
回复:
0
一道C语言试题 ---转
[复制链接]
fp_moon
fp_moon
当前离线
积分
326
窥视卡
雷达卡
升级
42%
当前用户组为
举人
当前积分为
326
, 升到下一级还需要 174 点。
96
主题
96
主题
96
主题
举人
举人, 积分 326, 距离下一级还需 174 积分
举人, 积分 326, 距离下一级还需 174 积分
积分
326
发消息
楼主
|
发表于 2013-1-26 12:33:55
|
显示全部楼层
|
阅读模式
int main(void)
{
int a[4] = {1, 2, 3, 4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x, %x/n", ptr1[-1], *ptr2);
return 0;
}
问,在x86平台下输出啥?
题目虽然恶心了点,但作为一个例子来分析,还是挺好玩的。学过C语言的朋友可以暂且不看下文,自己试着分析一下,看看结果跟我的是否一样,也不失为一件趣事。
下面把这道题牵涉到的所有边边角角的问题梳理一遍,详细讨论如下:
1、
&a+1
首先明确,a是一个具有4个整型变量的数组的名字,具体地说是这种数组的首元素的首地址,而&a是数组的首地址,请注意措辞。而关于指针加1,则需要指针运算的知识。没学过指针运算或者已经忘记了这个知识点的朋友们,下面就是关于指针运算你需要知道的事实:
就像上面的例子那样,式子&a+1表示的是指针加法运算,而不是普通的数值加法运算,之所以会这样是因为&a是一个指针而非普通数值(虽然它本质上也是一个整数)。那么你会问:加入此时&a=0xFFFF5700,那么&a+1是多少呢?
答案是:取决于&a的类型
。
a) 如果&a是一个指向char型的指针,那么&a+1 = 0xFFFF5701
b) 如果&a是一个指向short型的指针,那么&a+1 = 0xFFFF5702
c) 如果&a是一个指向int型的指针,那么&a+1 = 0xFFFF5704 (32位机器)
d) 如果&a是一个指向某种结构体struct foo的指针,那么&a+1 = 0xFFFF5700+sizeof(struct foo)
……
还没看出端倪吗?没错,指针加1不是指针内容简单地加1,而是
让指针指向下一个数据
,加2就是让指针指向下两个数据,这个数据的类型就是指针指向的类型,所以指针的加法究竟会让这个指针指向哪里,取决于这个指针指向的数据类型。
因此,综上所述,当&a与整数1做加法时,实际上是指针的加法,加1的含义是:令指针a指向
下一个数据
,下一个数据是啥?当然是紧挨着的下一个具有4个整型变量的数组了(因为&a的类型是指向具有4个整型变量的数组的指针嘛),于是a的指向了4的下一个地址,在用此值初始化ptr1,因此ptr1的指向如图所示:
由于在ptr1初始化的时候,令&a+1强制转换成整型指针(关于类型转换的详细讨论请参考http://blog.csdn.net/seton040/archive/2009/10/19/4699869.aspx),因此ptr1[-1]相当于把ptr1往前挪一个整型大小,即4个字节。 如下图:
显然,打印出的第一个数字是a[3]的内容,即数值4.
当然,我们还必须说明一个事实:数组下标是可以为负数的,实际上,取下标符“[ ]”的内部实现,就是指针运算!比如a[2],等价于*(a+2),即以a地址为基址,取偏移量为2的地址的值。所以ptr1[-1]等价于*(ptr-1)。
2、
(int *)((int)a+1)
至于指针ptr2的处理更恶心一点,呵呵!它先是把数组名a强制转换成整型变量,然后再加1,然后再强制转换成整型指针!真罗嗦,但不要紧,咱有的是耐性,无非就是让ptr2指向a[0]的第二个字节罢了,此时ptr2指向如下图所示:
显然,此时打印的内容就是ptr2所指向的往后4个字节的内容了,也就是a{0}的后三个字节和a[1]的第一个字节,那究竟会打印出啥玩意儿呢?上面的图没有画出里面的内容,要是把每个字节的内容都画出来就好了,呵呵!
要把上图中每一个字节的内容都打印出来没问题,但是你先要知道
字节序
的概念,字节序分两种,一种叫
大端
字节序(big-endian)
,当然除此之外必然有
小端
字节序(little-endian)
,让我们用一个问题,来引出字节序的概念,然后再来搞定这两个小鬼吧!
问题是这样的:对多字节存储的变量,机器是如何做出解释的??请看下图:
假如这是一个普通的int变量,地球人都知道,在32机器上int占用4个字节存储数据,就像上图中显示的那样,在4个字节中放置了一堆数字,但是机器究竟会把这个数解释成0x0103070F呢,还是解释成0x0F070301呢?
答案是:都有可能!
官方解释:
1、所谓大端(big-endian)序,就是
高优先位
对应
高有效位
。
2、所谓小端(little-endian)序,就是
高优先位
对应
低有效位
。
民间解释:
1、所谓大端(big-endian)序,就是读取或者存放数据时,
最低
位
对应
高地址
。
2、所谓小端(big-endian)序,就是读取或者存放数据时,
最低
位
对应
低地址
。
如,要把0x0103070F存放进存储器中时,如果把0E放进高地址处则是小端序,如果把0F放进低地址处则是大端序。照此,上图中存放的数据如果被机器理解成0x0103070F则是该机器是大端序的,否则若被理解成0x0F070301则是小端序的。
回到原来的问题,此时ptr2指向了a[0]的第二个字节。我们以x86平台为例(小端序),此时其内部数据分布是这样的:
由于x86平台是小端序的,根据咱刚刚讨论过的理论,小端序的存取时
最低
位
对应
低地址
,因此将会打印出0200 0000,如果题目中没有说明在x86平台,那答案是不确定的,取决于具体的平台,例如ARM平台就是大端序的。
到此这个试题基本算是圆满解决了,但是我还想再罗嗦一下,关于代码中的那个a,以及那个&a,估计还是有很多朋友心里不怎么平坦吧,每次遇到数组啊指针啊,虽然程序好像仿佛貌似也没出啥毛病,但总感觉是混过去的,这次我们要彻底解决它。记得某人说过,一个真正的程序员,必须对自己写的代码的每一个细节了如指掌。
再来考虑这个定义:
int a[4] = {1, 2, 3, 4};
这时我们必须明确,编译器根据我们提供的类型和数组大小,为我们分配了适当大小的存储区域,并且把这块存储区域叫做a,
请注意
:&a,就像我们上面提到的,它的类型是“指向具有4个整型变量数组的指针“,简而言之&a是一个数组指针。这个数组的名字a,当它作为右值(简单而言,出现在赋值号“=”左边的就是左值,出现在赋值号“=”右边的就是右值)时代表的是数组首元素的首地址,而不是数组的首地址。此时a的意义跟&a[0]是等价的,都是指向首元素的指针。记住咯!
数组的名字会在另一个地方与指针等价,那就是函数参数表,当出现在函数的参数表里面的时候,不管你写的是数组,还是指针,一旦进入函数内部,通通变成指针。
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
显身卡
返回列表
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
本版积分规则
发表回复
回帖后跳转到最后一页
浏览过的版块
C++
os
Copyright © 2008-2020
六狼论坛
(https://it.6wolf.com) 版权所有 All Rights Reserved.
Powered by
Discuz!
X3.4
京ICP备14020293号-2
本网站内容均收集于互联网,如有问题请联系
QQ:389897944
予以删除
快速回复
返回顶部
返回列表