Qual dos dois você prefere (2)?

Pros amadores, defensores do C++, eis a rotininha basica mais usada para introduzir uma linguagem ao estudante: Hello, world!. Dessa vez, ambas escritas em C e C++ e seus equivalentes em assembly. Me diz qual você acha que é melhor, ok?

;------ C code -----
; #include <stdio.h>
; void main(void) { puts("Hello"); }
;-------------------
  bits 64

  section .rodata
hellostr: db  'Hello',0

  section .text

  extern puts

  global main
main:
  mov   rdi,hellostr
  jmp   puts

Agora veja o código em C++… Note que os nomes das funções são tão grandes que a formatação para evitar a barra de scroll na parte de baixo da listagem não é possível…

;------- C++ code -----
; #include <iostream>
; int main() { std::cout << "Hello\n"; }
;----------------------
  bits 64

  section .rodata
hellostr: db  'Hello',0x0a,0

  section .text

  ; MACACOS ME MORDAM!
  ; Precisa mesmo de 7 símbolos externos para
  ; imprimir uma stringzinha?!
  extern _ZSt4cout
  extern _ZSlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 
  extern _ZStL8_ioinit
  extern _ZNSt8ios_base4InitC1Ev
  extern _ZNSt8ios_base4InitD1Ev
  extern __dso_handle
  extern __cxa_atexit

  global main
main:
  sub   rsp,8
  mov   rsi,hellostr
  mov   rdi,_ZSt4cout
  call  _ZSlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  xor   eax,eax
  add   rsp,8
  ret

_GLOBAL__sub_I_main:
  sub   rsp,8
  mov   rdi,_ZStL8_ioinit
  call  _ZNSt8ios_base4InitC1Ev
  mov   rdx,__dso_handle
  mov   rsi,_ZStL8__ioinit
  mov   rdi,_ZNSt8ios_base4InitD1Ev
  add   rsp,8
  jmp   __cxa_atexit

Credo! E olha que usei a otimização máxima… E quanto a usar a otimização mínima?! Usando a chave -O0, obtemos algo assim para as duas funções:

; código em C:
main:
  push rbp
  mov  rbp,rsp
  mov  rdi,hellostr
  call puts
  pop  rbp
  ret

Agora, segure-se na cadeira ao ver o código criado pelo C++:

; Código em C++:
main:
  push rbp
  mov  rbp,rsp
  mov  rsi,hellostr
  mov  rdi,_ZSt4cout
  call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  mov  eax,0    
  pop  rbp
  ret

_Z41__static_initialization_and_destruction_0ii:
  push rbp
  mov  rbp,rsp
  sub  rsp,16
  mov  [rbp-4],edi
  mov  [rbp-8],esi
  mov  dword [rbp-4],1
  jne  .L3
  cmp  dword [rbp-8],0xffff
  jne  .L3
  mov  rdi,_ZStL8__ioinit
  call _ZNSt8ios_base4InitC1Ev
  mov  rdx,__dso_handle
  mov  rsi,_ZStL8__ioinit
  mov  rdi,_ZNSt8ios_base4InitD1Ev
  call __cxa_atexit
.L3:
  leave
  ret

_GLOBAL__sub_I_main:
  push rbp
  mov  rbp,rsp
  mov  esi,0xffff
  mov  edi,1
  call _Z41__static_initialization_and_destruction_0ii
  pop  rbp
  ret

Caramba! Os mesmos 7 símbolos e mais uma função e ainda com uns 2 saltos condicionais!

Agora me diz: Os códigos finais, gerados pelo compilador C++ são ou não uma sopa de letrinhas? Se eu te der um código em assembly, gerado pelo g++, você seria capaz de entendê-lo? Olhando para esses códigos ai em cima, nas variantes C, a chamada a puts é bem evidente (Opa! Então estamos imprimindo uma string usando stdout!), mas não podemos dizer o mesmo dos códigos compilados C++…

Afinal, o que _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc significa? E essas funções estáticas adicionais? Com um pouco de experiência você saberá que essas funções adicionais são relacionadas ao destrutor da classe basic_ostream, mas que é confuso pacas, é…

Anúncios