两件事:
我认为您实际上并没有使用gcc -m64 hello.c。您得到的错误通常是由于使用 C 编译器编译 C++ 代码,而执行 gcc -m64 hello.cc- 之类的操作。
shell% gcc -m64 hello.c
shell% ./a.out
hello world [added missing newline]
shell% cp hello.c hello.cc
shell% gcc -m64 hello.cc
Undefined symbols:
"___gxx_personality_v0", referenced from:
_main in ccYaNq32.o
CIE in ccYaNq32.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
您可以通过以下方式“让它发挥作用”:
shell% gcc -m64 hello.cc -lstdc++
shell% ./a.out
hello world
其次,-m64 不是指定您希望在 Mac OS X 上生成 64 位代码的首选方式。首选方式是使用 -arch ARCH,其中 ARCH是ppc、ppc64、i386 或x86_64 之一。根据您的工具的设置方式(即 iPhone ARM、ppc64 已弃用等),可能有更多(或更少)可用的架构。另外,在 10.6 上,gcc 默认为-arch x86_64,或者默认生成 64 位代码。
使用这种风格,可以让编译器自动创建“胖二进制文件”——你可以多次使用-arch。例如,要创建“通用二进制”:
shell% gcc -arch x86_64 -arch i386 -arch ppc hello.c
shell% file a.out
a.out: Mach-O universal binary with 3 architectures
a.out (for architecture x86_64): Mach-O 64-bit executable x86_64
a.out (for architecture i386): Mach-O executable i386
a.out (for architecture ppc7400): Mach-O executable ppc
编辑: 添加了以下内容以回答 OP 的问题“我确实犯了一个错误,并调用我的文件 .cc 而不是 .c。我仍然对为什么这很重要感到困惑?”
嗯...这是一个复杂的答案。我会做一个简短的解释,但我会要求你有点相信“实际上有一个很好的理由”。
公平地说,“编译程序”是一个相当复杂的过程。出于历史和实际原因,当您执行gcc -m64 hello.cc 时,它实际上在幕后分解为几个离散的步骤。这些步骤中的每一个通常都会将每个步骤的结果提供给下一步,它们大约是:
- 在正在编译的源代码上运行 C 预处理器
cpp。此步骤负责执行所有#include 语句、各种#define 宏扩展和其他“预处理”内容。
- 在 C 预处理结果上正确运行 C 编译器。这一步的输出是一个
.s文件,或者是C代码编译成汇编语言的结果。
- 在
.s 源上运行as 汇编程序。这会将汇编语言汇编成一个.o 目标文件。
- 在
.o 文件上运行ld 链接器,以将各种已编译的目标文件以及各种静态和动态链接库链接到一个可用的可执行文件中。
注意:这是大多数编译器的“典型”流程。编译器的单独实现不必遵循上述步骤。出于性能原因,一些编译器将多个步骤合二为一。例如,gcc 的现代版本不使用单独的cpp 通行证。另一方面,tcc 编译器一次性执行上述所有步骤,无需使用额外的外部工具或中间步骤。
在上述传统的编译器工具链流程中,cc(或者,在我们的例子中,gcc)命令被称为“编译器驱动程序”。它是上述所有工具和步骤的“逻辑前端”,并且知道如何智能地应用所有步骤和工具(如汇编器和链接器)以创建最终的可执行文件。但是,为了做到这一点,它通常需要知道它正在处理的“类型”文件。例如,您不能真正将组装好的 .o 文件提供给 C 编译器。因此,有几个“标准”.* 名称用于指定文件的“种类”(有关详细信息,请参阅man gcc):
-
.c, .h C 源代码和 C 头文件。
-
.mObjective-C 源代码。
-
.cc, .cp, .cpp, .cxx, .c++ C++ 源代码。
-
.hhC++ 头文件。
-
.mm, .M Objective-C++ 源代码。
-
.s 汇编语言源代码。
-
.o 汇编的目标代码。
-
.aar存档或静态库。
-
.dylib动态共享库。
也可以使用各种编译器标志来覆盖这种“自动确定的文件类型”(请参阅man gcc 了解如何执行此操作),但通常更容易坚持标准约定,以便一切“只是自动工作”。
另外,如果您在原始示例中使用了 C++“编译器驱动程序”或 g++,则不会遇到此问题:
shell% g++ -m64 hello.cc
shell% ./a.out
hello world
原因是gcc 本质上说“在驱动工具链时使用 C 规则”,g++ 说“在驱动工具链时使用 C++ 规则”。 g++ 知道要创建一个可以工作的可执行文件,它需要将 -lstdc++ 传递给链接器阶段,而 gcc 显然不认为这是必要的,即使它知道在“编译源代码”处使用 C++ 编译器代码”阶段,因为 .cc 文件结尾。
Mac OS X 10.6 上默认可用的其他一些 C/C++ 编译器:gcc-4.0、gcc-4.2、g++-4.0、g++-4.2、llvm-gcc、llvm-g++、llvm-gcc-4.0、 llvm-g++-4.0、llvm-gcc-4.2、llvm-g++-4.2、clang。这些工具(通常)交换了工具链流程中的前两个步骤,并使用相同的较低级别的工具,如汇编器和链接器。 llvm- 编译器使用 gcc 前端解析 C 代码并将其转换为中间表示,然后使用 llvm 工具将该中间表示转换为代码。由于llvm 工具使用“低级虚拟机”作为其接近最终的输出,它允许更丰富的优化策略集,最值得注意的是它可以跨不同的已编译的.o 文件执行优化.这通常称为link time optimization。 clang 是一个全新的 C 编译器,它还以 llvm 工具作为其输出,允许进行相同类型的优化。
那么,就这样吧。关于为什么gcc -m64 hello.cc 对您失败的不那么简短的解释。 :)
编辑:还有一件事......
将gcc 和g++ 等命令链接到同一个“一体化”编译器驱动程序可执行文件是一种常见的“编译器驱动程序技术”。然后,在运行时,编译器驱动程序检查用于创建进程的路径和文件名,并根据该文件名是否以gcc 或g++(或等效)结尾来动态切换规则。这允许编译器的开发人员重用大部分前端代码,然后只需更改两者之间所需的少量差异。