首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >“函数隐式声明”与函数原始版本的区别

“函数隐式声明”与函数原始版本的区别
EN

Stack Overflow用户
提问于 2013-05-03 22:14:28
回答 2查看 2.7K关注 0票数 1

我使用gcc4.8。我写了这样的代码,使用睡眠。

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    /* I know it's worong to pass a floating number to sleep
     * this is only for testing. */
    sleep(0.001);               
    return 0;
}

我用"gcc函数A。C -o a“编译,得到警告”隐式声明函数‘-Wall’-Wimplicit function --o“。然后我运行它,这个程序休眠了大约1秒(看起来休眠间隔是0.001比1)。

然后我将代码更改为如下所示:

代码语言:javascript
复制
#include <unistd.h> /* add header file */
int main(int argc, char *argv[])
{
    /* I know it's worong to pass a floating number to sleep
     * this is only for testing. */
    sleep(0.001);               
    return 0;
}

这一次它只休眠了0.001秒,看起来像是休眠楼层0.001到0。

这两个睡眠不应该是一样的吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-05-03 22:18:32

在第一种(错误的)情况下,一个实际的浮点值被赋予休眠状态,因为假设sleep的原型接受一个浮点值(实际上是一个double )。sleep会将这个double的比特表示解释为一个int,并等待这么多秒。您很幸运,这只有1秒。在第二种情况下,浮点值被强制转换为舍入为0的int

票数 4
EN

Stack Overflow用户

发布于 2020-11-30 14:21:02

IMHO,请始终使用-Werror=implicit-function-declaration编译选项以防止编译器/链接器的智能默认行为造成的损坏。

通过将这两个案例编译成两个可执行文件sleep_include_no(第一个错误案例,不包含)和sleep_include_yes(第二个正常案例,包含),我做了一些简单的测试:

代码语言:javascript
复制
## 'sleep' will invoke the 'nanosleep', and use strace to show real duration
$ strace ./sleep_include_no 2>&1 | grep nanosleep
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7ffee0165970) = 0

$ strace ./sleep_include_yes 2>&1 | grep nanosleep
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=0}, 0x7ffce92eedd0) = 0

从gdb disas命令输出中截取的asm代码:

代码语言:javascript
复制
/* 1st wrong case (without include), compiler/linker's default behavior, \
   an immediate constant '0x1' is set into %eax as parameter of sleep */
=> 0x0000555555555141 <+8>:     movsd  0xebf(%rip),%xmm0        # 0x555555556008
   0x0000555555555149 <+16>:    movsd  %xmm0,-0x8(%rbp)
   0x000055555555514e <+21>:    mov    -0x8(%rbp),%rax
   0x0000555555555152 <+25>:    movq   %rax,%xmm0
   0x0000555555555157 <+30>:    mov    $0x1,%eax
   0x000055555555515c <+35>:    call   0x555555555030 <sleep@plt>

/* 2nd ok case (with include), \
   the 'cvttsd2si' instruction is something like cast double to int */
=> 0x0000555555555141 <+8>:     movsd  0xebf(%rip),%xmm0        # 0x555555556008
   0x0000555555555149 <+16>:    movsd  %xmm0,-0x8(%rbp)
   0x000055555555514e <+21>:    movsd  -0x8(%rbp),%xmm0
   0x0000555555555153 <+26>:    cvttsd2si %xmm0,%rax
   0x0000555555555158 <+31>:    mov    %eax,%edi
   0x000055555555515a <+33>:    call   0x555555555030 <sleep@plt>

所以编译器/链接器只是让它工作,但可能不是您所期望的。我认为这是因为编译器有太多的遗留特性需要处理和兼容性考虑,我们不应该责怪它。作为一名程序员,我唯一能做的就是使用-Werror=implicit-function-declaration发出强制警报。

P.S.分享一个通过不包括用户定义的API头文件并忽略函数警告的隐式声明而引发的血腥生产错误。以下是演示代码(3个源文件):

代码语言:javascript
复制
$ cat my_lib.h
#ifndef _my_lib_h_
#define _my_lib_h_
long long get_i64_from_my_lib(); 
#endif

$ cat my_lib.c
#include "my_lib.h"
long long get_i64_from_my_lib() {
        return 113840990519587; /* 6789 ABCD 0123 */
}

$ cat main.c 
#include <stdio.h>
/* #include "my_lib.h" (without this include, the result is buggy) */
int main() {
        long long i64 = get_i64_from_my_lib();
        printf("%lld, %0lx\n", i64, i64);
}

$ gcc -g -c my_lib.c
$ ar -cq my_lib.a my_lib.o
$ gcc -g -o my_exe main.c my_lib.a  ## emit implicit-function-declaration warning

## The returned type is not an expected i64, but a truncated i32. \
## When the returned value is less than 2^31, the function seems ok, \
## so it is an evil bug.
$ ./my_exe 
-1412628189, ffffffffabcd0123
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16361006

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档