printf("hello world")
最终在汇编代码中使用了更多的CPU指令(不考虑使用的标准库)而不是cout << "hello world"
?对于C ++,我们有:
movl $.LC0, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
对于C:
movl $.LC0, %eax
movq %rax, %rdi
movl $0, %eax
call printf
我正在使用gcc版本4.5.2
答案 0 :(得分:5)
对于Linux x86_64上的64位gcc -O3(4.5.0),其内容为: cout&lt;&lt; “Hello World”
movl $11, %edx ; String length in EDX
movl $.LC0, %esi ; String pointer in ESI
movl $_ZSt4cout, %edi ; load virtual table entry of "cout" for "ostream"
call _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
和 printf(“Hello World”)
movl $.LC0, %edi ; String pointer to EDI
xorl %eax, %eax ; clear EAX (maybe flag for printf=>no stack arguments)
call printf
这意味着,您的序列完全取决于任何特定的 编译器实现,其版本和可能的编译器选项。 您的编辑状态,您使用gcc 4.5.2(这是相当新的)。 似乎4.5.2引入了额外的64位寄存器 这个顺序无论出于什么原因它将64位RAX保存到RDI 在把它归零之前 - 这绝对没有意义(至少对我而言)。
更有趣:3个参数调用序列( g ++ -O1 -S source.cpp ):
void c_proc()
{
printf("%s %s %s", "Hello", "World", "!") ;
}
void cpp_proc()
{
std::cout << "Hello " << "World " << "!";
}
导致( c_proc ):
movl $.LC0, %ecx
movl $.LC1, %edx
movl $.LC2, %esi
movl $.LC3, %edi
movl $0, %eax
call printf
.LCx是字符串,没有涉及堆栈指针!
对于 cpp_proc :
movl $6, %edx
movl $.LC4, %esi
movl $_ZSt4cout, %edi
call _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
movl $6, %edx
movl $.LC5, %esi
movl $_ZSt4cout, %edi
call _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
movl $1, %edx
movl $.LC0, %esi
movl $_ZSt4cout, %edi
call _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
你现在看到的是这一切。
此致
RBO
答案 1 :(得分:1)
调用者代码大部分时间与性能无关。
我猜C ++代码的第2行将std :: cout的地址存储为运算符的隐式“this”参数&lt;&lt;方法
我可能在C部分错了,但在我看来它是不完整的。 rax的32位上半部分未在此片段中初始化,可能会在之前初始化。(不,我在这里错了)。
似乎printf将al(eax的LSB)的值作为输入告诉printf()有多少xmm 128寄存器用作输入。它看起来像是一个优化,能够将输入字符串传递给xmm寄存器或其他一些有趣的业务。
答案 2 :(得分:1)
int printf( const char*, ...)
是一个可变函数,可以接受一个或多个参数;而ostream& operator<< (ostream&, signed char*)
恰好是两个。我认为这说明了调用它们所需的指令差异。
C ++反汇编中的第2行是它通过ostream&amp; (在这种情况下cout
)。所以函数知道它输出的是什么流对象。
由于两者最终都进行了函数调用,因此这种比较在很大程度上是无关紧要的;在函数调用中执行的代码将更加重要。运算符&lt;&lt;对于许多右侧类型重载,并在编译时解决;另一方面,printf()必须在运行时解析格式字符串以确定数据类型,因此可能会产生额外的开销。无论哪种方式,在函数内执行的代码量将根据执行的指令淹没调用开销,并且几乎肯定会受到在图形显示器上呈现文本所需的OS代码的支配。所以总之,你是冒汗的小东西。
答案 3 :(得分:0)
movl移动很长,32位移动
movq是移动四元组,64位移动
printf有一个返回值,无论是写入的字符数还是失败时的-1,并且该值存储在%eax中,这就是所有额外的行都在担心。