本章描述了如何使用 C++ 编译器。
任何编译器的主要用途是将高级语言(如 C++)编写的程序转换成目标计算机硬件可执行的数据文件。您可以使用 C++ 编译器完成以下任务:
将源文件转换成可重定位的二进制 (.o) 文件,以后链接到可执行文件、静态(归档)库 (.a) 文件(使用 – xar)或动态(共享)库 (.so) 文件中
将目标文件或库文件(或两者)链接或重链接成可执行文件
在运行时调试处于启用状态的情况下编译可执行文件 (-g)
使用运行时语句或过程级文件配置 (-pg) 编译可执行文件
本节简要概述了如何使用 C++ 编译器编译和运行 C++ 程序。有关命令行选项的完整参考,请参见16.8 使用 dlopen 从 C 程序访问 C++ 库。
本章中的命令行示例说明了 CC 的用法。打印输出可能会稍有不同。
生成和运行 C++ 程序的基本步骤包括:
使用编辑器创建 C++ 源文件(后缀为表 2–1 中所列有效后缀之一)
调用编译器来生成可执行文件
通过输入可执行文件的名称来启动程序
以下程序在屏幕上显示消息:
example% cat greetings.cc #include <iostream> int main() { std::cout << “Real programmers write C++!” << std::endl; return 0; } example% CC greetings.cc example% ./a.out Real programmers write C++! example% |
在此示例中, CC 编译源文件 greetings.cc,并且在缺省情况下编译可执行程序生成文件 a.out。要启动该程序,请在命令提示符下键入可执行文件的名称 a.out。
传统的方法是,UNIX 编译器为可执行文件命名 a.out。每次编译都写入到同一个文件是比较笨拙的方法。另外,如果已经有这样一个文件存在,下次运行编译器时该文件将被覆盖。因此,改用 -o 编译器选项来指定可执行输出文件的名称,如以下示例所示:
example% CC– o greetings greetings.cc |
在此示例中,-o 选项通知编译器将可执行代码写入文件 greetings。(由单独源文件组成的程序通常提供无后缀的源文件名称。)
也可以在每次编译后使用 mv 命令来为缺省的 a.out 文件重命名。无论是哪种方法,都可以通过键入可执行文件的名称来运行程序:
example% ./greetings Real programmers write C++! example% |
本章其余部分讨论了 CC 命令使用的约定、编译器源代码行指令和其他有关编译器的使用问题。
CC [options] [source-files] [object-files] [libraries] |
选项是前缀为短划线 (–) 或加号 (+) 的选项关键字。某些选项带有参数。
通常,编译器选项的处理顺序是从左到右,从而允许有选择地覆盖宏选项(包含其他选项的选项)。在大多数的情况下,如果您多次指定同一个选项,那么最右边的赋值会覆盖前面的赋值,而不会累积。注意以下特殊情况:
所有链接程序选项和 -features、–I -l、– L、-library、–pti、–R、-staticlib、-U、-verbose、-xdumpmacros 和 -xprefetch 选项都会累积,但它们不会覆盖。
所有 –U 选项都在所有 –D 选项之后处理。
源文件、目标文件和库按它们在命令行上出现的顺序编译并链接。
在以下示例中,在启用了运行时调试的情况下,使用 CC 编译两个源文件(growth.C 和 fft.C)来生成名为 growth 的可执行文件:
example% CC -g -o growth growth.C fft.C |
命令行上附加在文件名后面的后缀确定了编译器处理文件的方式。如果文件名称的后缀没有在下表中列出,或文件名称没有后缀,那么都要传递到链接程序。
表 2–1 C++ 编译器识别的文件名称后缀
后缀 |
语言 |
操作 |
---|---|---|
C++ |
以 C++ 源文件编译,将目标文件放在当前目录中;目标文件的缺省名称是源文件名称加上 .o 后缀。 |
|
C++ |
操作与 .c 后缀相同。 |
|
C++ |
操作与 .c 后缀相同。 |
|
C++ |
操作与 .c 后缀相同。 |
|
C++ |
操作与 .c 后缀相同。 |
|
C++ |
操作与 .c 后缀相同。 |
|
C++ |
将预处理程序输出文件作为 C++ 源文件处理。操作与 .c 后缀相同。 |
|
汇编程序 |
使用汇编程序的汇编源文件。 |
|
汇编程序 |
使用 C 语言预处理程序和汇编程序的汇编源文件。 |
|
内联扩展 |
处理内联扩展的汇编内联模板文件。编译器将使用模板来扩展选定例程的内联调用。(内联模板文件是特殊的汇编文件。请参见 inline(1) 手册页。 |
|
目标文件 |
将目标文件传递到链接程序。 |
|
静态(归档)库 |
将目标库名传递到链接程序。 |
|
动态(共享)库 |
将共享对象的名称传递到链接程序。 |
C++ 编译器在命令行上接受多个源文件。编译器编译的单个源文件和其直接或间接支持的任何文件一起统称为编译单元。C++ 将每个源作为一个单独的编译单元处理。
缺省情况下,该编译器不使用高速缓存。只有指定了 -instances=extern 时,它才使用高速缓存。如果编译器使用高速缓存,它会检查高速缓存目录的版本,并在遇到高速缓存版本问题时发出错误消息。以后的 C++ 编译器也会检查缓存的版本。例如,具有不同模板缓存版本标识的未来版本编译器在处理此发行版本的编译器生成的缓存目录时,会发出与以下消息类似的错误:
Template Database at ./SunWS_cache is incompatible with this compiler |
编译器遇到新版本的编译器生成的缓存目录时,也会发出类似的错误。
升级编译器时,最好清除缓存。可对包含模板高速缓存目录(大多数情况下,模板高速缓存目录名为 SunWS_cache))的每个目录运行 CCadmin -clean。也可以使用 rm -rf SunWS_cache。
本节描述了编译和链接程序的某些方面。在以下示例中,使用 CC 编译三个源文件并链接目标文件以生成名为 prgrm 的可执行文件。
example% CC file1.cc file2.cc file3.cc -o prgrm |
在前面的示例中,编译器会自动生成加载器目标文件(file1.o、file2.o 和 file3.o),然后调用系统链接程序来为 prgrm 文件创建可执行程序。
编译后,目标文件(file1.o、file2.o 和 file3.o)仍保留不变。此约定让您易于重新链接和重新编译文件。
如果只编译了一个源文件,且在同一个操作中链接了程序,则对应的 .o 文件将会被自动删除。要保留所有 .o 文件,就不要在同一个操作中进行编译和链接,除非编译多个源文件。
如果编译失败,您将收到每个错误的对应消息。对于那些出现错误的源文件,不会生成 .o 文件,也不会生成可执行程序。
可以在不同的步骤中进行编译和链接。如果使用 -c 选项,将编译源文件并生成 .o 目标文件,但不创建可执行文件。如果不使用 -c 选项,编译器将调用链接程序。通过将编译和链接步骤分开,仅修复一个文件就不需要完整重新编译。以下示例显示了如何以独立的步骤编译一个文件并与其他文件链接:
example% CC -c file1.cc Make new object file example% CC -o prgrm file1.o file2.o file3.o Make executable file |
请确保链接步骤列出了生成完整程序所需的全部目标文件。如果在此步骤中缺少任何目标文件,链接将会失败,并出现 "undefined external reference" 错误(缺少例程)。
如果在不同的步骤中进行编译和链接,则使用3.3.3 编译时选项和链接时选项中所列的编译器选项时,一定要在编译和链接时保持一致。
如果使用其中任何选项编译子程序,必须在链接时也使用相同的选项:
如果使用 -library、-fast、-xtarget 和 -xarch 选项,必须确保包括相应的链接程序选项,如果编译和链接同时进行的话,就可以忽略这些链接程序选项。使用 —dryrun 查看这些选项的扩展以确定链接步骤中所需的选项。
如果使用 -p、-xpg 和 -xprofile,则在一个阶段中包括选项而在其他阶段中不包括相应选项并不影响程序的正确性,但不能进行文件配置。
如果使用 -g 和 -g0,则在一个阶段中包括选项而在其他阶段中不包括相应选项并不影响程序的正确性,但会影响调试程序的能力。对于没有使用其中任一选项编译但使用 -g 或 -g0 链接的模块,将无法正确调试。请注意,要对包含函数 main 的模块进行调试,通常必须使用 -g 选项或 -g0 选项对其进行编译。
在以下示例中,使用 -library=stlport4 编译器选项编译程序。
example% CC -library=stlport4 sbr.cc -c example% CC -library=stlport4 main.cc -c example% CC -library=stlport4 sbr.o main.o -o myprogram |
如果没有一致地使用 -library=stlport4,程序的某些部分将使用缺省的 libCstd,其他部分将使用可选的替换 STlport 库。生成的程序可能不进行链接,因此在任何情况下都不能正常运行。
如果程序使用模板,某些模板可以在链接时实例化。在这种情况下,来自最后一行(链接行)的命令行选项将用于编译实例化的模板。
使用新的 -m64 选项可指定目标编译的内存模型。生成的可执行文件仅能在运行 64 位内核的 Solaris OS 或 Linux OS 下的 64 位 UltraSPARC 或 x86 处理器上运行。64 位对象的编译链接和执行只能在支持 64 位执行的 Solaris 或 Linux OS 上进行。
使用 -V 选项可显示 CC 调用的每个程序的名称和版本号。使用 -v 选项可显示 CC 调用的完整命令行。
使用 —verbose=%all 可显示有关编译器的其他信息。
命令行上编译器无法识别的任何参数都解释为链接程序选项、目标程序文件名或库名称。
基本区别是:
对于无法识别的非选项(即前面没有短划线或加号),不会生成警告。(然而,这些选项会传递到链接程序。如果链接程序无法识别它们,将会生成链接程序错误消息。)
在以下示例中,请注意,CC 无法识别 -bit,该选项传递给链接程序 (ld),它会尝试解释该选项。因为一个字母的 ld 选项可以连在一起,所以链接程序将 -bit 视为 -b -i -t,所有这些都是合法的 ld 选项。这可能并不是您所希望看到的结果:
example% CC -bit move.cc < - -bit is not a recognized CC option CC: Warning: Option -bit passed to ld, if ld is invoked, ignored otherwise |
在下一个示例中,用户本想键入 CC 选项 -fast,但遗漏了前导短划线。编译器又一次将参数传递到链接程序,而链接程序将参数解释为文件名称:
example% CC fast move.cc < - The user meant to type -fast move.CC: ld: fatal: file fast: cannot open file; errno=2 ld: fatal: File processing errors. No output written to a.out |
C++ 编译器软件包由前端、优化器、代码生成器、汇编程序、模板预链接程序和链接编辑器组成。CC 命令会自动调用其中每个组件,除非使用命令行选项进行其他指定。
因为这些组件中的任何一个都可能生成错误,并且各个组件执行不同的任务,所以标识生成错误的组件是有意义的。可使用 -v 和 -dryrun 选项帮助解决此问题。
正如下表所示,不同编译器组件的输入文件拥有不同的文件名后缀。后缀建立了要进行的编译类型。有关文件后缀的含义,请参阅表 2–1。
表 2–2 C++ 编译系统的组件
组件 |
说明 |
使用说明 |
---|---|---|
前端(编译器预处理程序和编译器) | ||
代码优化器 |
-xO[2-5], -fast |
|
x86: 中间语言转换器 |
-xO[2-5], -fast |
|
SPARC: 汇编语言模板的内联扩展 |
指定 .il 文件 |
|
汇编程序 | ||
SPARC: 代码生成器、内联函数、汇编程序 |
|
|
ube |
x86: 代码生成器 |
-xO[2-5], -fast |
模板预链接程序 |
仅与 -instances=extern 选项一起使用 |
|
链接编辑器 |
本节讨论了关于预处理 C++ 编译器所特有的指令的信息。
预处理程序指令 pragma 是 C++ 标准的一部分,但每个编译器中,pragma 的形式、内容和含义都不相同。有关 C++ 编译器可识别的 pragma 的详细信息,请参见附录 B。
Sun C++ 还支持 C99 关键字 _Pragma。这两种调用
#pragma dumpmacros(defs) _Pragma("dumpmacros(defs)") |
是等效的。要使用 _Pragma 而不是 #pragma,请将 pragma 文本写成文字字符串(用括号括起来作为 _Pragma 关键字的一个参数)。
C++ 编译器接受以下形式的 #define 预处理程序指令。
#define identifier (...) replacement_list #define identifier (identifier_list, ...) replacement_list |
如果列出的宏参数以省略号结尾,那么该宏的调用允许使用除了宏参数以外的其他更多参数。其他参数(包括逗号)收集到一个字符串中,宏替换列表中的名称 __VA_ARGS__ 可以引用该字符串。以下示例说明了如何使用可变参数列表的宏。
#define debug(...) fprintf(stderr, __VA_ARGS__) #define showlist(...) puts(#__VA_ARGS__) #define report(test, ...) ((test)?puts(#test):\ printf(__VA_ARGS__)) debug(“Flag”); debug(“X = %d\n”,x); showlist(The first, second, and third items.); report(x>y, “x is %d but y is %d”, x, y); |
其结果如下:
fprintf(stderr, “Flag”); fprintf(stderr, “X = %d\n”, x); puts(“The first, second, and third items.”); ((x>y)?puts(“x>y”):printf(“x is %d but y is %d”, x, y)); |
附录中的表 A–2 介绍了预定义的宏。可以在 #ifdef 这样的预处理程序条件中使用这些值。+p 选项可防止自动定义 sun、unix、sparc 和 i386 预定义宏。
发出警告后,#error 指令不再继续编译。指令原来的行为是发出警告并继续编译。其新行为(和其他编译器保持一致)是发出错误消息并立即停止编译。编译器退出并报告失败。
编译需要的内存量取决于多个参数,包括:
每个过程的大小
优化级别
为虚拟内存设置的限制
磁盘交换文件的大小
在 SPARC 平台上,如果优化器用完了所有内存,那么它将通过在较低优化级别上重试当前过程来尝试恢复。然后优化器将以在命令行上通过 -xOlevel 选项指定的原始级别恢复后续例程。
如果编译包括大量例程的单独源文件,编译器可能会用完所有内存或交换空间。可以尝试降低优化级别。或者,将最大的过程分为其自身的单独文件。
swap -s 命令用于显示可用的交换空间。有关更多信息,请参见 swap(1M) 手册页。
以下示例演示了 swap 命令的用法:
example% swap -s total: 40236k bytes allocated + 7280k reserved = 47516k used, 1058708k available |
使用 mkfile(1M) 和 swap(1M) 可增加工作站上交换空间的大小。(您必须成为超级用户才能执行该操作)。mkfile 用于命令创建特定大小的文件,而 swap -a 用于将文件增加到系统交换空间:
example# mkfile -v 90m /home/swapfile /home/swapfile 94317840 bytes example# /usr/sbin/swap -a /home/swapfile |
在 -xO3 或更高级别编译非常大型的例程(一个过程中有数几千行代码)需要大量内存。在这种情况下,系统性能可能降低。您可以通过限制单个进程的可用虚拟内存量来控制这种情况。
要在 sh shell 中限制虚拟内存,请使用 ulimit 命令。有关更多信息,请参见 sh(1) 手册页。
以下示例显示了如何将虚拟内存限制为 4GB:
example$ ulimit -d 4000000 |
在 csh shell 中,可使用 limit 命令限制虚拟内存。有关更多信息,请参见 csh(1) 手册页。
下一个示例也显示了如何将虚拟内存限制为 4GB:
example% limit datasize 4G |
这些示例都会使优化器在数据空间达到 4GB 时尝试恢复。
虚拟空间的限制不能大于系统总的可用交换空间。在实际使用时,虚拟空间的限制要足够的小,以允许在大型编译过程中正常使用系统。
请确保编译不会消耗一半以上的交换空间。
有 8GB 的交换空间时,请使用以下命令:
在 sh shell 中:
example$ ulimit -d 4000000 |
在 csh shell 中:
example% limit datasize 4G |
最佳设置取决于要求的优化程度、实际内存量和可用的虚拟内存量。
工作站至少应有 1 GB 内存,推荐使用 2 GB。有关详细的要求,请参见产品发行版自述文件 (http://developers.sun.com/sunstudio/documentation/)。
不应将 Unix strip 命令用于 C++ 目标文件,因为这会导致这些目标文件不可用。
可以通过定义特殊的 shell 别名、使用 CCFLAGS 环境变量或使用 make 来简化复杂的编译器命令。
以下示例为带有常用选项的命令定义了别名。
example% alias CCfx "CC -fast -xnolibmil" |
以下示例使用了别名 CCfx。
example% CCfx any.C |
现在命令 CCfx 等效于:
example% CC -fast -xnolibmil any.C |
可以通过设置 CCFLAGS 变量来指定选项。
可以在命令行中显式使用 CCFLAGS 变量。以下示例说明了如何设置 CCFLAGS (C Shell):
example% setenv CCFLAGS ’-xO2 -m64’ |
以下示例显式使用了 CCFLAGS。
example% CC $CCFLAGS any.cc |
使用 make 时,如果像上述示例那样设置 CCFLAGS 变量,且 makefile 的编译规则是隐式的,那么调用 make 的结果就相当于下面的编译:
CC -xO2 -m64 files...
make 实用程序是功能非常强大的程序开发工具,可以方便地与所有 Sun 编译器一起使用。有关更多信息,请参见 make(1S) 手册页。
使用 makefile 的隐式编译规则(即没有 C++ 编译行)时,make 程序会自动使用 CCFLAGS。