函数原型与定义的问题

小魔菇 2010-06-18 10:16:08
我在文件a.c中定义一个函数


//a.c
void display(float d,char i)
{
printf("float = %f,char = %x \n",d,i);
}

然后在另一个文件b.c中使用它

//void display(float d,char i);
int main()
{
float d = 10.0;
char j = 3;
display(d,j);
}


gcc -c a.c b.c
gcc a.o b.o -o a

我在b.c中加上函数的声明void display(float d,char i);就成功了
没有加上函数的声明,该函数也可以调用 但是打印的结果是错的

加上声明,这样可以让函数的声明与定义是一致的 所以可以执行正确
没有加上的话 main为什么会执行错?它会到哪儿去找函数的声明?
...全文
122 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
小魔菇 2010-06-19
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 brookmill 的回复:]

声明了函数:位于ebp-12的float变量,通过eax直接入栈了
未声明函数:通过flds和fstpl去FPU的栈上绕了一圈又出来,好像就变成double了。
我一向就不懂浮点数,刚才翻了指令集也没看明白这两条指令具体是怎么操作的。
可以参考这两个帖子:
http://topic.csdn.net/t/20060309/10/4602466.html
http://topic.csd……
[/Quote]
好的 谢谢
小魔菇 2010-06-18
  • 打赏
  • 举报
回复
声明了函数

16 movsbl -5(%ebp),%eax
17 movl %eax, 4(%esp)
18 movl -12(%ebp), %eax
19 movl %eax, (%esp)
20 call display

未声明函数

16 movsbl -5(%ebp),%eax
17 flds -12(%ebp)
18 movl %eax, 8(%esp)
19 fstpl (%esp)
20 call display


确实可以看的到声明的第17行,是把char变量放在ebp+4,因为float是4个字节
而未声明的在18行可以看到 把char变量放在ebp+8,因为编译的时候把float转化成了double型,是8个字节
brookmill 2010-06-18
  • 打赏
  • 举报
回复
声明了函数:位于ebp-12的float变量,通过eax直接入栈了
未声明函数:通过flds和fstpl去FPU的栈上绕了一圈又出来,好像就变成double了。
我一向就不懂浮点数,刚才翻了指令集也没看明白这两条指令具体是怎么操作的。
可以参考这两个帖子:
http://topic.csdn.net/t/20060309/10/4602466.html
http://topic.csdn.net/t/20051229/11/4488098.html
小魔菇 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 brookmill 的回复:]

引用 9 楼 eclipse_2 的回复:
当函数没有声明的时候 参数就转化为double或者int,就算我传的是float或者char的时候,也是压入8个字节或4个字节到堆栈中去的
但是在执行的时候发现参数原来是float或者char,就只取了堆栈中的4个字节或者1个字节,取的是低地址的内容,所以都是0了。
不知道这样想的是不是对的 还请指教。

这个和我的理解完全一致, 不过我也不……
[/Quote]
非常感谢!
yzx714 2010-06-18
  • 打赏
  • 举报
回复
没声明时,参数升级了吧,float升级为double,int不变;所以打印错误。是C陷阱与缺陷还是C专家编程讲过这个问题,我记不太清楚了
brookmill 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 eclipse_2 的回复:]
当函数没有声明的时候 参数就转化为double或者int,就算我传的是float或者char的时候,也是压入8个字节或4个字节到堆栈中去的
但是在执行的时候发现参数原来是float或者char,就只取了堆栈中的4个字节或者1个字节,取的是低地址的内容,所以都是0了。
不知道这样想的是不是对的 还请指教。
[/Quote]
这个和我的理解完全一致, 不过我也不是很有把握。
刚查了c99标准,找到这么一段:
6.5.2.2 Function calls
......
6 If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions.
小魔菇 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 brookmill 的回复:]

所谓链接,只是解析符号而已。编译产生的目标文件里面的具体的代码,是不会再去修改了。调用函数的时候如何传参、如何压栈,就是目标文件里面代码的一部分。
楼主如果有兴趣,可以用gcc -S选项看看产生的汇编代码,然后对比一下有没有display声明的区别。
[/Quote]
恩 正在看
就是看不懂 呵呵
小魔菇 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 brookmill 的回复:]

引用 5 楼 eclipse_2 的回复:
编译的时候没有发生警告 解析那些未识别的符号应该是在连接的时候解析的吧
编译的时候把float和char全部转换成double和int
连接的时候发现错了,就转换回来了?
但是打印的结果是
float = 0.000000,char = 0

怎么解释啊

函数名是在链接的时候解析的。但是,调用display函数的时候如何传参数,是在……
[/Quote]

哦 原来是这样的
当函数没有声明的时候 参数就转化为double或者int,就算我传的是float或者char的时候,也是压入8个字节或4个字节到堆栈中去的
但是在执行的时候发现参数原来是float或者char,就只取了堆栈中的4个字节或者1个字节,取的是低地址的内容,所以都是0了。
不知道这样想的是不是对的 还请指教。
brookmill 2010-06-18
  • 打赏
  • 举报
回复
所谓链接,只是解析符号而已。编译产生的目标文件里面的具体的代码,是不会再去修改了。调用函数的时候如何传参、如何压栈,就是目标文件里面代码的一部分。
楼主如果有兴趣,可以用gcc -S选项看看产生的汇编代码,然后对比一下有没有display声明的区别。
brookmill 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 eclipse_2 的回复:]
编译的时候没有发生警告 解析那些未识别的符号应该是在连接的时候解析的吧
编译的时候把float和char全部转换成double和int
连接的时候发现错了,就转换回来了?
但是打印的结果是
float = 0.000000,char = 0

怎么解释啊
[/Quote]
函数名是在链接的时候解析的。但是,调用display函数的时候如何传参数,是在编译的时候就确定了的,链接的时候已经无法补救了。
打印的结果不对,是因为main里面按照double和int的格式压栈,而display里面按照float和char的格式去栈上取,自然就错位了。
brookmill 2010-06-18
  • 打赏
  • 举报
回复
如果gcc加了-Wall选项,会有一个警告
b.c:6: warning: implicit declaration of function `display'
这个“implicit declaration”就是默认的声明。由于main见到的参数是一个float和一个char,他的默认声明可能就是 int display(double d,int i)
小魔菇 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 dobzhansky 的回复:]

编译阶段对于未声明的函数会有警告, 默认整形了吧,

连接时刻补救了
发生了转换
[/Quote]
编译的时候没有发生警告 解析那些未识别的符号应该是在连接的时候解析的吧
编译的时候把float和char全部转换成double和int
连接的时候发现错了,就转换回来了?
但是打印的结果是
float = 0.000000,char = 0

怎么解释啊
brookmill 2010-06-18
  • 打赏
  • 举报
回复
如果a.c里面的函数是void display(double d,int i)
那么b.c里面即使没有声明,结果也是对的。
所以,main里面调用display的时候,可能是把浮点数转换成double格式压栈,整型(包括char/short)按照int格式压栈。

brookmill 2010-06-18
  • 打赏
  • 举报
回复
编译b.c的时候,如果调用display之前没见过display的函数声明,编译器不会去任何地方找,而是会根据调用的情况来自动生成一个函数声明。
在这里的display会被默认为返回int。至于参数是怎么声明的,我得再想想……
steptodream 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 dobzhansky 的回复:]

编译阶段对于未声明的函数会有警告, 默认整形了吧,

连接时刻补救了
发生了转换
[/Quote]
惊现么个大牛!
Dobzhansky 2010-06-18
  • 打赏
  • 举报
回复
编译阶段对于未声明的函数会有警告, 默认整形了吧,

连接时刻补救了
发生了转换

23,121

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧