在Linux下工作,我刚刚遇到以下问题。 (当然,有人会给我答案,但到目前为止,我还没有找到任何简单明了的答案:)
/*compile with gcc -o out.x hello.c*/
#include<stdio.h>
int main()
{
printf("Hello World2\r\n");
printf("Hello World3\r\n ");
return 0;
}
在Linux下运行以下代码会产生两个字符串但结尾字符是不同的:第一个输出以0x0d结尾,而第二个以0x0d结尾,0x0a。
这是由编译器(GCC)完成的,如obj文件中所示:
Contents of section .rodata:
400610 01000200 48656c6c 6f20576f 726c6432 ....Hello World2
400620 0d004865 6c6c6f20 576f726c 64330d0a ..Hello World3..
400630 2000 .
所以,问题是:
由于
答案 0 :(得分:5)
在运行时创建格式化输出需要时间; printf
来电很慢。 GCC知道这一点,所以replaces第一个函数调用了puts
。由于puts
会自动添加\n
,因此GCC需要从字符串中删除\n
以进行补偿。
GCC这样做是因为它认为printf
为built-in。因为这对字节输出<甚至对write
的调用次数无影响;我强烈建议保持原样。如果您 想要禁用它,则can pass -fno-builtin-printf
,但唯一的效果是在尝试不必要地格式化字符串时减慢代码速度。
答案 1 :(得分:3)
ask GCC(在Linux / Debian / Sid / x86-64上使用GCC7.2)发出汇编程序更简单。所以我用
编译了你的程序bflash.c
gcc -fverbose-asm -O0 -S bflash.c -o bflash-O0.S
在没有优化的情况下获得它,并使用
gcc -fverbose-asm -O1 -S bflash.c -o bflash-O1.S
获得-O1
优化。您可以使用各种other optimization flags重复实验。
即使没有优化,bflash-O0.S
也包含:
.section .rodata
.LC0:
.string "Hello World2\r"
.LC1:
.string "Hello World3\r\n "
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
# bflash.c:5: printf("Hello World2\r\n");
leaq .LC0(%rip), %rdi #,
call puts@PLT #
# bflash.c:6: printf("Hello World3\r\n ");
leaq .LC1(%rip), %rdi #,
movl $0, %eax #,
call printf@PLT #
# bflash.c:8: return 0;
movl $0, %eax #, _4
# bflash.c:9: }
popq %rbp #
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
如您所见,第一个printf
已经优化为puts
;这是C11标准n1570(as-if rule)所允许的。顺便说一下,bflash-01.S
包含类似的代码。请注意,C11标准已经考虑了当前的优化实践(标准化委员会的许多成员都是编译器实现者)。
BTW Clang 5,调用为clang-5.0 -O1 -fverbose-asm -S bflash.c -o bflash-01clang.s
,执行相同类型的优化。
我怎样才能避免这种“优化”(!?)
关注Daniel H's answer(您可能会编译with -ffreestanding
,但我不建议这样做。)
或者避免使用printf
中的<stdio.h>
并实施自己较慢的打印功能。如果您实现自己的打印功能,请以不同的名称命名(因为printf
在C11标准中定义),也许(如果需要)编写您自己的GCC plugin以便按照您的方式进行优化(以及该插件)最好是一些免费软件is GPL compatible,阅读GCC runtime library exception)。
C语言规范(研究n1570)定义了semantics,即编译程序的行为。它不需要任何特定的字节序列出现在可执行文件中(标准中甚至可能没有提到)。如果您需要这样的属性,请找到不同的编程语言,并放弃所有重要的optimizations GCC正在努力为您做的事情。优化正在制作writing a C compiler difficult(如果你想要一个非优化的编译器,使用GCC以外的其他东西,但接受性能损失可能是三倍或更多,wrt代码用gcc -O2
编译) 。