为了更深入了解程序编译,尽量从无到有地进行系统性地了解,先对程序编译中使用到的GCC与G++进行认识,熟悉。
首先该篇借鉴自以下博客,如果大家需要了解更加详细可以打开如下链接:
GCC使用简介
gcc与g++的区别
(顺带说下刚了解的:在include语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目录中搜索。)
GCC由英文GNU Compier Collection简写而来,其作用是将我们平常通过高级计算机语言编写的源代码构建成计算机能认识的,能直接进行处理的二进制代码。
GCC是Linux下最常用的编译程序,是Linux平台下编译器的事实标准,在Linux平台下的嵌入式开发领域,GCC也是用得最普遍 的一种编译器。
GCC一直被大家所广泛使用,当然也有其原因,下面来分几点进行阐述GCC能被大家所广泛使用的理由:
支持宿主开发:即要为哪个平台开发程序,就在哪个平台上进行开发。
支持交叉编译:即要为哪个平台开发程序,但在另一个平台上进行的开发编译。
Linux,Windows,Solaris…
支持C、C++、Ada、Java、Objective-C、FORTRAN、Pascal…
对于GNU来说,程序的编译共进行4步,如下引用图所示:
下面对这几个过程做简要说明:
输入文件:.c源代码文件;
输出文件:.i中间文件;
需要文件:类似.h头文件的包含文件;
主要处理内容:主要处理文件中的#ifdef、 #include和#define命令;
特地生成输出文件命令:gcc -E xxx.c -o xxx.i(这里.i文件一般用不到);
输入文件:预处理生成的.i文件;
输出文件:.s文件;
特地生成输出文件命令:GCC -S xxx.i -o xxx.s;
输入文件:编译生成的.s文件;
输出文件:.o文件;
特地生成输出文件命令:GCC -c test.s -o test.o;
输入文件:汇编生成的.o文件,与其它的机器代码文件和库文件;
输出文件:可执行的二进制代码文件;
主要处理内容:将各个.o文件及一些库文件,机器代码连接成一个可执行的二进制代码文件;
生成输出文件命令:GCC xxx.o -o xxx
源文件直接生成可执行文件:
命令:GCC xxx.c -o xxx;
说明:xxx.c为源文件,xxx为可执行文件,-o 是生成可执行文件的输出选项;
源文件生成目标文件:
命令:GCC -c xxx.c
说明:如果我们只想让源文件生成目标文件(给文件虽然也是机器代码但不可执行),可以使用标记-c 。(默认情况下生成xxx.o);也可以利用”GCC -c xxx.c -o abc.o“命令将生成的目标文件命名为”abc.o”;
-I:可以使用多个 来指定各个目录,如:gcc test.c –I../inc –I../../inc2 -o test(指定../inc和../../inc2目录);
符号常量:可以在编译命令行中定义。为此,我们可以简单的在命令行中使用-D选项即可,如:gcc -DTEST_CONFIGURATION test.c -o test(等价于在源文件中加”#define TEST_CONFIGURATION”,其好处是必修改源文件就能改变由符号常量控制的行为)。
-Wall:可以使用来发现程序中一系列的常见错误警告,如:gcc -Wall test.c -o test,可以使用”-Wno-unused“来关闭unused警告,如:gcc -Wall -Wno-unused test.c -o test。
具体的详细警告可以看这里链接
这里编译的命令还包括其他的一些选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。其中具体的阐述可以在一下链接中找到:编译命令选项说明
这里只引用了部分常用选项:
这里我想继续沿用开头博客中的书写方式进行总结,个人觉得这种方式能利于我们解决gcc与g++的误解,当然具体内容可以看文章开头的连接。
纠正:两者都可以;
说明:这里也需要说明下,文件如果是.c为后缀的,gcc会把他认为是c进行编译;而如果是.cpp结尾的,gcc与g++均会将他认为是c++进行编译;
注意:两者的语法方式还是有所区别,c++的语法比c更加严谨,具体的语法区别这里就不详细说明了;
编译阶段:编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
首先先对”__cplusplus”进行了解,该宏用来判断程序是用c还是c++编译程序编译的。当编译c++程序时,这个标示符会被定义,编译c程序时,不会定义。
例如如下例子:
#ifdef __cplusplus
extern "C" {
#endif
…………
…………
#ifdef __cplusplus
}
#endif
如果改段代码是用g++编译器编译(__cplusplus是c++才有定义的宏),那么”__cplusplus”宏就会被定义,就会执行里面的”extern “C” {“,即表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。(在这里所说的类C,代表的是跟C语言的编译和连接方式一致的所有语言)
编译时,gcc/g++两者等价,g++会自动调用gcc;
链接时,可以用g++或者gcc -lstdc++。
因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。
其实并没有关系,无论是gcc还是g++,用extern “c”时,都是以C的命名方式来为symbol命名,否则,都以c++方式命名。请看下面一段引用的例子:
me.h:
extern "C" void CppPrintf(void);
me.cpp:
#include
#include "me.h"
using namespace std;
void CppPrintf(void)
{
cout << "Hello\n";
}
test.cpp:
#include
#include
#include "me.h"
int main(void)
{
CppPrintf();
return 0;
}
1. 先给me.h加上extern "C",看用gcc和g++命名有什么不同
[root@root G++]# g++ -S me.cpp
[root@root G++]# less me.s
.globl _Z9CppPrintfv //注意此函数的命名
.type CppPrintf, @function
[root@root GCC]# gcc -S me.cpp
[root@root GCC]# less me.s
.globl _Z9CppPrintfv //注意此函数的命名
.type CppPrintf, @function
完全相同!
2. 去掉me.h中extern "C",看用gcc和g++命名有什么不同
[root@root GCC]# gcc -S me.cpp
[root@root GCC]# less me.s
.globl _Z9CppPrintfv //注意此函数的命名
.type _Z9CppPrintfv, @function
[root@root G++]# g++ -S me.cpp
[root@root G++]# less me.s
.globl _Z9CppPrintfv //注意此函数的命名
.type _Z9CppPrintfv, @function
完全相同!
【结论】完全相同,可见extern "C"与采用gcc/g++并无关系,以上的试验还间接的印证了前面的说法:在编译阶段,g++是调用gcc的。
大部分libxxxx.so**只是一个链接**,以RH9为例,比如libm.so它链接到/lib/libm.so.x,/lib/libm.so.6又链接到/lib/libm-2.3.2.so,
如果没有这样的链接,还是会出错,因为ld只会找libxxxx.so,所以如果你要用到xxxx库,而只有libxxxx.so.x或者libxxxx-x.x.x.so,做一个链接就可以了ln -s libxxxx-x.x.x.so libxxxx.so
手工来写链接参数总是很麻烦的,还好很多库开发包提供了生成链接参数的程序,名字一般叫xxxx-config,一般放在/usr/bin目录下,比如
gtk1.2的链接参数生成程序是gtk-config,执行gtk-config –libs就能得到以下输出”-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic
-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm”,这就是编译一个gtk1.2程序所需的gtk链接参数,xxx-config除了–libs参数外还有一个参数是–cflags用来生成头 文件包含目录的,也就是-I参数。你可以试试执行gtk-config –libs –cflags,看看输出结果
现在的问题就是怎样用这些输出结果了,最笨的方法就是复制粘贴或者照抄,聪明的办法是在编译命令行里加入这个 xxxx-config --libs --cflags
,比如编译一个gtk程序:gcc gtktest.c gtk-config --libs --cflags
这样就差不多了。注意`不是单引号,而是1键左边那个键。
-I参数是用来指定头文件目录,/usr/include目录一般是不用指定的,gcc知道去那里找,但 是如果头文件不在/usr/include里我们就要用-I参数指定了。
关于包含库与头文件的编译命令例子:
g++ curltest.cpp -o curltest -L/mnt/hgfs/windows/curl-7.19.5/lib/.libs
-lcurl -I/mnt/hgfs/windows/curl-7.19.5/include