Linux系统编程Day03

1. GCC编译器

1.1 GCC工作流程和常用选项
	gcc编译器从拿到一个c源文件到生成一个可执行程序,中间一共经历了四个步骤:
	1.预处理(生成.i文件 即gcc -E test.c -o test.i)
	2.编译(生成.s文件 即gcc -S test.i -o test.s)
	3.汇编(生成.o文件 即gcc -c test.s -o test.o)
	4.链接(生成可执行程序 gcc test.o -o test)	
		注:如果不加-o test 会自动生成一个可执行a.out文件

	gcc常用选项:
	选项					作用
	-o file			指定生成的输出文件名为file
	-E				只进行预处理
	-S(大写)			只进行预处理和编译
	-c(小写)			只进行预处理、编译和汇编
	-v / --version	查看gcc版本号
	-g				包含调试信息(配合gdb调试)
	-On n=0~3		编译优化,n越大优化得越多
	-Wall			提示更多警告信息(gcc -Wall -Werror test.c表示将所有警告作为错误信息输出)
	-D				编译时定义宏(gcc file.c -DDEBUG 即在预处理的时候加入宏DEBUG)

2.静态库和动态库

2.1静态链接和动态链接
	链接分为两种:静态链接、动态链接。

	1)静态链接
	静态链接:由链接器在链接时将库的内容加入到可执行程序中。
	优点:
	对运行环境的依赖性较小,具有较好的兼容性
	
	缺点:		
	生成的程序比较大,需要更多的系统资源,在装入内存时会消耗更多的时间
	库函数有了更新,必须重新编译应用程序
	
	2)动态链接
	
	动态链接:连接器在链接时仅仅建立与所需库函数的之间的链接关系,在程序运行时才将所需资源调入可执行程序。		
	优点:	
	在需要的时候才会调入对应的资源函数
	简化程序的升级;有着较小的程序体积
	实现进程之间的资源共享(避免重复拷贝)
	
	缺点:		
	依赖动态库,不能独立运行
	动态库依赖版本问题严重
2.2 静态库和动态库
	1.静态库的制作
	静态库可以认为是一些目标代码的集合,是在可执行程序运行前就已经加入到执行
	代码中,成为执行程序的一部分。

	按照习惯,一般以“.a”做为文件后缀名。静态库的命名一般分为三个部分:		
	前缀:lib
	库名称:自定义
	后缀:.a
	最终的静态库的名字应该为:libxxx.a
	
	制作步骤:
		步骤1:将c源文件生成对应的.o文件
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -c add.c -o add.o
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -c sub.c -o sub.o
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -c mul.c -o mul.o
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -c div.c -o div.o

		步骤2:使用打包工具ar将准备好的.o文件打包为.a文件 libtest.a
			在使用ar工具是时候需要添加参数:-rcs
			r更新
			c创建
			s建立索引
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ ar -rcs libtest.a add.o sub.o div.o mul.o
		
	2.静态库的使用
	静态库制作完成之后,需要将.a文件和头文件一起发布给用户。
	假设测试文件为main.c,静态库文件为libtest.a头文件为head.h
	
	编译命令:
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1/dir1$ gcc test.c -I./ -L./ -ltest -o test
	
	参数说明:	
	-L:表示要连接的库所在目录
	-I./: I(大写i) 表示指定头文件的目录为当前目录
	-l(小写L):指定链接时需要的库,去掉前缀和后缀

	3.动态库制作和使用
		共享库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。
	不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,
	规避了空间浪费问题。

		动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页
	会带来麻烦。用户只需要更新动态库即可,增量更新。
	
	按照习惯,一般以“.so”做为文件后缀名。共享库的命名一般分为三个部分:	
	前缀:lib
	库名称:自己定义即可
	后缀:.so
	所以最终的动态库的名字应该为:libxxx.so

	1)动态库制作

	步骤一:生成目标文件,此时要加编译选项:-fPIC(fpic)
	参数:-fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -fpic -c add.c
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -fpic -c sub.c
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -fpic -c mul.c
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -fpic -c div.c

	
	步骤二:生成共享库,此时要加链接器选项: -shared(指定生成动态链接库)
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ gcc -shared add.o sub.o mul.o div.o -o libtest.so
	
	步骤三: 通过nm命令查看对应的函数
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ nm libtest.so | grep add
	ldd查看可执行文件的依赖的动态库

	2)动态库的使用
	1.拷贝自己制作的共享库到/lib或者/usr/lib(不能是/lib64目录)
	
	2.临时设置LD_LIBRARY_PATH:
		export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径		
		
	3.永久设置,把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径,设置到~/.bashrc或者 /etc/profile文件中	
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ vim ~/.bashrc
		最后一行添加如下内容:	
		export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xujie/share/3rd/2share_test
	
	使环境变量生效
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ source ~/.bashrc
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ ./test
	
	4.将其添加到 /etc/ld.so.conf文件中		
	编辑/etc/ld.so.conf文件,加入库文件所在目录的路径		
	运行sudo ldconfig -v,该命令会重建/etc/ld.so.cache文件	
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ sudo vim /etc/ld.so.conf
		文件最后添加动态库路径(绝对路径)使生效	
		xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ sudo ldconfig -v
	
	5.使用符号链接, 但是一定要使用绝对路径	
	xujie@xujie-virtual-machine:~/桌面/dir4/dir1$ sudo ln -s /home/deng/test/6share_test/libtest.so /lib/libtest.so

3.GDB调试器

 3.1 生成调试信息
	一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,
我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可
以做到这一点。如:	
		gcc -g hello.c -o hello			
		g++ -g hello.cpp -o hello

	如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。

3.2 启动GDB
	1.启动gdb:gdb program
		program 也就是你的执行文件,一般在当前目录下。
	
	2.设置运行参数
		set args 可指定运行时参数。(如:set args 10 20 30 40 50 )
		show args 命令可以查看设置好的运行参数。
	
	3.启动程序
		run: 程序开始执行,如果有断点,停在第一个断点处
		start: 程序向下执行一行。
		
3.3 3 显示源代码(进入GDB调试模式下)
	用list命令来打印程序的源代码。默认打印10行。
	list linenum: 打印第linenm行的上下文内容.
	
	list function: 显示函数名为function的函数的源程序。
	
	list: 显示当前行后面的源程序。
	
	list -: 显示当前行前面的源程序。
	
	一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。
	
	set listsize count:设置一次显示源代码的行数。
	
	show listsize: 查看当前listsize的设置。

3.4 断点操作
	1)简单断点
	break 设置断点,可以简写为b
	b 10 设置断点,在源程序第10行
	b func 设置断点,在func函数入口处
	
	2)多文件设置断点
	C++中可以使用class::function或function(type,type)格式来指定函数名。	
	如果有名称空间,可以使用namespace::class::function或者function(type,type)格式来指定函数名。
		break filename:linenum -- 在源文件filename的linenum行处停住
		break filename:function -- 在源文件filename的function函数的入口处停住		
		break class::function或function(type,type) -- 在类class的function函数的入口处停住		
		break namespace::class::function -- 在名称空间为namespace的类class的function函数的入口处停住
	
	3)查询所有断点		
		info b
		info break
		i break
		i b

	4) 条件断点
		一般来说,为断点设置一个条件,我们使用if关键词,后面跟其断点条件。		
		设置一个条件断点:			
			b test.c:8 if Value == 5

	5)维护断点
		1)delete [range...] 删除指定的断点,其简写命令为d。
		如果不指定断点号,则表示删除所有的断点。range表示断点号的范围(如:3-7)。
		比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,
		当你还需要时,enable即可,就好像回收站一样。
		
		2) disable [range...] 使指定断点无效,简写命令是dis。	
		如果什么都不指定,表示disable所有的停止点。
	
		3) enable [range...] 使无效断点生效,简写命令是ena。	
		如果什么都不指定,表示enable所有的停止点。

	6)调试代码
		1.run 运行程序,可简写为r
		2.next 单步跟踪,函数调用当作一条简单语句执行,可简写为n
		3.step 单步跟踪,函数调进入被调用函数体内,可简写为s
		4.finish 退出进入的函数
		5.until 在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体,可简写为u。
		6.continue 继续运行程序,停在下一个断点的位置,可简写为c
		7.quit 退出gdb,可简写为q

	7) 数据查看
	查看运行时数据
	print 打印变量、字符串、表达式等的值,可简写为p	
	p count 打印count的值

	8) 自动显示
		你可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量
	会自动显示。相关的GDB命令是display。

	display 变量名
	info display -- 查看display设置的自动显示的信息。
	undisplay num(num是info display时显示的编号)
	delete display dnums… -- 删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
	disable display dnums…
	enable display dnums…
	disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。

	9) 查看修改变量的值
	1)ptype width -- 查看变量width的类型		
		type = double
	
	2)p width -- 打印变量width 的值
		$4 = 13
	
		你可以使用set var命令来告诉GDB,width不是你GDB的参数,而是程序的
	变量名,如:	
		set var width=47 // 将变量var值设置为47
	
	注:在你改变程序变量取值时,最好都使用set var格式的GDB命令

4. Makefile

4.1 Makefile简介
	Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整
个工程完全自动编译,极大的提高了软件开发的效率
	make主要解决两个问题:
	1) 大量代码的关系维护	
		大项目中源代码比较多,手工维护、编译时间长而且编译命令复杂,难以记忆及
	维护,把代码维护命令及编译命令写在makefile文件中,然后再用make工具解析此
	文件自动执行相应命令,可实现代码的合理编译
	
	2) 减少重复编译时间
	    n 在改动其中一个文件的时候,能判断哪些文件被修改过,可以只对该文件进行重
	新编译,然后重新链接所有的目标文件,节省编译时间

4.2 Makefile语法规则
一条规则:
	目标:依赖文件列表
		命令列表
		
	Makefile基本规则三要素:
	1)目标:	
	通常是要产生的文件名称,目标可以是可执行文件或其它obj文件,也可是一个动作的名称
	
	2)依赖文件:	
	用来输入从而产生目标的文件
	一个目标通常有几个依赖文件(可以没有)
	
	3)命令:	
	make执行的动作,一个规则可以含几个命令(可以没有)
	有多个命令时,每个命令占一行

4.3 make命令格式:
	make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。
	make命令格式:		
	make [ -f file ][ options ][ targets ]
	
	1.[ -f file ]:		
		make默认在工作目录中寻找名为GNUmakefile、makefile、Makefile的
	文件作为makefile输入文件
		-f 可以指定以上名字以外的文件作为makefile输入文件
	
	2.[ options ]		
	-v: 显示make工具的版本信息
	-w: 在处理makefile之前和之后显示工作路径
	-C dir:读取makefile之前改变工作路径至dir目录
	-n:只打印要执行的命令但不执行
	-s:执行但不显示执行的命令
	
	3.[ targets ]:		
	若使用make命令时没有指定目标,则make工具默认会实现makefile文件内的第一个目标,然后退出
	指定了make工具要实现的目标,目标可以是一个或多个(多个目标间用空格隔开)。

4.4 Makefile示例
	测试程序: test.c add.c sub.c mul.c div.c

	1 最简单的Makefile		
	test:test.c add.c sub.c mul.c div.c
	    gcc test.c add.c sub.c mul.c div.c -o test
	缺点:效率低,修改一个文件,所有文件会被全部编译

	2. 第二个版本Makefile
	test:test.o add.o sub.o mul.o div.o
	    gcc test.o add.o sub.o mul.o div.o -o test
	​
	test.o:test.c
	    gcc -c test.c
	add.o:add.c
	    gcc -c add.c
	sub.o:sub.c
	    gcc -c sub.c
	mul.o:mul.c
	    gcc -c mul.c
	div.o:div.c
	    gcc -c div.c

	4.5 Makefile中的变量
	1)定义变量方法:		
	变量名=变量值
	
	2)引用变量:		
	$(变量名)或${变量名}
	
	3)makefile的变量名:		
	makefile变量名可以以数字开头
	变量是大小写敏感的
	变量一般都在makefile的头部定义
	变量几乎可在makefile的任何地方使用
	
	Makefile的第三个版本:
	#变量
	OBJS=add.o sub.o mul.o div.o test.o
	TARGET=test
	​
	$(TARGET):$(OBJS)
	    gcc $(OBJS) -o $(TARGET) 
	​
	add.o:add.c
	    gcc -c add.c -o add.o
	​
	sub.o:sub.c
	    gcc -c sub.c -o sub.o
	​
	mul.o:mul.c
	    gcc -c mul.c -o mul.o
	​
	div.o:div.c
	    gcc -c div.c -o div.o
	​
	test.o:test.c
	    gcc -c test.c -o test.o
	​
	clean:
	    rm -rf $(OBJS) $(TARGET)

4.6 自动变量

	$@: 表示规则中的目标
	$<: 表示规则中的第一个条件
	$^: 表示规则中的所有条件, 组成一个列表, 以空格隔开,如果这个列表中有重复的项则消除重复项。
	注意:自动变量只能在规则的命令中中使用

	Makefile的第四个版本:
	#变量
	OBJS=add.o sub.o mul.o div.o test.o add.o
	TARGET=test
	CC=gcc
	​
	#$@: 表示目标
	#$<: 表示第一个依赖
	#$^: 表示所有的依赖
	​
	$(TARGET):$(OBJS)
	    #$(CC) $(OBJS) -o $(TARGET) 
	    $(CC) $^ -o $@
	    echo $@
	    echo $<
	    echo $^
	​
	add.o:add.c
	    $(CC) -c $< -o $@ 
	​
	sub.o:sub.c
	    $(CC) -c $< -o $@ 
	​
	mul.o:mul.c
	    $(CC) -c $< -o $@ 
	​
	div.o:div.c
	    $(CC) -c $< -o $@ 
	​
	test.o:test.c
	    $(CC) -c $< -o $@
	​
	clean:
	    rm -rf $(OBJS) $(TARGET)
	​

4.7 模式规则
	模式规则示例:		
		%.o:%.c	
		$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

	Makefile第五个版本:
	OBJS=test.o add.o sub.o mul.o div.o
	TARGET=test
	$(TARGET):$(OBJS)
	    gcc $(OBJS) -o $(TARGET) 
	​
	%.o:%.c
	    gcc -c $< -o $@

4.8 Makefile中的函数
	makefile中的函数有很多,在这里给大家介绍两个最常用的。
	
	1.wildcard – 查找指定目录下的指定类型的文件
		src = $(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src
		
	2.patsubst – 匹配替换
		obj = $(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o		
		在makefile中所有的函数都是有返回值的。		
		
	Makefile第六个版本:
		SRC=$(wildcard *.c)
		OBJS=$(patsubst %.c, %.o, $(SRC))
		TARGET=test
		$(TARGET):$(OBJS)
		    gcc $(OBJS) -o $(TARGET) 
		​
		%.o:%.c
		    gcc -c $< -o $@
		.PHONY:clean
		clean:
		    rm -rf $(OBJS) $(TARGET)

4.9Makefile中的伪目标
	clean用途: 清除编译生成的中间.o文件和最终目标文件		
	make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令,解决方案:
	
	伪目标声明: .PHONY:clean		
	声明目标为伪目标之后,makefile将不会该判断目标是否存在或者该目标是否需要更新
	
	clean命令中的特殊符号:		
		“-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”
		“@”不显示命令本身,只显示结果。如:“@echo clean done”
	
	Makefile第七个版本:		
		SRC=$(wildcard *.c)
		OBJS=$(patsubst %.c, %.o, $(SRC))
		TARGET=test
		$(TARGET):$(OBJS)
		    gcc $(OBJS) -o $(TARGET) 
		​
		%.o:%.c
		    gcc -c $< -o $@
		.PHONY:clean
		clean:
		    rm -rf $(OBJS) $(TARGET)
	 
	
	总结: 一条规则,两个函数,三个变量。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值