两件事情:
我认为您实际上没有使用gcc -m64 hello.c
。您收到的错误通常是这样做的结果:gcc -m64 hello.cc
——使用C编译器来编译C ++代码。
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
其次,在Mac OS X上指定生成64位代码的首选方式不是使用-m64
,而是使用-arch ARCH
,其中ARCH是ppc
、ppc64
、i386
或x86_64
之一。根据你的工具设置情况,可能会有更多(或更少)的体系结构可用(即iPhone ARM、ppc64已弃用等)。另外,在10.6上,gcc
默认为-arch x86_64
或默认生成64位代码。
使用这种风格,编译器可以自动创建“fat二进制文件”-可以多次使用-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源代码和头文件。
.m
:Objective-C源代码。
.cc
、.cp
、.cpp
、.cxx
、.c++
:C++源代码。
.hh
:C++头文件。
.mm
、.M
:Objective-C++源代码。
.s
:汇编语言源代码。
.o
:已汇编的目标代码。
.a
:ar
档案或静态库。
.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
文件之间执行优化。这通常称为“链接时优化”。clang
是一个全新的C编译器,它也将llvm
工具作为其输出目标,从而允许进行相同类型的优化。
所以,这就是为什么gcc -m64 hello.cc
对您来说失败的不那么短的解释。:)
编辑:还有一件事...
“编译器驱动程序技术”的常见做法是将命令如gcc
和g++
符号链接到同一个“多合一”编译器驱动程序可执行文件。然后,在运行时,编译器驱动程序会检查用于创建进程的路径和文件名,并根据文件名是否以gcc
或g++
(或等效名称)结尾动态切换规则。这使得编译器的开发人员可以重用大部分前端代码,然后只需更改两者之间需要的少量差异。