Linux基础(四) gcc

1 gcc的编译过程

在这里插入图片描述
从sum.c到生成可执行程序的过程

首先定义一个sum.c文件

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

#define DEBUG

int main(void){
        int a = NUM1;
        int aa;
        int b = NUM2;
        int sum = a + b;
#ifdef DEBUG
        // 打印输出值
        printf("this sum value is: %d + %d = %d\n", a, b, sum);
#endif
        return 0;
}

再定义一个head.h文件

#ifndef __HEAD_H__                                 
#define __HEAD_H__                                 
                                                    
#define NUM1 10                                     
#define NUM2 20                                    
#endif    
1.1 预处理器

头文件展开,宏替换,去掉注释

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
head.h  sum.c
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc -E sum.c -o sum.i
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
head.h  sum.c  sum.i

打开sum.i文件,在文件的最后,可以看到定义的main函数

# 6 "sum.c"
int main(void){
 int a = 10;
 int aa;
 int b = 20;
 int sum = a + b;


 printf("this sum value is: %d + %d = %d\n", a, b, sum);

 return 0;
}

main函数中的注释已经被去掉,NUM1和NUM2也被替换成了数字。

1.2 编译器处理

此过程是将C文件编译成汇编文件

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc -S sum.i -o sum.s
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
head.h  sum.c  sum.i  sum.s

打开sum.s, 都是一些汇编指令

        .file   "sum.c"
        .text
        .section        .rodata
        .align 8
.LC0:
        .string "this sum value is: %d + %d = %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $10, -12(%rbp)
        movl    $20, -8(%rbp)
        movl    -12(%rbp), %edx
        movl    -8(%rbp), %eax
        addl    %edx, %eax
1.3 汇编器处理

此过程是将汇编文件编译成二进制文件

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc -c sum.s -o sum.o
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
head.h  sum.c  sum.i  sum.o  sum.s

以下是二进制文件中的内容

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^P^C^@^@^@^@^@^@^@^@^@^@@^@^@^@^@^@@^@^M^@^L^@UH<89>åH<83>ì^PÇEô
^@^@^@ÇEø^T^@^@^@<8b><8b>^<89><8b><8b><8b><89>ÆH<8d>=^@^@^@^^@^@^@^^@^@^@^^@^@^@^@ÉÃ^@^@^@^@this sum value is: %d + %d = %d

1.4 链接器处理

此过程是将函数库中相应的代码组合到目标文件中

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.o -o app
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
app  head.h  sum.c  sum.i  sum.o  sum.s

执行这个可执行程序,输出结果

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ./app
this sum value is: 10 + 20 = 30
1.5 gcc参数
1.5.1 指定文件目录
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# tree
.
├── include
│   └── head.h
└── sum.c

1 directory, 2 files
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -o app
sum.c:2:10: fatal error: head.h: No such file or directory
 #include "head.h"
          ^~~~~~~~

指定文件目录

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -I ./include -o app
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
app  include  sum.c
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ./app
this sum value is: 10 + 20 = 30
1.5.2 添加宏

先把宏注释掉然后编译

//#define DEBUG

重新编译sum.c,什么也不输出

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -I ./include -o app
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ./app

添加宏

root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -o app -I include -D DEBUG
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ./app
this sum value is: 10 + 20 = 30
1.5.3 优化
-Wall 提示
-O 优化 
-O 优化的最高级
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -o app -I include -D DEBUG -o3 -Wall
sum.c: In function ‘main’:
sum.c:8:6: warning: unused variable ‘aa’ [-Wunused-variable]
  int aa;
      ^~

在代码中,定义了aa,没有引用到。

生成调试信息
-g
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls
app  include  sum.c
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# gcc sum.c -o app1 -I include -D DEBUG -O3 -Wall -g
sum.c: In function ‘main’:
sum.c:8:6: warning: unused variable ‘aa’ [-Wunused-variable]
  int aa;
      ^~
root@iZ2ze9ftqv2b7zbety6qd8Z:~/learn_gcc# ls -l
total 32
-rwxr-xr-x 1 root root  8304 Jan 26 16:59 app
-rwxr-xr-x 1 root root 11416 Jan 26 17:13 app1
drwxr-xr-x 2 root root  4096 Jan 26 16:45 include
-rw-r--r-- 1 root root   238 Jan 26 16:57 sum.c

可以看出-g 生成调试信息的app1比app占用的空间大

-rwxr-xr-x 1 root root  8304 Jan 26 16:59 app
-rwxr-xr-x 1 root root 11416 Jan 26 17:13 app1
1.5.4 gcc参数总结
-g:包含调试信息
-c:只编译子程序
-D:编译时定义宏
-Wall:提示更多警告信息
-E:生成预处理文件
-o:产生目标文件
编译优化: -OO: 没有优化, -O1:缺省值  -O3:优化级别最高
-I + 目录:指定头文件目录

2 静态库的制作和使用

2.1 定义.c文件和头文件

head.h

#ifndef HEAD_H
#define HEAD_H

#include <stdio.h>

int add(int, int);
int sub(int, int);
int mul(int, int);

#endif

add.c

#include <head.h>

int add(int a, int b)
{
    int result = a + b;
    return result;
}

sub.c

#include <head.h>

int sub(int a, int b)
{
    return a - b;
}

mul.c

#include <head.h>
int mul(int a, int b)
{
    return a * b;
}

目录结构如下

```cpp
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library# tree
.
├── include
│   └── head.h
├── lib
└── src
    ├── add.c
    ├── mul.c
    └── sub.c

3 directories, 4 files
12.2 将.c打包出.o
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library# cd src
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# gcc *.c -c -I ../include
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# ls
add.c  add.o  mul.c  mul.o  sub.c  sub.o
12.3 将.o打包成静态库.a

静态库的命名规则

lib + 库的名字 + .a
例如:libCaculate.a
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# ar rcs libCaculate.a *.o
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# ls
add.c  add.o  libCaculate.a  mul.c  mul.o  sub.c  sub.o
12.4 将.a文件移动到lib文件夹下
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# mv libCaculate.a ../lib
root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library/src# cd ..

查看文件夹结构

root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library# tree
.
├── include
│   └── head.h
├── lib
│   └── libCaculate.a
└── src
    ├── add.c
    ├── add.o
    ├── mul.c
    ├── mul.o
    ├── sub.c
    └── sub.o
12.5 测试静态库的使用

1 创建main.c

#include <stdio.h>
#include <head.h>

int main(void)
{
    int a = 5;
    int b = 6;
    printf("a = %d, b = %d\n", a, b);

    int sumValue = add(a, b);
    printf("add(a, b) = %d\n", sumValue);

    int mulValue = mul(a, b);
    printf("mul(a, b) = %d\n", mulValue);

    int subValue = sub(a, b);
    printf("mul(a, b) = %d\n", subValue);
    return 0;
}

将之前的动态库和main.c打包出可执行程序caculate

 gcc main.c lib/libCaculate.a -o caculate -I include/

也可以这么写

gcc main.c -I include -L lib -l Caculate -o caculate

整体目录结构

root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library# tree
.
├── caculate
├── include
│   └── head.h
├── lib
│   └── libCaculate.a
├── main.c
└── src
    ├── add.c
    ├── add.o
    ├── mul.c
    ├── mul.o
    ├── sub.c
    └── sub.o

执行结果

root@iZ2ze9ftqv2b7zbety6qd8Z:~/static_library# ./caculate 
a = 5, b = 6
add(a, b) = 11
mul(a, b) = 30
mul(a, b) = -1

3 静态库的优缺点

每个.c文件对应一个.o文件。所有的.o文件可打包出一个.a文件。最后打包出可执行程序时,是直接引入所依赖的.o文件。用到哪些.o文件,就将哪些.o文件打包到可执行程序中。用不到的.o则不会打包到程序中。

静态库的优点:
1 发布程序时,不需要提供对应的库
2 加载库的速度快

缺点:
1 库被打包到应用程序中,导致库的体积很大
2 库发生了改变,需要重新编译程序。

4 动态库的制作和使用

先看下整体文件结构

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# tree
.
├── include
│   └── head.h
├── lib
└── src
    ├── add.c
    ├── mul.c
    └── sub.c
4.1 将.c文件打包出.o文件
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# cd src/
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library/src# gcc -fPIC -c *.c -I ../include
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library/src# ls
add.c  add.o  mul.c  mul.o  sub.c  sub.o
4.2 将.o文件打包出.so文件
gcc -shared -o libCaculate.so *.o -I include
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library/src# ls
add.c  add.o  libCaculate.so  mul.c  mul.o  sub.c  sub.o

将动态库移动到上一级的lib文件夹中

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library/src# mv libCaculate.so ../lib/

查看整体结构

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# tree
.
├── include
│   └── head.h
├── lib
│   └── libCaculate.so
└── src
    ├── add.c
    ├── add.o
    ├── mul.c
    ├── mul.o
    ├── sub.c
    └── sub.o
4.3 打包生成app程序

添加main.c文件

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# cat main.c 
#include <stdio.h>
#include <head.h>

int main(void)
{
    int a = 5;
    int b = 6;
    printf("a = %d, b = %d\n", a, b);

    int sumValue = add(a, b);
    printf("add(a, b) = %d\n", sumValue);

    int mulValue = mul(a, b);
    printf("mul(a, b) = %d\n", mulValue);

    int subValue = sub(a, b);
    printf("mul(a, b) = %d\n", subValue);
    return 0;
}
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# tree
.
├── app
├── include
│   └── head.h
├── lib
│   └── libCaculate.so
├── main.c
└── src
    ├── add.c
    ├── add.o
    ├── mul.c
    ├── mul.o
    ├── sub.c
    └── sub.o

打包生成app程序

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# gcc main.c lib/libCaculate.so -o app -I include/

执行生成的程序

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ./app 
a = 5, b = 6
add(a, b) = 11
mul(a, b) = 30
mul(a, b) = -1
4.4 另一种打包方式

先移除生成好的app程序

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# rm app 
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ls
include  lib  main.c  src

重新打包生成app

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# gcc main.c -I include/ -L ./lib/ -l Caculate -o app
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ls
app  include  lib  main.c  src
4.5 解决链接失败

执行app

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ./app
./app: error while loading shared libraries: libCaculate.so: cannot open shared object file: No such file or directory

查看程序所依赖的所有的动态库

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ldd app
	linux-vdso.so.1 (0x00007ffd2c593000)
	libCaculate.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffa0a6dc000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ffa0accf000)

其中

/lib64/ld-linux-x86-64.so.2 (0x00007ffa0accf000)

是动态连接器,动态链接器会将以下动态库

	linux-vdso.so.1 (0x00007ffd2c593000)
	libCaculate.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffa0a6dc000)

加载到依赖程序中。

4.5.1 将动态库拷贝到系统lib中

动态库找不到,可以将生成的.so动态库放到系统lib目录中

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# cp lib/libCaculate.so  /lib

再执行

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ./app 
a = 5, b = 6
add(a, b) = 11
mul(a, b) = 30
mul(a, b) = -1

此时再查看程序所依赖的动态库,可以正常链接到

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ldd app
	linux-vdso.so.1 (0x00007ffe075ea000)
	libCaculate.so => /lib/libCaculate.so (0x00007f948c6af000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f948c2be000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f948cab3000)

但是不推荐这么做,所以暂时先删掉

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# rm /lib/libCaculate.so 
4.5.2 临时把库导入系统环境变量
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# echo $LD_LIBRARY_PATH
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# export LD_LIBRARY_PATH=./lib
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# echo $LD_LIBRARY_PATH
./lib

再来查看依赖,现在可以正常链接到库

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ldd app
	linux-vdso.so.1 (0x00007ffed67fd000)
	libCaculate.so => ./lib/libCaculate.so (0x00007f4d795c6000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d791d5000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4d799ca000)
root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ./app
a = 5, b = 6
add(a, b) = 11
mul(a, b) = 30
mul(a, b) = -1

这么做只是临时的不是永久的,只适合测试用,终端关闭后,下次就链接不到了。

4.5.3 修改用户环境变量

这种方式不常用,永久设置。
打开.bashrc文件

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# cd ~
root@iZ2ze9ftqv2b7zbety6qd8Z:~# vi .bashrc

再将lib目录导入到.bashrc文件中。然后需要重启终端。
这里就不写了,这个也不推荐。

4.5.4 添加到链接器的配置文件中

这种方式比较常用

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# cd /etc
root@iZ2ze9ftqv2b7zbety6qd8Z:/etc# ls -l ld.so.conf
-rw-r--r-- 1 root root 34 Jan 27  2016 ld.so.conf

添加动态库的路径

include /etc/ld.so.conf.d/*.conf
#root/share_library/lib

然后更新链接库

Zroot@iZ2ze9ftqv2b7zbety6qd8Z:/etc# ld config -v

此时可以查看动态库

root@iZ2ze9ftqv2b7zbety6qd8Z:/etc# ldconfig -v

此时再来查看依赖是否正常

root@iZ2ze9ftqv2b7zbety6qd8Z:~/share_library# ldd app
	linux-vdso.so.1 (0x00007ffdd13e1000)
	libCaculate.so => ./lib/libCaculate.so (0x00007f917c3ea000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f917bff9000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f917c7ee000)

5 动态库的优缺点

优点:
1 执行程序体积小
2 动态库更新了,不需要更新编译程序

缺点:
1 发布程序的时候,需要将动态库提供给用户
2 动态库没有被打包到应用程序中,加载的速度相对较慢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值