GCC学习笔记

一、简介

1.1 什么是GCC

  • GCC(GNU Compiler Collection)是由GNU开发的编程语言译器
  • GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等)

1.2 GCC、gcc、g++三者关系

  • gcc(GUN C Compiler)是GCC中的c编译器
  • g++(GUN C++ Compiler)是GCC中的c++编译器
  • 两者都可以编译c和cpp文件,但存在差异;gcc在编译cpp时语法按照c来编译,但默认不能链接到c++的库

二、源代码处理流程

源代码处理流程

  • 源代码到可执行文件经历预处理、编译、汇编、链接几个过程;
  • 实际调用程序
  1. 预编译和编译程序:ccl
  2. 汇编器:as
  3. 链接器:ld
  • 示例文件
// hello.c
#include <stdio.h>

int main(void)
{
	printf("nihao\n");
	
	return 0;
}

2.1 预编译

  • 预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。
2.1.1 处理规则
  1. 删除所有的#define,展开所有的宏定义。
  2. 处理所有条件编译指令,如#if,#endif、#ifdef,#elif 和 #else
  3. 处理#include指令,将文件内容替换到对应位置,该过程递归实现
  4. 删除所有注释,//、/* */
  5. 保留所有的#program编译指令,编译器需要使用
  6. 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号
2.1.2 操作示例
# gcc -E 指令只会将预处理操作的结果输出到屏幕上,并不会自动保存到某个文件
# 常跟 -o 指令配合,输出.i(c) / .ii(cpp)文件
$gcc -E hello.c -o hello.i

# 使用 -C(大写)阻止GCC删除源文件和头文件中的注释
$gcc -E -C hello.c -o hello.i

2.2 编译

  • 编译程序:将某一种程序设计语言写的程序翻译成等价的另一种语言的程序的程序,称之为编译程序(compiler)
  • 把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码(.s)文件
2.2.1 处理说明
  1. 词法分析:将字符串序列分割成一系列的记号
  2. 语法分析:语法树是一种以表达式为节点的树,同时将运算符的优先级确定下来
  3. 语义分析:对静态语义的分析,包含:声明和类型的匹配,类型的转换
  4. 优化:
  5. 目标代码生成:生成汇编表示的代码序列
  6. 目标代码优化:目标代码优化器对上述代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等
2.2.2 操作示例
# 使用-S(大写),将指定文件处理至编译阶段结束
# - 如果操作对象是.i文件,则GCC编译器只需编译此文件;
# - 如果操作对象是.c文件,则GCC编译器会对其进行预处理和编译这两步
$gcc -S hello.i -o hello.s

# 使用-fverbose-asm;编译器自行为汇编代码添加注释

2.3 汇编

  • 将汇编代码转变成机器可执行的指令(机器码文件);生成目标文件.o(windows下)、.obj(Linux下)
  1. 根据汇编指令和机器指令的对照表一一翻译过来
  2. 汇编过程有汇编器as完成
2.3.1 操作示例
# gcc -c(小写) src.s  -o dest.o
$gcc -c hello.s -o hello.o

2.4 链接

  • 目标文件已经是二进制文件,与可执行文件的组织形式类似,只是有些函数和全局变量的地址还未找到,因此还无法执行。
  • 链接的作用就是找到这些目标地址,将所有的目标文件组织成一个可以执行的二进制文件。
    在这里插入图片描述
// test.h
#ifndef __TEST_H_
#define __TEST_H_

int add(int a, int b);
int sub(int a, int b);
int div(int a, int b);
#endif
// add.c
#include "test.h"
int add(int a, int b)
{
	return a + b;
}
// sub.c
#include “test.h”
int sub(int a,int b)
{
    return a - b;
}
// div.c
#include “test.h”
int div(int a,int b)
{
    return a / b;
}
// main.c
#include <stdio.h>
#include "test.h"  //必须引入头文件
int main(void)
{
    int m, n;
    printf("Input two numbers: ");
    scanf("%d %d", &m, &n);
    printf("%d+%d=%d\n", m, n, add(m, n));
    printf("%d-%d=%d\n", m, n, sub(m, n));
    printf("%d÷%d=%d\n", m, n, div(m, n));
    return 0;
}
2.4.1 链接其他目录中的库
# 1) 把链接库作为一般的目标文件,为GCC指定链接库的完整路径与文件名
$gcc main.c -o main.out /usr/lib/libm.a

# 2) 使用-L选项,为 GCC 增加另一个搜索链接库的目录:
$gcc main.c -o main.out -L/usr/lib -lm

# 3) 把包括所需链接库的目录加到环境变量 LIBRARYPATH 中。
2.4.2 静态链接库的创建和使用
1. 创建
  • 能生成静态链接库的条件
  1. 源文件中只提供可以重复使用的代码,例如函数、设计好的类等,不能包含 main 主函数;
  2. 源文件在实现具备模块功能的同时,还要提供访问它的接口,也就是包含各个功能模块声明部分的头文件。
  • 创建流程
  1. 将所有指定源文件,都编译成相应的目标文件
  2. 然后使用 ar 压缩指令,将生成的目标文件打包成静态链接库
# 1) 将所有指定源文件,都编译成相应的目标文件
$gcc -c sub.c add.c div.c

# 2) 然后使用 ar 压缩指令,将生成的目标文件打包成静态链接库
# 格式:ar rcs 静态链接库名称 目标文件1 目标文件2 ...
# 静态链接库名称规则:libxxx.a(Linux) / libxxx.lib(Windows)
$ar rcs libmymath.a add.o sub.o div.o
2. 使用
  1. 将main.c文件编译为目标文件
  2. 执行链接命令
$gcc -c mian.c

# 当作一般文件
$gcc -static main.o libmymath.a

# 编译器无法找到文件
$gcc main.o -static -L /root/demo -lmymath
2.4.3 动态链接库的创建和使用
1. 创建
  1. 直接使用源文件创建动态链接库
# -fpic 表示各文件目录中函数、类等功能模块的地址使用相对地址
$gcc -fpic -shared 源文件名... -o 动态链接库名
  1. 先使用 gcc -c 指令将指定源文件编译为目标文件
$gcc -c -fpic add.c sub.c div.c
$gcc -shared add.o sub.o dic.o -o libmymath.so
2. 使用
$gcc main.c libmymath.so -o main.out
  • 生成的main.out通常无法直接执行
$./main.out
./main.out: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
  • 通过ldd main.out查看当前文件在执行时需要的所有动态链接库,以及存储位置
  • 常用解决方案
  • 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64);
  • 在终端输入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx,其中 xxx 为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效);
  • 修改~/.bashrc 或~/.bash_profile 文件,即在文件最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx(xxx 为动态库文件的绝对存储路径)。保存之后,执行source .bashrc指令(此方式仅对当前登陆用户有效)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值