GCC使用入门

本文介绍了GCC编译器的历史、特点,以及在Linux环境下编译C/C++程序的步骤,包括多文件编译、链接外部库、静态和动态链接的区别,以及如何使用ar工具创建和管理库文件。
摘要由CSDN通过智能技术生成

1. GCC介绍

1.1 GCC的历史

        GNU C Compiler(GCC)的作者是Richard Stallman,他是GNU项目的负责人,GCC是Linux的唯一编译器。GNU的意思是“GNU is Not Unix ”。

        GNU项目是在1984年开始的,其目的是创建一个免费的Unix-like操作系统。每一个Unix-like操作系统都需要一个C编译器,当时没有免费的C编译器,于是GNU项目开始开发一个免费的C编译器,即“GCC”。在1987年,第一个可移植的免费的C编译器——GCC发布。

1.2 GCC的特点

        GCC是一个可移植的编译器——它可以在大多数平台上运行;

        GCC不仅仅是一个本地的编译器——可以从一个平台编译出另外一个平台的软件;

        GCC可以自由的编辑和修改——任何人都有使用和修改GCC的自由。

1.3 C/C++的特点

         C和C++允许直接访问计算机的内存。因此他们通常被用于编写底层的系统软件。但是要确保对内存的正确访问。 

2. GCC编译(Linux环境)

2.1 编译一个简单的C程序

// hello.c源码

#include <stdio.h>

int main(){
    printf("Hello, world!\n");
    return 0;
}

编译运行这个hello.c程序,生成可执行文件hello

$ gcc -Wall hello.c -o hello

其中,-Wall表示-Warning all,即发现潜在错误会给出警告。

$ ./hello

./hello运行hello文件,输出"Hello, world!"

2.2 编译多个源文件

编译hello.c和main.c文件,其中main.c是主函数,hello.h声明函数原型,hello.c提供函数原型的定义。

// hello.c

#include <stdio.h>
#include "hello.h"

void hello(const char* stting){
        printf(string);
}
// main.c

#include <stdio.h>
#include "hello.h"

int main(){
        hello("Hello, World!\n");
        return 0;
}
// hello.h
void hello(const char* string);
$ gcc -Wall main.c hello.c -o newhello

 注意:hello.h不需要放在命令行里,GCC分析到#include "hello.h"能自己找到该头文件。

"FILE.h"在当前文件目录下找,找不到就去系统头文件目录找;

<FILE.h>直接去系统头文件目录下找。

系统头文件一般在/usr/include或/usr/local/include目录下。

执行gcc命令后,输出结果。

2.3 冗余编译选项,独立编译和链接顺序

(1)冗余编译选项  -v

$ gcc -v -Wall hello.c

"-v"选项能被用于详细陈述编译和链接的详细信息。

(2)独立编译和链接顺序

        如果一个程序被存储在一个文件中,如果这个程序有一点改变,则需要重新编译整个文件,重新编译十分耗时。因此,我们可以将一个原文件分为多个文件进行单独编译,这样当某个文件有改动时,只需要单独对这个文件进行编译即可。分为两个阶段:

第一个阶段:将多个文件分别编译成.o目标文件

第二个阶段:将.o目标文件用linker链接器将各个.o目标文件链接起来,来形成一个可执行文件。

① 将.c文件编译成.o目标文件;

$ gcc -Wall -c main.c hello.c

 结果会生成main.o文件和hello.o文件。

② 将.o文件链接;

$ gcc main.o hello.o -o hello

将main.o文件和hello.o文件链接成可执行文件hello。

注意:main.o和hello.o的顺序,现在大部分编译器不考虑.o文件的顺序,但是在古老的编译器,要把main.o放在前面,hello.o放在后面,英文main函数要调用hello函数。

③ 执行hello。

3. 重编译重链接、链接外部库

3.1 重编译重链接

        通常来说,链接比编译更快——在一个有许多源文件的大型项目中,只重新编译那些被修改的源文件是一个很大的节约。

        重新编译只被改动过的源文件可以被GNU Make自动的执行。

3.2 链接外部库

        一系列预先编译好的.o文件组合起来形成一个库文件。静态链接库文件在Linux是.a文件,在Windows是.lib文件,库文件通过GNU archiver(ar)来创建打包。 

        为了确保编译器能够链接到外部库,我们需要将库应用到gcc命令行:

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

即编译main.c文件成可执行文件calc,其中main.c中引用的库从/usr/lib/libm.a中寻找。

        为了避免外部库路径过长,编译器提供了'-l'的短接命令命令:

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

        编译选项'-lNAME'将尝试用库文件'libNAME.a'来链接目标文件,这个库将在标准库(standard library)寻找。

        对于使用一个静态库,必须要包含正确的头文件,因为头文件包含了函数所需要的原型以及对应的参数和返回类型。

        当额外的库被安装到其他目录,就有必要扩大搜索路径,为了这些库能被找到。“-I”和“-L”参数,其中-I表示在-I路径下寻找头文件,-L表示在-L路径下寻找需要链接的库文件。注意:在#inlcude " xxx"不要加绝对路径的信息,这样到别的平台运行容易报错。采用相对路径合理。

4. ar创建库、搜索路径

4.1 使用ar创建一个库文件

库文件=多个.o目标文件融合在一起

你可以使用下面的命令来创建一个静态库文件:

$ ar cr libNAME.a file1.o file2.o ... filen.o

ar表示创建库文件,cr表示创建(create)和替换(replace)

ar还可以查看一个库文件里面有多少个目标文件。

$ ar t libNAME.a

    4.2 使用ar创建库文件例子

当前目录下有以下四个文件:mylib.h func1.c   func2.c   main.c   

// mylib.h

int func1(int x, int y);

void func2(int x);
// func1.c

# include "mylib.h"

int func1(int x, int y){
    return (x + y);
}
// func2.c

#include <stdio.h>
#include "mylib.h"

void func2(int x){
    printf("The result is %d\n", x);
}
// main.c

#include <stdio.h>
#include "mylib.h"

int main(){
    int i;
    i = func1(1, 2);
    func2(i);
    return 0;
}

(1)使用gcc将func1.c 和 func2.c将源文件编译成func1.o和func2.o目标文件。

$ gcc -Wall -c func1.c func2.c 

 结果如下图:

(2)将func1.o和func2.o融合成库文件libhello.a

$ ar cr libhello.a func1.o func2.o

结果如下图:

(3)查询libhello.a包括哪些.o目标文件

$ ar -t libhello.a

(4)使用main.c和库文件libhello.a编译程序生成可执行文件hello

方法一:

$ gcc -Wall main.c libhello.a -o hello

 注意:main.c 和libhello.a的顺序不能颠倒!!!

 

方法二:

此时如果我们使用短接命令-l

$ gcc -Wall main.c -lhello -o h2

提示报错: 

这是因为-l命令不是从当前目录找libhello.a, 而是从系统库文件目录找libhello.a 

我们将libhello.a文件复制到系统库目录/usr/lib下

$ sudo cp libhello.a /usr/lib

 因为要拷贝到系统目录,需要sudo执行

再次执行

$ gcc -Wall main.c -lhello -o h2

 显示生成可执行文件h2:

(5)运行hello文件

 

4.3 -L命令的使用

-L能够制定库文件搜索路径

方法一:相对路径

$ gcc -Wall main.c -L. -lhello -o h3

-L.表示在当前目录下寻找库文件,-lhello表示要寻找的库文件名为libhello.a

方法二:绝对路径

$ gcc -Wall main.c -L/home/gtc/Desktop/code/v2 -lhello -o h4

4.4 程序文件结构划分main.c、lib文件和include文件

include文件夹保存头文件,lib文件夹下放库文件

$ gcc -Wall -Iinclude -L./lib  main.c -lhello -o hello

其中-Iinclude表示在当前目录下寻找include头文件

-L./lib表示在当前目录下寻找库文件

-lhello表示要寻找的库文件名字为: libhello.a

5. 静、动态链接库

        外部库通常有两种形式:静态库(static libraries)动态库(shared libraries), 静态库是.a文件(Linux),.lib(Windows)。在静态库中,凡是调用到的函数,都会被拷贝到目标文件里面。

缺点:如果多个程序同时调用同一个静态库,会将同样的库重复复制多份到机器码中,造成内存过大。

        动态库采用了一种更高级的链接形式,可以使得最终的可执行文件更小。动态库的文件后缀是.so(shared object)。windows中动态库的后缀是.dll

静态库、动态库在Linux和Windows后缀区别
LinuxWindows
静态库.a.lib
动态库.so.dll

        动态库链接的时候,不是把机器码拷贝到可执行文件里,而是拷贝一个地址(指针)到可执行文件,因此多个程序调用同一个动态库时,只用将一个动态库加载进内存即可,能够节约内存。

        可执行文件在运行之前,动态库的机器码才会被加载进内存,并被多个程序共享使用,这种方式叫动态链接(dynamic linking)。

        

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值