linux编译过程、动静态库编译、makefile编程

本文详细介绍了GCC的编译过程,包括预处理、编译、汇编和链接四个阶段,并讲解了静态库和动态库的创建方法。此外,还深入探讨了Makefile的使用,包括规则编写、多目录编程以及变量使用。最后,讨论了GCC的编译选项、环境变量设置和路径指定。内容涵盖了软件开发中的关键环节,旨在提升编程效率和质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译过程

1、预处理阶段

gcc *.c -E -o *.i(预编译)

2、编译阶段

gcc *.i -S -o *.S(检查语法、翻译成汇编语言)

3、汇编阶段

gcc *.S -c -o *.o(汇编语言翻译成机器指令,不带地址的二进制文件)

4、链接阶段

gcc *.o -o *.elf(将机器码链接成段,添加各种地址信息到headinfo段中,带地址的二进制文件)

静态库和动态库的创建

1、静态库的创建

(1)创建.c文件,存放函数定义

sub.c

  1 #include"sub.h"
  2 int sub_func(int x,int y)
  3 {
  4 return x-y;
  5 }

add.c

  1 #include"add.h"
  2 #include<stdarg.h>
  3 int add_func(int x,int y)
  4 {
  5 return x+y;
  6 }

(2)创建.h文件,存放函数声明
sub.h

#ifndef __SUB_FUNC_H
#define __SUB_FUNC_h
int sub_func(int x,int y);
#endif

add.h

#ifndef __ADD_FUNC_H
#define __ADD_FUNC_H
int add_func(int x,int y);
#endif

(3)将.c编译成.o

gcc -c add.c sub.c

(4)链接成.a

ar -crv libmylib.a add.o sub.o

(5)使用静态库

写main函数,然后使用静态库链接编译
gcc main.c -o main libmylib.a
./main 
2、动态库的创建

(1)编写好代码之后直接链接

gcc -fpic -shared libmylib.so sub.c add.c

(2)编写好main函数链接动态库(动态库要移动到/lib,不然可执行文件执行不了或添加为所在目录添加环境变量)

gcc main.c -o main libmylib.so
还可以链接指定目录下的库(动态静态都可以)
gcc main.c -o main -L directory -l filename
3、添加环境变量(.so所在目录)

(1)永久添加

修改~/.bashrc文件,在末尾添加(也可以修改/etc/profile,这个是对所有用户生效的,bashrc只是对当前用户)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:目录名
保存并退出,输入 source ~/.bashrc 

(2)临时添加(重启后失效)

#.表示当前目录,":"是分隔作用
export LD_LIBRARY_PATH=.:/opt/test

高质量编程之make管理器(使用makefile文件)

1、安装make
apt-get install build-essential
2、编写*.c、*.h文件
声明放在头文件、定义放在源文件
3、编写makefile文件

vim makefile
然后写入命令
例如:

main:main.o sub.o add.o
	gcc main.o sub.o add.o -o main
main.o:main.c
	gcc -c main.c -o main.o 
sub.o:sub.c 
	gcc -c sub.c -o sub.o 
add.o:add.c 
	gcc -c add.c -o add.o 
clean:
	rm -rf ./*.o
调用:
直接make(只会执行第一个目标,可以把后面所有目标写到第一个目标的依赖里面),就会编译出main的可执行文件(还会编译出main.o sub.o add.o)
make clean:就会删除掉所有.o文件(中间文件,可删除,但重新make会影响效率)
基本语法就是 目标:依赖文件
				执行编译行为
还可以将目标、依赖文件,编译行为都改成变量(提高可变性)
将上面例子改写:
#TARGET、OBJS、CC均为make自有宏定义
#TARGET为目标、OBJS为依赖文件、CC为C语言编译器名字、CXX为C++编译器名字、RM为删除文件命令。默认命令是“rm –f”。
TARGET=main
#makefile默认换行就是另一个语句,如果一定要换行的话可以用\接上
OBJS+=main.o
OBJS+=add.o sub.o
CC=gcc
#变量名要用$(变量),$@表示目标文件、$<表示第一个依赖文件、$^表示所有依赖文件,这三个都是自动化变量
#静态规则
%.o:%.c
	CC -c $< -o $@
$(TARGET):$(OBJS)
	CC $^ -o $@
#创建伪目标,当目录下有clean文件时也能执行
.PHONY:clean
clean:
	rm -rf ./*.o
	@echo clean---    # @表示隐藏此语句,执行时不打印该语句直接执行
4、makefile多目录编程

首先,把所有头文件都放进include目录里,把.c文件放到source目录里(除了main.c,main.c直接放在当前目录,include和source是二级目录)
makefile如下:

CC=gcc
RM=rm -rf
#文件目录说明
#绝对不可以使用DIR=`pwd`    #调用shell命令pwd获取当前目录
#详情查看文末链接调用shell命令的两种方式的差异
DIR 	:=$(shell pwd) 
INCDIR	:=$(DIR)/include
SRCDIR	:=$(DIR)/source
#预编译选项
CPPFLAGS:= -I$(INCDIR)
TARGET=main
#获得依赖文件.o,所先用wildcard获取匹配模式文件名函数找到所有.c文件
#然后使用patsubst %.c,%.o,$(SRCS)模式字符串替换函数将SRCS中的.c全部替换成.c等到依赖文件(因为.o可能还没生成,只能替换得到)
#foreach dir,$(SRCDIR),$(wildcard $(dir)/*.c) 将$(SRCDIR)的字符串逐个拿出放到dir中,然后执行wildcard
SRCS:=$(wildcard *.c) #main.c
SRCS+=$(foreach dir,$(SRCDIR),$(wildcard $(dir)/*.c))
OBJS:=$(patsubst %.c,%.o,$(SRCS))

如果直接make不输入参数,则默认执行第一个目标所以在第一个目标依赖所有目标
all:$(TARGET) test clean
	@echo ----
%.o:%.c
	$(CC) -c $< %@ $(CPPFLAGS)
$(TARGET):$(OBJS)
	$(CC) $^ -o $@ $(CPPFLAGS)
.PHONY:test clean
test:
	@echo $(OBJS)
clean:
	$(RM) $(OBJS)
5、其他

(1)=和:=的差别

a=3
b=$a
c:=$a #立即=a
a=5   #要使用该变量时再=a,如果此时a已经改变,就是改变后的值了
test:
echo $(b) #b=5
echo $(c) #c=3

(2)$< $^ $@ $?

$<:第一个依赖;
$^:全部依赖
$@:目标
$?:被修改过的依赖(时间戳比目标文件晚的依赖,因为目标文件最后生成,如果依赖的时间戳比目标文件晚,则代表被修改过)

(3)调用shell命令的两种方式(获取目录时的大坑)
shell命令的两种方式的差异

	直接引用 `shell命令`       #末尾是换行符,makefile换行是另一个语句了,所以不能用这个方式获取依赖文件的目录
	函数调用$(shell shell命令) #换行符会被替换成空格
	$(shell echo *.c) 作用和$(wildcard *.c)一样

(4)路径的指定
头文件路径的搜索顺序:

由参数-I指定的路径(用CPPFLAGS:= -I$(CPATH)存储,指定多个路径时,按指定路径顺序查找)
   然后再找gcc的环境变量C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH
   再找内定目录

动态库的搜索路径搜索的先后顺序是:

编译目标代码时指定的动态库搜索路径;
   环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
   配置文件/etc/ld.so.conf中指定的动态库搜索路径(配置直接添加目录即可,配置完要用ldconfig);
   默认的动态库搜索路径/lib;
   默认的动态库搜索路径/usr/lib。

(5)gcc参数

-c              用于把源码文件编译成 .o 对象文件,不进行链接过程
-o              用于连接生成可执行文件,在其后可以指定输出文件的名称
-g              用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir           用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Ldir             用于把新目录添加到库搜索路径上,可以使用相对和绝对路径,“-L.”、“-L./include”、“-L/opt/include”
-Wall         生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见GCC手册,一般用这个足矣!
-w             关闭所有告警信息
-fPIC         用于生成位置无关的代码
可以写在CPPFLAGS(预编译,一般存储头文件目录,-I$(INCDIR))、CFLAGS(编译)、LBFLAGS(链接库,存储库目录,-L$(LBDIR))

(6)条件判断
判断是否相等(ifeq,ifneq;ifeq相等时为true,ifneq为false)

ifeq ($(num),11) #判断num是不是等于11
	echo "num=$(num)"
else ifeq($(num),12)
	echo "num=12"
else
	echo "nothing"
endif

判断是否定义(ifdef和ifndef)

ifdef $(num)
	echo "yes"
else 
	echo "no"
endif

链接

Makefile 简明教程

Makefile

shell命令的两种方式的差异

gcc指定路径

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值