首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么叫<puts>?

为什么叫<puts>?
EN

Stack Overflow用户
提问于 2022-02-18 15:08:12
回答 2查看 115关注 0票数 1

我试图在gdb中看到一个简单C程序的分解二进制文件。

C程序:

代码语言:javascript
复制
int main(){
        int i = 2;
        if (i == 0){
                printf("YES, it's 0!\n");
        }else{
                printf("NO");
        }
        return 0;
}

拆卸指令:

代码语言:javascript
复制
   0x0000000100401080 <+0>:     push   rbp
   0x0000000100401081 <+1>:     mov    rbp,rsp
   0x0000000100401084 <+4>:     sub    rsp,0x30
   0x0000000100401088 <+8>:     call   0x1004010e0 <__main>
   0x000000010040108d <+13>:    mov    DWORD PTR [rbp-0x4],0x2
   0x0000000100401094 <+20>:    cmp    DWORD PTR [rbp-0x4],0x0
   0x0000000100401098 <+24>:    jne    0x1004010ab <main+43>
   0x000000010040109a <+26>:    lea    rax,[rip+0x1f5f]        # 0x100403000
   0x00000001004010a1 <+33>:    mov    rcx,rax
   0x00000001004010a4 <+36>:    call   0x100401100 <puts>
   0x00000001004010a9 <+41>:    jmp    0x1004010ba <main+58>
   0x00000001004010ab <+43>:    lea    rax,[rip+0x1f5b]        # 0x10040300d
   0x00000001004010b2 <+50>:    mov    rcx,rax
   0x00000001004010b5 <+53>:    call   0x1004010f0 <printf>
   0x00000001004010ba <+58>:    mov    eax,0x0
   0x00000001004010bf <+63>:    add    rsp,0x30
   0x00000001004010c3 <+67>:    pop    rbp
   0x00000001004010c4 <+68>:    ret
   0x00000001004010c5 <+69>:    nop

我想这个指令,

代码语言:javascript
复制
0x00000001004010a4 <+36>:    call   0x100401100 <puts>

指点

代码语言:javascript
复制
printf("YES, it's 0!\n");

现在让我们假设是的,那么我怀疑为什么在这里调用<push>,而在0x00000001004010b5 <+53>: call 0x1004010f0 <printf>中调用<printf>

EN

回答 2

Stack Overflow用户

发布于 2022-02-18 15:26:33

使用C标准中定义的语义,printf("YES, it's 0!\n")产生与puts("YES, it's 0!")相同的输出,这可能更有效,因为不需要对字符串进行替换分析。

由于不使用返回值,编译器可以将printf调用替换为对puts的等效调用。

这种类型的优化很可能是为了减少经典K&R程序hello.c的可执行大小而引入的。将printf替换为puts避免了链接比puts大得多的printf代码。在您的例子中,这种优化是适得其反的,因为putsprintf都是链接的,但是现代系统使用动态链接,因此尝试以这种方式减少可执行文件的大小已经不再有意义了。

您可以在此戈德波特编译器浏览器页面上播放编译器设置,以观察编译器行为:

  • 即使使用-O0gcc也执行printf / puts替换,但是clang没有,这两个编译器都为两个调用生成代码,而不是优化测试if (i == 0),这在禁用优化时是可以的。我怀疑gcc团队甚至在没有优化的情况下也无法抵制对尺寸基准的偏见。
  • 对于-O1和更高版本,这两个编译器只为else分支生成代码,调用printf
  • 如果将第二个字符串更改为"N",则printf将转换为对putchar的调用,这是另一个优化。
票数 3
EN

Stack Overflow用户

发布于 2022-02-18 15:24:44

这是个优化。

使用没有格式说明符和尾随换行符的格式字符串调用printf,相当于使用删除尾随换行符的相同字符串调用puts

由于printf有很多处理格式说明符的逻辑,但是puts只编写给定的字符串,后者将更快。因此,在第一次调用printf的情况下,编译器会看到这种等价性,并进行适当的替换。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71175676

复制
相关文章

相似问题

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