C语言学习·编译过程
# 预处理器 cpp, 展开通文件, 宏替换, 去掉注释
gcc -E hello.c -o hello.i
# 编译器 gcc, c文件 -> 汇编文件
gcc -S hello.i -o hello.s
# 汇编器 as, 汇编文件 -> 二进制文件
gcc -c hello.s -o hello.o
# 链接器 ld, 将函数库中的代码组合到目标文件中
gcc hello.o -o hello
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 编译参数
- 指定宏
#include <iostream>
int main() {
#ifdef DEBUG
std::cout << "hello world\n";
#endif
return 0;
}Copy to clipboardErrorCopied
1
2
3
4
5
6
7
2
3
4
5
6
7
gcc sum.c -o sum -D DEGUBCopy to clipboardErrorCopied
1
- 输出警告信息
gcc sum.c -o sum -WallCopy to clipboardErrorCopied
1
- 优化代码
# 优化等级分为o1、 o2、 o3
gcc sum.c -o sum -o3 Copy to clipboardErrorCopied
1
2
2
- 生成调试信息
gcc sum.c -o sum -g
1
# 静态库
# 创建静态库
// include/head.h
#ifndef HEAD_H
#define HEAD_H
int sum(int, int);
int mul(int, int);
#endifCopy to clipboardErrorCopied
1
2
3
4
5
6
2
3
4
5
6
// ./sum.c
#include "head.h"
int sum(int a, int b) {
return a + b;
}
// ./mul.c
#include "head.h"
int mul(int a, int b) {
return a * b;
}Copy to clipboardErrorCopied
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
生成.o文件
gcc -c *.c -I includeCopy to clipboardErrorCopied
1
打包静态库
ar rcs lib/libtest.a *.oCopy to clipboardErrorCopied
1
# 使用静态库
// ./main.c
#include "head.h"
#include "stdio.h"
int main()
{
int a = sum(2, 4);
int b = mul(2, 4);
printf("%d %d\n", a, b);
return 0;
}Copy to clipboardErrorCopied
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
使用静态库编译文件
gcc main.c lib/libtest.a -I include -o main
gcc main.c -I include -L lib -l test -o mainCopy to clipboardErrorCopied
1
2
2
查看查看文件中符号
nm mainCopy to clipboardErrorCopied
1
# 优缺点
静态库在使用的时候,会根据程序中用的到函数,将对应函数所在的.o打包到程序中。
- 发布程序的时候,不需要提供对应的库。
- 加载库的速度快
将这些库打包到可执行文件中同时也会造成:
- 可执行程序的体积大
- 库发生改变,需要重新编译程序
# 动态库
# 创建动态库
生成与位置无关的目标文件
gcc -fPIC -c *.c -I includeCopy to clipboardErrorCopied
1
打包动态库**.so**
gcc -shared -o lib/libtest.so *.o -I includeCopy to clipboardErrorCopied
1
# 使用动态库
gcc main.c lib/libtest.so -I include -o main
gcc main.c -L lib -l test -I include -o mainCopy to clipboardErrorCopied
1
2
2
在使用第二种方法,执行可执行文件时,会出现找不到路径的问题,这是因为动态库在加载的时候,需要动态链接器进行加载,可以适应ldd命令查看可执行程序需要的动态库。
动态链接器在加载库的时候,根据环境变量中的目录惊进行查找,可以添加自己的目录到环境变量中。
动态库的搜索路径的顺序
- 编译目标代码时指定的动态库搜索路径
- 环境变量
LD_LIBRARY_PATH
指定的动态库搜索路径 - 配置文件/etc/ld.so.conf中指定的动态库搜索路径
- 默认的动态库搜索路径/lib和/usr/lib
添加到LD_LIBRARY_PATH,这种方法是临时的。
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATHCopy to clipboardErrorCopied
1
可以将命令写到bash配置文件里面,这样每次加载shell之前,都会配置目录。
vim ~/.bashrc
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATHCopy to clipboardErrorCopied
1
2
2
另外也可以配置ld.so.conf。
sudo vim /etc/ld.so.conf
/home/user/linux/lib
sudo ldconfig -vCopy to clipboardErrorCopied
1
2
3
2
3
查看可执行文件依赖的动态库
ldd appCopy to clipboardErrorCopied
1
# 优缺点
动态库只是在内存地址的共享库中标记,在程序运行时才加载。
- 可执行程序体积小
- 动态库更新,不需要重新编译
- 发布程序需要将库提供给用户
- 动态库没有打包应用程序中,加载速度相对慢
# GDB调试
全称 | 功能 | |
---|---|---|
list | l | 显示多行源代码 |
break | b | 设置断点 |
delete | d | 删除断点 |
info | i | 描述程序的状态 |
run | r | 开始运行程序 |
display | disp | 跟踪查看某个变量,每次停下来都显示它的值 |
undisplay | 停止追踪 | |
step | s | 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句 |
finish | 跳出本次函数 | |
next | n | 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) |
p | 打印内部变量值 | |
ptype | 打印变量类型 | |
continue | c | 继续程序的运行,直到遇到下一个断点 |
u | 跳出本次循环 | |
set var name=v | 设置变量的值 | |
start | st | 开始执行程序,在main函数的第一条语句前面停下来 |
file | 装入需要调试的程序 | |
kill | k | 终止正在调试的程序 |
watch | 监视变量值的变化 | |
backtrace | bt | 产看函数调用信息(堆栈) |
frame | f | 查看栈帧 |
quit | q | 退出GDB环境 |
# 生成调试程序
gcc *.c -o app -gCopy to clipboardErrorCopied
1
# 查看代码
l main.c:mainCopy to clipboardErrorCopied
1
# 设置断点
- 设置断点
b 22
b 15 if i == 15Copy to clipboardErrorCopied
1
2
2
- 查看断点
i bCopy to clipboardErrorCopied
1
- 删除断点
d 1
d 1-5Copy to clipboardErrorCopied
1
2
2
# 执行程序
- 开始
startCopy to clipboardErrorCopied
1
- 单步
nCopy to clipboardErrorCopied
1
- 下个断点
cCopy to clipboardErrorCopied
1
# 单步调试
- 进入函数内部
sCopy to clipboardErrorCopied
1
- 跳出本次循环
uCopy to clipboardErrorCopied
1
- 退出函数
finishCopy to clipboardErrorCopied
1
# 查看变量
- 查看变量的值
p varCopy to clipboardErrorCopied
1
- 查看变量类型
ptype varCopy to clipboardErrorCopied
1
- 设置变量的值
set var i=10Copy to clipboardErrorCopied
1
# 追踪变量
- 追踪变量
display i
display jCopy to clipboardErrorCopied
1
2
2
- 查看追踪的变量
i displayCopy to clipboardErrorCopied
1
- 删除追踪的变量
undisplay 1 # 对应变量的编号Copy to clipboardErrorCopied
1
# 多进程调试
在fork之前设置mode。
- set follow-fork-mode child,跟踪子进程
- set follow-fork-mode parent 跟踪父进程
# makefile
# 普通版
app: main.o sum.o sub.o
gcc main.o sum.o sub.o -o app
main.o: main.c
gcc -c main.c
sum.o: sum.c
gcc -c sum.c
sub.o: sub.c
gcc -c sub.cCopy to clipboardErrorCopied
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 自动变量
$@: 规则中的目标
$<: 规则中的第一个依赖
$^: 规则中的所有依赖Copy to clipboardErrorCopied
1
2
3
2
3
obj = main.o sum.o sub.o
target = app
# makefile系统变量
CC = gcc
$(target):$(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@Copy to clipboardErrorCopied
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# makefile函数
target = app
# 得到所有.c文件
src = $(wildcard ./*.c)
# 将.c替换为.o
obj = $(patsubst ./%.c, ./%.o, $(src))
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
# 声明伪目标
.PHONY:clean
clean:
rm $(obj) $(target)Copy to clipboardErrorCopied
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 静态模型
c194a9eg(objects)中获取, "%.o"表示以".o"结尾的目标, 即foo.o bar.o, 依赖模式就是把"%.o"中的"%"拿来加上".c"
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) &< -o $@Copy to clipboardErrorCopied
1
2
3
4
2
3
4
等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.oCopy to clipboardErrorCopied
1
2
3
4
2
3
4
上次更新: 2022/12/02, 22:04:34