关于函数返回指针的问题

36 views
Skip to first unread message

rclijia

unread,
Jul 31, 2011, 10:17:04 PM7/31/11
to Linux C/C++/系统编程 一站式学习
代码结构大体如下:

自己实现了一个内存池,
void *my_malloc(uint size)
{
void *p1;
......; //从内存池中申请了一块内存

return p1;
}


int main(int argc, char *argv[])
{
......;

void *p2 = malloc(100);
}

通过GDB调试,在my_malloc函数里,p1的值是0x7ffff430c0b0,
可main函数里赋值给p2后,p2的值竟然成了0xffffffff430c0b0,高位全自动补1了,
这是为什么啊?
测试环境: LINUX 2.6.32(64Bit), gcc 4.4.3

Li Haifeng

unread,
Aug 1, 2011, 10:58:52 AM8/1/11
to learning-l...@googlegroups.com


2011/8/1 rclijia <rcl...@gmail.com>
 
目前在Windows机器下不方便测试。
 
两个值,一个是无符号,一个是有符号。你强制转化为unsigned long 打印一下,看看情况



--
Li Haifeng
Laboratory of Service Computing Technology and System
Home page:http://tek-life.org

rclijia

unread,
Aug 3, 2011, 1:16:34 AM8/3/11
to Linux C/C++/系统编程 一站式学习
自己问,自己答:
原因如下:
在libnids.c中,没有包含my_malloc()函数的声明,编译器就会假定类型为: int my_malloc().
在32位机器上没有问题,
但是在64位的机器上,把Int型赋值给指针变量,那肯定就错了!

这种情况编译器有警告,可我没注意,程序运行起来肯定就挂了!


On 8月1日, 下午10时58分, Li Haifeng <omy...@gmail.com> wrote:
> 2011/8/1 rclijia <rcli...@gmail.com>

rclijia

unread,
Aug 3, 2011, 1:21:56 AM8/3/11
to Linux C/C++/系统编程 一站式学习
谢谢 Li Haifeng!

也怪我没描述的特别详细,这两个函数不在同一个源文件里,
否则,我想很多人一下就能看出问题所在了!

Li Haifeng

unread,
Aug 3, 2011, 2:35:17 AM8/3/11
to learning-l...@googlegroups.com
2011/8/3 rclijia <rcl...@gmail.com>

自己问,自己答:
原因如下:
在libnids.c中,没有包含my_malloc()函数的声明,编译器就会假定类型为: int my_malloc().
 
应该不是声明的问题。声明只是在预编译的时候起作用,在链接的时候,还是引用定义的函数。

如有错误,请指正。

宋劲杉

unread,
Aug 3, 2011, 3:10:46 AM8/3/11
to learning-l...@googlegroups.com
类型转换不对,出运行时错误是肯定的。
正因为链接能链接上,所以才能编译出程序来,所以才有机会出运行时错误。

2011/8/3 Li Haifeng <omy...@gmail.com>



--
宋劲杉
北京浪弯融科科技有限公司
地址:北京市中关村东路66号世纪科贸大厦B座2308   邮编:100080
电话:62672521 62672591
传真:62672501
Email:songj...@tvie.com.cn
公司网站:http://www.tvie.com.cn

rclijia

unread,
Aug 3, 2011, 4:04:19 AM8/3/11
to Linux C/C++/系统编程 一站式学习
编译时,只是针对本文件内的代码,
由于碰到了未定义的函数,而且还没有声明,那返回值无法事先预知啊,
此时编译器默认返回值为int型,
但实际上代码为:void *p = my_malloc(100), 本意是想得到64位的数据,
编译器生成的实际汇编代码只会复制低32位数据,
实际链接时,发现了my_malloc()的实际定义和之前猜测的不一样,
编译器会打印警告信息,但错误已经发生了,没机会改了。
链接器只是把my_malloc的函数入口地址填补上而已,

我是这么想的,不知道对不对?!


On 8月3日, 下午3时10分, 宋劲杉 <songjins...@gmail.com> wrote:
> 类型转换不对,出运行时错误是肯定的。
> 正因为链接能链接上,所以才能编译出程序来,所以才有机会出运行时错误。
>
> 2011/8/3 Li Haifeng <omy...@gmail.com>
>
>
>
>
>
>
>
>
>

> > 2011/8/3 rclijia <rcli...@gmail.com>

> Email:songjins...@tvie.com.cn <songjins...@gmail.com>
> 公司网站:http://www.tvie.com.cn

宋劲杉

unread,
Aug 3, 2011, 4:07:27 AM8/3/11
to learning-l...@googlegroups.com
应该就是这样。

2011/8/3 rclijia <rcl...@gmail.com>
Email:songj...@tvie.com.cn
公司网站:http://www.tvie.com.cn

Li Haifeng

unread,
Aug 3, 2011, 5:45:50 AM8/3/11
to learning-l...@googlegroups.com


2011/8/3 rclijia <rcl...@gmail.com>

编译时,只是针对本文件内的代码,
由于碰到了未定义的函数,而且还没有声明,那返回值无法事先预知啊,
此时编译器默认返回值为int型,
但实际上代码为:void *p = my_malloc(100), 本意是想得到64位的数据,
编译器生成的实际汇编代码只会复制低32位数据,
实际链接时,发现了my_malloc()的实际定义和之前猜测的不一样,
编译器会打印警告信息,但错误已经发生了,没机会改了。
链接器只是把my_malloc的函数入口地址填补上而已,

我是这么想的,不知道对不对?!

不对

宋劲杉

unread,
Aug 3, 2011, 5:48:44 AM8/3/11
to learning-l...@googlegroups.com
那你觉得是怎么样的呢?
Email:songj...@tvie.com.cn
公司网站:http://www.tvie.com.cn

rclijia

unread,
Aug 3, 2011, 6:09:14 AM8/3/11
to Linux C/C++/系统编程 一站式学习
这个问题大家可以讨论一下了,
弄清楚源代码从预处理、编译、汇编、链接,到最终可执行程序,
这个过程中系统都干神马了,还是很有意义的,呵呵

On 8月3日, 下午5时48分, 宋劲杉 <songjins...@gmail.com> wrote:
> 那你觉得是怎么样的呢?
>
> 2011/8/3 Li Haifeng <omy...@gmail.com>
>
>
>
>
>
>
>
>
>
>
>

> > 2011/8/3 rclijia <rcli...@gmail.com>

siqiao chen

unread,
Aug 3, 2011, 12:44:02 PM8/3/11
to learning-l...@googlegroups.com
宋老师,您的教材里有提过隐式声明会当成int func(...) 的规则。

请问这个规则为什么会存在阿?感觉除了增加出错率,这玩艺没有任何好处么?


2011/8/3 宋劲杉 <songj...@gmail.com>

Li Haifeng

unread,
Aug 3, 2011, 8:49:06 PM8/3/11
to learning-l...@googlegroups.com
2011/8/4 siqiao chen <siqia...@gmail.com>

宋老师,您的教材里有提过隐式声明会当成int func(...) 的规则。

请问这个规则为什么会存在阿?感觉除了增加出错率,这玩艺没有任何好处么?
 
不明白为很么会增加出错率。

声明只是在编译的时候起作用,告诉编译器,这个函数是在其他文件,或者是在后面定义的,暂时先不用管。

为什么要声明呢?是因为在调用之前编译器找不到函数的声明,所以会编译出错,既然能编译通过,说明在调用之前,编译起已经扫描到原函数了,不用再次声明。

按照lz说的情况,不声明照样 能编译过去,你再声明,就没有任何必要。

至于声明的返回值问题。
1。c语言不是c++,不支持重载,它被汇编后,函数的名字是不更改的;并不像c++那样汇编结束后函数的名字前面需要加上返回值类型,后面需要加上参数的个数以及参数的类型。
2。它并不影响最终结果,换句话说,它不影响最终的可执行文件。

因此,lz所述的错误根源不在于你声明的时候,返回值类型匹不匹配。

 如果有错误,请批评指正。

宋劲杉

unread,
Aug 3, 2011, 9:16:22 PM8/3/11
to learning-l...@googlegroups.com
我在书中有提到过吧,是old style C的历史遗留问题。只有加这么一条规则,old style C代码才能编译过。

2011/8/4 siqiao chen <siqia...@gmail.com>

宋劲杉

unread,
Aug 3, 2011, 9:17:46 PM8/3/11
to learning-l...@googlegroups.com
rclijia和siqiao chen是对的。

2011/8/4 Li Haifeng <omy...@gmail.com>

rclijia

unread,
Aug 3, 2011, 9:42:23 PM8/3/11
to Linux C/C++/系统编程 一站式学习
回复 Li Haifeng:

" 声明只是在编译的时候起作用,告诉编译器,这个函数是在其他文件,或者是在后面定义的,暂时先不用管。

为什么要声明呢?是因为在调用之前编译器找不到函数的声明,所以会编译出错,既然能编译通过,说明在调用之前,编译起已经扫描到原函数了,不用再次声
明。
"

你的这两句话,第一行是OK的,
第二行的后半句就说不通了。

编译的时候找不到my_malloc()的声明与定义,但此时还要用到my_malloc的返回值,
那从返回值的起始地址开始,往后是读取1个字节,还是2个字节,还是4个字节......?不确定啊
这个时候和预留函数入口地址还不一样,此时此刻是必须要生成实际汇编指令的,
只能假定个返回值类型,编译器就默认为Int, 取4个字节(但这里错了,实际是想取8个字节),然后生成了对应的汇编代码,
函数的入口地址此时还未知,这个可以留个位置与记号,后面的链接器会补充。

把所有的.c文件都编译完后,链接器会把所有的.o文件链接成一个可执行文件,
这其中会把之前预留的my_malloc()函数的入口地址补充上,所以程序才能运行起来,

但运行的时候,执行到这一句:
void *p = my_malloc(100);

实际的效果已经类似 : void *p = (int) my_malloc(100), 高32位数据被截断了,
再往下执行, 只要操作p指针,就可能会段错误。

为什么是可能段错误呢?
一个侥幸的结果就是,通过my_malloc()申请的内存在堆上,一般都是类似0x6xxxxx开头的地址,
地址总位数不超过32位,那程序运行就没问题了,
但这是个"不定时的定时炸弹",随时会爆炸的!

我觉得应该是这么回事。
我是宋老师当年在亚嵌的学生,呵呵!

On 8月4日, 上午9时17分, 宋劲杉 <songjins...@gmail.com> wrote:
> rclijia和siqiao chen是对的。
>
> 2011/8/4 Li Haifeng <omy...@gmail.com>
>
>
>
>
>
>
>
>
>

> > 2011/8/4 siqiao chen <siqiaoc...@gmail.com>


>
> >> 宋老师,您的教材里有提过隐式声明会当成int func(...) 的规则。
>
> >> 请问这个规则为什么会存在阿?感觉除了增加出错率,这玩艺没有任何好处么?
>
> > 不明白为很么会增加出错率。
>
> > 声明只是在编译的时候起作用,告诉编译器,这个函数是在其他文件,或者是在后面定义的,暂时先不用管。
>
> > 为什么要声明呢?是因为在调用之前编译器找不到函数的声明,所以会编译出错,既然能编译通过,说明在调用之前,编译起已经扫描到原函数了,不用再次声明。
>
> > 按照lz说的情况,不声明照样 能编译过去,你再声明,就没有任何必要。
>
> > 至于声明的返回值问题。
>
> > 1。c语言不是c++,不支持重载,它被汇编后,函数的名字是不更改的;并不像c++那样汇编结束后函数的名字前面需要加上返回值类型,后面需要加上参数的个数以及参数的类型。
> > 2。它并不影响最终结果,换句话说,它不影响最终的可执行文件。
>
> > 因此,lz所述的错误根源不在于你声明的时候,返回值类型匹不匹配。
>
> > 如果有错误,请批评指正。
>

> >> 2011/8/3 宋劲杉 <songjins...@gmail.com>


>
> >>> 那你觉得是怎么样的呢?
>
> >>> 2011/8/3 Li Haifeng <omy...@gmail.com>
>

> >>>> 2011/8/3 rclijia <rcli...@gmail.com>

Li Haifeng

unread,
Aug 4, 2011, 8:36:36 AM8/4/11
to learning-l...@googlegroups.com
咦,原来这样啊~,万分感谢。

2011/8/4 rclijia <rcl...@gmail.com>

Yuan, Tao

unread,
Aug 4, 2011, 8:53:59 PM8/4/11
to learning-l...@googlegroups.com
据我所知,返回值通常放在寄存器,对应不同的长度,分别是al, ax, eax, rax.

void *p = my_malloc(100);

指针变量p 在栈上, 分配了8个字节空间。
但是在赋值的时候,因为编译器认为是int,
所以生成的汇编代码只能是类似 MOV QWORD PTR [RSP + x], EAX

楼主正确的地址是 0x7ffff430c0b0 ,也就是eax里面是F430C0B0,最高位(符号位)是1,
所以扩展为QWORD就成了 0xFFFFFFFF F430C0B0.

按照64bit 系统的内存分配
http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/mem.html#mmap),
这个地址显然在512G 以上,一定是内核地址空间的,所以一定会出现访问异常。


2011/8/3 rclijia <rcl...@gmail.com>:

Reply all
Reply to author
Forward
0 new messages