当前位置:首页 > 智能硬件
[导读]c编译器是编译c程序的必备工具,缺少c编译器情形下,c程序以及c++程序将无法运行。对于c编译器,主要有3款。本文对于c编译器的讲解基于gcc c编译器。此外,本文的gcc c编译器为上篇文章的补充。如果你对本文内容存在一定兴趣,不妨继续往下阅读哦。

c编译器是编译c程序的必备工具,缺少c编译器情形下,c程序以及c++程序将无法运行。对于c编译器,主要有3款。本文对于c编译器的讲解基于gcc c编译器。此外,本文的gcc c编译器为上篇文章的补充。如果你对本文内容存在一定兴趣,不妨继续往下阅读哦。

2.jpg

一、链接外部库

库是预编译的目标文件(object files)的集合,它们可被链接进程序。静态库以后缀为‘.a’的特殊的存档文件(archive file)存储。

标准系统库可在目录 /usr/lib 与 /lib 中找到。比如,在类 Unix 系统中 C 语言的数学库一般存储为文件 /usr/lib/libm.a。该库中函数的原型声明在头文件 /usr/include/math.h 中。C 标准库本身存储为 /usr/lib/libc.a,它包含 ANSI/ISO C 标准指定的函数,比如‘printf’。对每一个 C 程序来说,libc.a 都默认被链接。

下面的是一个调用数学库 libm.a 中 sin 函数的的例子,创建文件calc.c:

#include

#include

int

main (void)

{

double x = sin (2.0);

printf ("The value of sin(2.0) is %f/n", x);

return 0;

}

尝试单独从该文件生成一个可执行文件将导致一个链接阶段的错误:

$ gcc -Wall calc.c -o calc

/tmp/ccbR6Ojm.o: In function 'main':

/tmp/ccbR6Ojm.o(.text+0x19): undefined reference to ‘sin’

函数 sin,未在本程序中定义也不在默认库‘libc.a’中;除非被指定,编译器也不会链接‘libm.a’。

为使编译器能将 sin 链接进主程序‘calc.c’,我们需要提供数学库‘libm.a’。一个容易想到但比较麻烦的做法是在命令行中显式地指定它:

$ gcc -Wall calc.c /usr/lib/libm.a -o calc

函数库‘libm.a’包含所有数学函数的目标文件,比如sin,cos,exp,log及sqrt。链接器将搜索所有文件来找到包含 sin 的目标文件。

一旦包含 sin 的目标文件被找到,主程序就能被链接,一个完整的可执行文件就可生成了:

$ ./calc

The value of sin(2.0) is 0.909297

可执行文件包含主程序的机器码以及函数库‘libm.a’中 sin 对应的机器码。

为避免在命令行中指定长长的路径,编译器为链接函数库提供了快捷的选项‘-l’。例如,下面的命令

$ gcc -Wall calc.c -lm -o calc

与我们上面指定库全路径‘/usr/lib/libm.a’的命令等价。

一般来说,选项 -lNAME使链接器尝试链接系统库目录中的函数库文件 libNAME.a。一个大型的程序通常要使用很多 -l 选项来指定要链接的数学库,图形库,网络库等。

二、编译C++与Fortran

GCC 是 GNU 编译器集合(GNU Compiler Collection)的首字母缩写词。GNU 编译器集合包含 C,C++,Objective-C,Fortran,Java 和 Ada 的前端以及这些语言对应的库(libstdc++,libgcj,……)。

前面我们只涉及到 C 语言,那么如何用 gcc 编译其他语言呢?本节将简单介绍 C++ 和 Fortran 编译的例子。

首先我们尝试编译简单的 C++ 的经典程序 Hello world:

#include

int main(int argc,char *argv[])

{

std::cout << "hello, world/n";

return 0;

}

将文件保存为‘hello.cpp’,用 gcc 编译,结果如下:

$ gcc -Wall hello.cpp -o hello

/tmp/cch6oUy9.o: In function `__static_initialization_and_destruction_0(int, int)':

hello.cpp:(.text+0x23): undefined reference to `std::ios_base::Init::Init()'

/tmp/cch6oUy9.o: In function `__tcf_0':

hello.cpp:(.text+0x6c): undefined reference to `std::ios_base::Init::~Init()'

/tmp/cch6oUy9.o: In function `main':

hello.cpp:(.text+0x8e): undefined reference to `std::cout'

hello.cpp:(.text+0x93): undefined reference to `std::basic_ostream >& std::operator<< >(std::basic_ostream >&, char const*)'

/tmp/cch6oUy9.o:(.eh_frame+0x11): undefined reference to `__gxx_personality_v0'

collect2: ld returned 1 exit status

出错了!!而且错误还很多,很难看懂,这可怎么办呢?在解释之前,我们先试试下面的命令:

$ gcc -Wall hello.cpp -o hello -lstdc++

噫,加上-lstdc++选项后,编译竟然通过了,而且没有任何警告。运行程序,结果如下:

$ ./hello

hello, world

通过上节,我们可以知道,-lstdc++ 选项用来通知链接器链接静态库 libstdc++.a。而从字面上可以看出,libstdc++.a 是C++ 的标准库,这样一来,上面的问题我们就不难理解了──编译 C++ 程序,需要链接 C++ 的函数库 libstdc++.a。

编译 C 的时候我们不需要指定 C 的函数库,为什么 C++ 要指定呢?这是由于早期 gcc 是指 GNU 的 C 语言编译器(GNU C Compiler),随着 C++,Fortran 等语言的加入,gcc的含义才变化成了 GNU 编译器集合(GNU Compiler Collection)。C作为 gcc 的原生语言,故编译时不需额外的选项。

不过幸运的是,GCC 包含专门为 C++ 、Fortran 等语言的编译器前端。于是,上面的例子,我们可以直接用如下命令编译:

$ g++ -Wall hello.cpp -o hello

GCC 的 C++ 前端是 g++,而 Fortran 的情况则有点复杂:在 gcc-4.0 版本之前,Fortran 前端是 g77,而gcc-4.0之后的版本对应的 Fortran 前端则改为 gfortran。下面我们先写一个简单的 Fortran 示例程序:

C Fortran 示例程序

PROGRAM HELLOWORLD

WRITE(*,10)

10 FORMAT('hello, world')

END PROGRAM HELLOWORLD

将文件保存‘hello.f’,用 GCC 的 Fortran 前端编译运行该文件

$ gfortran -Wall hello.f -o hello

$ ./hello

hello, world

我们已经知道,直接用 gcc 来编译 C++ 时,需要链接 C++ 标准库,那么用 gcc 编译 Fortran时,命令该怎么写呢?

$ gcc -Wall hello.f -o helloworld -lgfortran -lgfortranbegin

注意:上面这条命令与 gfortran 前端是等价的(g77 与此稍有不同)。其中库文件 libgfortranbegin.a (通过命令行选项 -lgfortranbegin 被调用) 包含运行和终止一个 Fortran 程序所必须的开始和退出代码。库文件 libgfortran.a 包含 Fortran 底层的输入输出等所需要的运行函数。

对于 g77 来说,下面两条命令是等价的(注意到 g77 对应的 gcc 是 4.0 之前的版本):

$ g77 -Wall hello.f -o hello

$ gcc-3.4 -Wall hello.f -o hello -lfrtbegin -lg2c

命令行中的两个库文件分别包含 Fortran 的开始和退出代码以及 Fortran 底层的运行函数。

以上便是此次小编带来的“c编译器”相关内容,通过本文,希望大家对本文讲解的内容具备一定的认知。如果你喜欢本文,不妨持续关注我们网站哦,小编将于后期带来更多精彩内容。最后,十分感谢大家的阅读,have a nice day!

智能硬件

18003 篇文章

关注

发布文章

技术子站