VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
123
返回列表 发新帖
楼主: VBProFan

一个令人疯掉的C指针问题,不是搞编译器的绝对想不到

[复制链接]
发表于 2010-6-20 15:11:50 | 显示全部楼层
1# VBProFan

只要你能理解指针是个什么玩意儿,这道题目就会很简单了。

我以前在和学弟学妹交流时,总会向他们强调:指针也仅仅是一种数据类型而已,只不过在多数编辑器内部的存储方式恰巧和整型一样而已(理论上它的确可以保存成浮点型)。而且指针这种数据类型在语义层次上支持加法和减法。只不过很多C语言教材都把指针作为单独一章来讲解(并且通常放置在书本的后半部分),给初学者潜意识里造成它和以前所学的数据类型不同的感觉。

当然,指针的加减法不能简单地理解为数值操作,比如:
  1. int a = 0;
  2. int *p = &a;
  3. p = p + 1;
复制代码

其中 p = p + 1 这一句里的“1”不能简单地理解为数值“1”,而应该理解为“一个单元”,而整句话的意思就是“该指针指向下一个单元”。

我们知道C语言里不同的基础类型所占用的空间大小不同,比如在Visual C++的实现中,long型是4个字节,char型是1个字节。这意味着,long型指针指向下一单元时,会前进4个字节;而char型指针指向下一单元只会前进1个单元,其他类型依此类推。

这也就是为什么“指针既然是一种数据类型,但却要给每个类型设置一个对应的指针”的原因,它是在告诉编译器这类指针一个单元的步长。


回到这道题目上,我们只看 int a[5] = {1, 2, 3, 4, 5};。大部分新手可能只注意到5个量:a[0]、a[1]、...、a[4],但在语义上来说,“a”也是一个量,它的类型是“长度为5的整型数组”,在Visual C++上,它占用空间大小是20个字节(可以用sizeof(a)得到)。

因此,原题中“int *ptr = (int *)(&a + 1);”这句话看起来诡异,但按照上面的分析,其实无法就是对这种“长度为5的整型数组”类型的指针做了一次加法操作,即前进1个单元(20个字节),最后把结果的类型强制转换为“整型指针”。

到目前为止,我们已经能给出一个理论上的答案了,这段程序的运行结果是给出“a”后面一个单元上的内容。那它到底是输出什么内容呢?以我所掌握的知识来说:这个结果是不确定的!因为C语言标准文档里没有规定程序运行时内存空间的分配方案,因此不同的厂商在实现的编译器在处理这个问题上都各不相同。

一个比较常见的方案是按照源代码中定义的顺序重下往上分配空间。比如题目中 a, b, c, ptr 都是局部变量,因此分配在“栈空间”里,c是第一个变量,因此在栈底,接着是a、b以及 ptr。在这种模式下会输出“7”(即c[0]的值)。

但这个问题还会牵涉到“操作系统的虚拟内存管理”、“编译器的优化”、“空间对齐”等诸多问题。比如你用GCC编译时,是否开启O2优化选项,产生的结果是不一样的。因此,确切地说这道题是条件不足,并不能给出唯一的结果。


关于指针和数组更详细的讨论,欢迎参看我以前的文章:《编程村一日游》http://blog.csdn.net/redraiment/archive/2010/01/10/5171510.aspx

评分

参与人数 3威望 +12 人气 +1 收起 理由
YaDa + 4 我很赞同
VBProFan + 4 打字辛苦啊~
szdan + 4 + 1 精品文章

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2010-6-20 21:11:20 | 显示全部楼层
复习到本帖的5楼时,无意中发现一个 Maxthon 浏览器的 bug:当选定一个字符串时,用鼠标一拖,本来是用搜索引擎搜索这个字符串的,但是如果选定一个数字加冒号开头的字符串时,例如“5: abc#%$#%”,再一拖,一个错误框就出现了。。。
回复 支持 反对

使用道具 举报

发表于 2010-6-28 12:56:41 | 显示全部楼层
学习一下.这个有点难呀.
回复 支持 反对

使用道具 举报

发表于 2010-8-16 19:38:01 | 显示全部楼层
int *ptr =  (int*)(&a+1); 看惯了VB,这个看上去很别扭

int* ptr =  (int*)(&a+1); 这样写就很习惯(可惜这样写的人不多),int* 整体看作是一个数据类型,ptr是变量。

C语言我就是习惯不了,唉------

有没有BASIC化的C语言?
回复 支持 反对

使用道具 举报

发表于 2010-8-16 19:57:29 | 显示全部楼层
24# YaDa

int* ptr =  (int*)(&a+1); 这样写就很习惯(可惜这样写的人不多),int* 整体看作是一个数据类型
因为你这样写从理论上来说是错的……按你这样写会习惯性写成int* a,b; 以为b也是int*,实际上b是int……应该写成int *a,*b ……
回复 支持 反对

使用道具 举报

发表于 2010-8-16 20:46:02 | 显示全部楼层
我们定义的是变量a,b,但 int *a,*b 看上去定义的是*a,*b。非常别扭。而*a,*b在编程的时候,指的是指向a,b的指针,int *a,*b 这样的定义感觉很容易误导思维。

int* ptr =  (int*)(&a+1);这样写,理论上没有错吧?只是适宜于一次定义一个变量。

我看懂C语言的教材很快,但一直不习惯C语言,所以特想要BASIC化的C语言。
回复 支持 反对

使用道具 举报

发表于 2010-8-16 21:46:41 | 显示全部楼层
26# YaDa

所以特想要BASIC化的C语言
Pascal ?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2010-8-16 23:15:38 | 显示全部楼层
本帖最后由 VBProFan 于 2010-8-16 23:20 编辑
int *ptr =  (int*)(&a+1); 看惯了VB,这个看上去很别扭

int* ptr =  (int*)(&a+1); 这样写就很习惯(可惜这样写的人不多),int* 整体看作是一个数据类型,ptr是变量。

C语言我就是习惯不了,唉------

有没有 ...
YaDa 发表于 2010-8-16 19:38


不知道你有没有看过林锐博士的《高质量C++/C编程指南》的2.6节?

2.6 修饰符的位置

修饰符 * 应该靠近数据类型还是该靠近变量名,是个有争议的活题。
若将修饰符 * 靠近数据类型,例如:int*  x; 从语义上讲此写法比较直观,即x是int 类型的指针。
上述写法的弊端是容易引起误解,例如:int*  x, y; 此处y容易被误解为指针变量。虽然将xy分行定义可以避免误解,但并不是人人都愿意这样做。


l【规则2-6-1应当将修饰符 * 紧靠变量名
例如:
char  *name;
int   *x, y;    // 此处y不会被误解为指针

评分

参与人数 1人气 +3 收起 理由
YaDa + 3 丑陋的C语言

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2010-8-16 23:18:50 | 显示全部楼层
我看懂C语言的教材很快,但一直不习惯C语言,所以特想要BASIC化的C语言。
YaDa 发表于 2010-8-16 20:46


你可自定义一种语言,叫做 YaDa C,然后为它写一个编译器

评分

参与人数 1人气 +3 收起 理由
YaDa + 3 好思路,但没这个水平。^v^

查看全部评分

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2023-3-22 04:59

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表