1

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

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 -Wall ac -o a”编译它,得到警告“函数'sleep'的隐式声明[-Wimplicit-function-declaration]”。然后我运行它,这个程序睡眠大约 1 秒(似乎睡眠细胞 0.001 到 1)。

然后我把代码改成这样:

#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 秒,好像睡眠层从 0.001 到 0。

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

4

2 回答 2

4

在第一种(错误的)情况下,一个真正的浮点值被赋予休眠状态,因为假设 的原型sleep采用浮点值(double实际上是 a)。sleep会将它的位表示解释double为 anint并等待这么多秒。你很幸运,这只有 1 秒。在第二种情况下,浮点值被转换为一个int四舍五入为 0 的值。

于 2013-05-03T14:18:31.530 回答
0

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

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

## '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

disas来自 gdb命令输出的 asm 代码片段:

/* 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发出强制警报。

PS 分享一个由于不包含用户定义的 API 头文件和忽略函数警告的隐式声明而引发的血腥生产错误。这是演示代码(3个源文件):

$ 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
于 2020-11-30T06:21:02.253 回答