将C++转换为C

Convert C++ to C

本文关键字:转换 C++      更新时间:2023-10-16

假设我把一个c++程序带到哪里,并将其编译成一个程序集(.S)文件。

然后,我将该汇编文件"disassembly"到C中,该代码是否可以在不同的平台上重新编译?

我问这个问题的原因是,我试图开发的平台没有c++编译器,而它确实有c编译器。

是的,按照您描述的方式,这确实是可能的。不,它不会移植到任何CPU体系结构、操作系统和编译器三元组,除了你的。

让我们看看原因。拿一些基本的C++代码。。。

#include <iostream>
int main()
{
std::cout << "Hello, world!n";
return 0;
}

让我们在x86-64 Linux上使用g++将其转换为汇编程序(我特意打开了优化,并丢弃了调试符号)。。。

$ g++ -o test.s -O3 -S test.cpp

结果是…

.file   "test.cpp"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello, world!n"
.section    .text.unlikely,"ax",@progbits
.LCOLDB1:
.section    .text.startup,"ax",@progbits
.LHOTB1:
.p2align 4,,15
.globl  main
.type   main, @function
main:
.LFB1027:
.cfi_startproc
subq    $8, %rsp
.cfi_def_cfa_offset 16
movl    $.LC0, %esi
movl    $_ZSt4cout, %edi
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
xorl    %eax, %eax
addq    $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1027:
.size   main, .-main
.section    .text.unlikely
.LCOLDE1:
.section    .text.startup
.LHOTE1:
.section    .text.unlikely
.LCOLDB2:
.section    .text.startup
.LHOTB2:
.p2align 4,,15
.type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1032:
.cfi_startproc
subq    $8, %rsp
.cfi_def_cfa_offset 16
movl    $_ZStL8__ioinit, %edi
call    _ZNSt8ios_base4InitC1Ev
movl    $__dso_handle, %edx
movl    $_ZStL8__ioinit, %esi
movl    $_ZNSt8ios_base4InitD1Ev, %edi
addq    $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1032:
.size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section    .text.unlikely
.LCOLDE2:
.section    .text.startup
.LHOTE2:
.section    .init_array,"aw"
.align 8
.quad   _GLOBAL__sub_I_main
.local  _ZStL8__ioinit
.comm   _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident  "GCC: (GNU) 5.3.1 20151207 (Red Hat 5.3.1-2)"
.section    .note.GNU-stack,"",@progbits

这种混乱就是我们为异常处理、模板和名称空间付出的代价。让我们手动将其分解为C,丢弃异常处理表以获得更清晰的视图。。。

/* std::ostream */
typedef struct
{
/* ... */
} _ZSo;
/* extern operator<<(std::basic_ostream&, const char*); */
extern _ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc();
/* namespace std { extern ostream cout; } */
extern _ZSo _ZSt4cout;
/* Our string, of course! */
static const char* LC0 = "Hello, world!n";
int main()
{
_ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(&_ZSt4cout, LC0);
return 0;
}

看起来不可读,但可移植,对吧?现在是这样。但是,这个代码不起作用!您还需要用struct的术语定义_ZSo(std::ostream),更不用说包括所有的异常处理内容了。

一旦你开始使用try/throw/catch,事情就变得几乎不可能便携。我的意思是,__cxa_allocate_exception__cxa_throw绝对不可能是便携的!解决这一问题的唯一方法是重写所有程序(包括您使用的所有库,甚至是标准库!),以使用较慢的setjmp/longjmp方法处理异常,而不是零成本异常处理。

最后,但并非最不重要的是,即使是在最简单的输入下,非人类反汇编程序也很可能无法正确完成这项工作。请记住,在最底层的东西(也称为机器代码和汇编语言)中,没有类型的概念。例如,你永远不可能知道寄存器是有符号整数、无符号整数,还是基本上是其他的。编译器还可以随意使用堆栈指针,从而使作业恶化。

编译结束后,编译器不希望将来进行任何反汇编,会删除所有这些宝贵的信息,因为在运行时通常不需要这些信息。在大多数情况下,如果可能的话,拆卸真的不值得,而且很可能你正在寻求不同的解决方案。从中等偏上的语言翻译成中等偏下的语言将这一点发挥到了极致,并接近了可以翻译成其他语言的极限。