【Linux学习笔记⑤】——GNU C语言开发环境【GNU make、静态库】


⌛️



GNU ☁️

上一篇文章链接: 【Linux学习笔记④】——Shell程序设计【变量 输入与输出 条件表达式 判断语句 循环语句 Shell函数】.
下一篇文章链接: 🚧 🚧…


一、GNU 概述

  ● GNU 的全称为 GNU’s Not Unix,这是官方的递归定义,永远找不到本义,是开源软件的幽默。

  ● GNU工程已经开发了一个被称为 “GNU”(GNU是“不是UNIX”的缩写)的、对 Unix 向上兼容的完整的自由软件系统 (free software system) 。由 Richard Stallman【自由软件运动的精神领袖、GNU计划以及自由软件基金会的创立者、著名黑客】完成的最初的 GNU 工程的文档被称为 ‘GNU宣言’,该宣言已经被翻译成多种其它语言。

  ● 你可以也可以不用为获取 GNU 软件而支付费用。不论是否免费,一旦你得到了软件,你在使用中就拥有三种特定的自由:
    ① 首先是复制程序并且把它送给你的朋友或者同事的自由。
    ② 而后是通过获取完整的源代码,按照你的意愿修改程序的自由。
    ③ 最后是发布软件的改进版并且有助于创建自由软件社团的自由。

比较项GNUUnix
起源由Richard Stallman开发的,他是MIT AI Lab的黑客。由Ken Thompson和Dennis Ritchie为贝尔实验室开发的。
最初命名GNU是开发的软件的名称。由贝尔实验室开发的名称为AT&T UNIX。
独自发挥作用该软件(Shell)本身无法运行,因为它需要一个内核才能与硬件交互。UNIX由Shell和内核组成,可以单独运行。
依赖GNU仅仅是Shell软件,它依赖于任何内核,并且正确地部署了UNIX内核。它不依赖于任何其他操作系统,它具有自己的组件。
源代码GNU源代码可免费向公众公开,可以根据要求修改代码。UNIX源代码不适用于公众。


二、GNU C 编辑器

  ● GUN C 是在 Linux 系统下的 C语言。

2.1 目标代码的生成过程

  ● 用高级语言编写的代码必须经过编译和链接,最终生成可执行的目标代码。

  ① 首先,C/C++源代码会经过编译器,汇编源代码经过汇编器,生成目标文件(.o文件)。
  ② 如果有多个文件需要编译,为了便于管理,可将具体操作按一定规则写入makefile文件,make工具根据makefile文件的要求执行相关命令生成目标文件。
  ③ 如果这些目标代码中的函数需要在其他应用中重复使用,可通过归档文件.ar将这些.o文件归档为函数库。
  ④ 最后,通过链接器将目标文件及相关函数库链接成为共享库、可链接文件或可执行文件。

在这里插入图片描述




2.2 GNU 概述

  ● GCC(GNU Compiler Collection)是 GNU 下编译器及其相关工具的集合。

  ● GCC 原名为 “ GNU C 语言编译器 ”,因为它原本只能处理 C 语言。但随着 GCC 的发展,在功能上得到了不断的扩充,现在它具有一下特点:
    ① 支持多种高级语言(如 C++、Java等)
    ② 支持多种硬件处理器(例如:x86、ARM等)
    ③ 支持多种操作系统平台(例如:Linux、Windows等)


2.3 GNU C 编译链接工具——gcc

  ■ 语法gcc [选项] 目标文件 源文件
  ▶功能:将 C 语言编译为目标代码或可执行文件。

  ● 常用选项如下

选项功能
-S编译生成汇编文件(.s)
-c编译生成目标文件(.o)
-o指定输出文件名
-Wall打印警告信息

  ● 例如

gcc -c test.c		# 生成目标代码 test.o
gcc -o test test.o	# 将目标文件 test.o 链接为可执行文件 test


2.4 GNU C 编译链接工具——as

  ■ 语法as [选项] 汇编文件
  ▶功能:将汇编语言源代码汇编为目标代码。

  ● 常用选项如下

选项功能
-o设置输出文件名
-M输出链接信息
-T commandfile指定链接命令文件

  ● 例如

ld -T linkcmds -o test test.o 	# 指定链接命令文件


2.5 GNU C 编译链接工具——ld

  ■ 语法ld [选项] 目标文件列表
  ▶功能:将若干目标文件和函数库链接在一起,重定位符号引用和数据。(在默认情况下,无须定义链接命令文件,链接器 ld 会使用默认的链接命令文件)

  ● 例如

as -o test.o test.s 	# 将汇编语言源代码 test.s 汇编为目标代码 test.o
# 由于 gcc 是编译工具的集成器, 因此汇编器 as 可以用 gcc -S 代替


三、项目管理工具——GNU make

3.1 项目管理概述

  ● 在开发规模较大的应用项目时,常采用模块化设计方法,即将系统分解为若干个模块。各模块完成各自特定的功能。

  ● 此时,系统中存在多个源代码文件。当生成最终的可执行文件时,必须逐个编译这些源代码文件,并在最后将所有的目标代码链接为可执行程序…如果这些步骤都需要手工操作就很耗时间。

  ● 为此 GNU 项目开发了一个用于自动完成这些操作的项目管理工具make,用户只需将这些步骤按一定的语法规则以命令的方式写入文本文件,一般命名为Makefile。此后用户只需在命令提示符下输入make命令,make工具会根据Makefile文件中的定义,自动执行一些列编译和链接工作。这便节省了时间。

3.2 基于 make 工具的项目管理

  ■ Makefile文件的语法

目标文件: 依赖文件1  依赖文件2 ... 依赖文件n
<tab键>命令1
<tab键>命令2
...
<tab键>命令n

  ◆ 说明:① 只有当所依赖的文件被更新时,make才执行相应的命令更新目标文件。② “命令” 指的是产生目标文件需要执行的命令。

  ■ make命令的语法make [选项] [目标]

  ▶功能:创建指定的目标,如果没有指定目标,则创建第一个目标。make使用的默认的规则定义文件是 GNUmakefile、makefile、Makefile。

  ● 常用选项如下

选项功能
-o设置输出文件名
-M输出链接信息
-T commandfile指定链接命令文件

  ● 样例如下:【首先编写一个 Makefile 文件,准备对其进行项目管理】

# test_script
appexam: main.o app.o mod.o lib.o
	gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h
	gcc -c main.c

app.o: app.c app.h
	gcc -c app.c

mod.o: mod.c
	gcc -c mod.c

lib.o: lib.c lib.h
	gcc -c lib.c

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。

  ● 然后我们对该脚本进行调用:

make appexam		# 指定要创建的目标为 appexam 这个文件

  ● 然后,我们将得到各目标之间的依赖关系如下图所示:【当某个目标被修改时,则依赖它的所有上级目标(包括上级祖先等)都会被重新编译】

在这里插入图片描述
  ◆ :最下面一排的,“main.o” 改为 “main.c”、“app.o” 改为 “app.c”。


3.3 Makefile中的变量

  ● 为了使在 Makefile 中规则的书写更为简洁,也为了能适应不同的开发环境,可在定义 Makefile 定义变量,变量可用于保存文件名列表、命令和命令参数等。make 工具支持 4 种类型的变量:自定义变量、环境变量、预定义变量、自动变量。

3.3.1 自定义变量

  ● 这类变量有用户自定义,一般用大写字母表示。

  ■ 语法变量名=字符串

  ▶功能:将 “字符串” 赋值给 “变量名”。注:在 Makefile 中无数据类型。

  ★调用$(变量名)

  ● 样例如下:【即把 3.2 的例子改写如下】

# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	gcc -o appexam  $(OBJS)		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h
	gcc -c main.c

app.o: app.c app.h
	gcc -c app.c

mod.o: mod.c
	gcc -c mod.c

lib.o: lib.c lib.h
	gcc -c lib.c

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


3.3.2 环境变量

  ● make在运行过程中,会将环境变量转化为同名同值的make变量,用户也可在Makefile中对这些变量进行重新定义。

3.3.3 预定义变量

  ● GNU make预定义了一些变量(就像英语中的固定搭配的语法),它们在Makefile文件中可以直接使用。也可以对这些变量进行重新定义。一些常用的预定义变量如下表:

预定义变量名含义默认值
AR归档程序ar
AS汇编器as
CCC语言编译器cc
CXX带有标准输出的 C 语言预处理程序$(CC)-E
RM删除文件的命令rm -r

  ● 样例如下:【即可把 3.3.1 的例子改写如下】

# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	$(CC) -o appexam  $(OBJS)		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h
	$(CC) -c main.c

app.o: app.c app.h
	$(CC) -c app.c

mod.o: mod.c
	$(CC) -c mod.c

lib.o: lib.c lib.h
	$(CC) -c lib.c

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


3.3.4 自动变量

  ● 自动变量由make工具预先定义,具有特定的含义,它的值与规则中的目标和依赖对象有关。下面给出部分常用的自动变量及其定义。

变量功能
$^所有的依赖文件,以空格隔开,以出现的先后为序
$<第一个依赖文件的名称
$?所有的依赖文件,以空格分开,它们的修改日期比目标的创建日期晚
$*不包含扩展名的目标文件名称
$@目标的完整名称

  ● 样例如下:【即可把 3.3.3 的例子改写如下】

# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	$(CC) -o $@  $^		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h
	$(CC) -c -o $@ $<	# 相当于 gcc -c -o main.c

app.o: app.c app.h
	$(CC) -c -o $@ $<

mod.o: mod.c
	$(CC) -c -o $@ $<

lib.o: lib.c lib.h
	$(CC) -c -o $@ $<

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


3.4 Makefile文件中的潜规则

  ● 通常为了产生目标文件,需要在目标文件和依赖对象之间建立明确的规则,定义如何生成目标的流程,但有时可简化这些操作。即用以下三种规则。

3.4.1 隐含规则

  ● GNU make 定义了内置的隐含规则,在不给出产生目标文件的命令时,由make自动添加。

  ● 样例如下:【即可把 3.3.4 的例子改写如下】

# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	$(CC) -o $@  $^		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h

app.o: app.c app.h

mod.o: mod.c

lib.o: lib.c lib.h

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


3.4.2 后缀规则

  ● 定义:将具有某后缀的文件(例如.c文件)转换为具有另外一种后缀的文化(例如 .o文件)的方法。使用方法是将每个以两个成对出现的后缀名定义。

  ● 样例如下:【即可把 3.4.1 的例子改写如下】

# test_script
.c.o:
	gcc -c $<
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	$(CC) -o $@  $^		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


3.4.3 模式规则

  ● 模式规则是对具体规则的进一步抽象,定义了一类具有相同行为特点的规则,例如用%表示通配符号。

  ● 样例如下:【即可把 3.4.2 的例子改写如下】

# test_script
%.c: %.o
	$(CC) -c $<-o  $@	
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
	$(CC) -o $@  $^		# 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
	
main.o: main.c app.h

app.o: app.c app.h

mod.o: mod.c

lib.o: lib.c lib.h

clean:
	rm -f*.o		# 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。


四、创建和使用函数库

  ● 函数库:由若干目标文件按某种格式构成的集合,目标文件是由源文件经过编译生成的中间代码。在进行软件开发的过程中,往往会积累许多可复用代码。为了提高软件的开发效率,可将这些代码编译,并分类打包成函数库,供其他项目使用。

  ● 函数库可分为两类:静态库和共享库。它们俩在与应用程序的链接方式上具有不同的特点。
    ① 在链接静态库时,会将使用的静态库对象嵌入到可执行映像文件中。
    ② 在链接共享库时,仅当可执行映像文件中保留加载目标对象所需的信息后,在调用时,才真正将目标对象加载至内存。

4.1 静态库

  ● 静态库由ar工具创建。经编译的应用程序和静态库链接时,链接器将静态库中被调用的对象嵌入到可执行映像文件中,这样在没有静态库的环境下,应用程序也能独立运行。

  ● 静态库文件的命名规则是libxxx.a,以lib开头,.a作为文件名后缀

4.1.1 静态库管理工具

  ■ 语法ar [选项] [归档文件] 目标文件列表
  ▶功能:用于创建、修改和查询归档文件。

  ● 常用的一些选项如下

选项功能描述
-c创建一个函数库
-r向函数库中插入目标对象,若存在则替换
-u若函数库中已经存在同名目标,则用新目标更新
-t显示函数库中目标对象列表
-d从函数库中删除目标对象


4.1.2 创建静态库

  ● 样例如下:【用两个 C 源文件创建静态库】

  第一个命名为 “test_1.c” 的 C 语言源文件:

// test_1.c
int add(int x, int y)
{
	return x + y;
}

  第二个命名为 “test_2.c” 的 C 语言源文件:

// test_2.c
int func(int count)
{
	int sum = 0;
	for(int i = 1; i < count; i++)
		sum += i;
	return sum;
}

  创建静态库的步骤如下

(1) 编译 test_1.c 和 test_2.c, 生成目标文件
gcc -c -Wall test_1.c		# 生成目标文件 test_1.o
gcc -c -Wall test_2.c		# 生成目标文件 test_2.o

(2) 创建静态库 ---> 并将库命名为 test_lib.a
ar -cru lib_test.a test_1.o test_2.o
// 注: 	选项 c 告诉了 ar 创建一个新的静态库, 除非该静态库已存在
//		选项 r 告诉了 ar 替换已经存在的目标文件
// 		选项 u 告诉了 ar 被替换的目标文件必须是最新的

(3) 为了使用静态库, 首先需定义静态库的应用接口, 代码如下:
// test_interface.h
#ifndef _DEMOLIB_API_H
#define _DEMOLIB_API_H

extern int add(int x, int y);		// 外部函数声明(即说明该函数的实现在外部)
extern int func(int count);

#endif

  下面是使用lib_test.a的测试程序(该文件命名为test_file.c),代码如下:

// test_file.c
#incldue<stdio.h>
#incldue"lib_test.a"
int main()
{
	int val, x, y;
	x = 2;
	y = 18;
	val = add(x, y);
	printf("The mult of x and y is %d.\n", val);
	val = func(100);
	printf("The sum is %d.\n", val);
	return 0;
}

  利用静态库test_lib.a编译生成可执行文件test的具体方法如下:

gcc test_file.c -L. lib_test.a -o test		// -L. 表示静态库在当前目录下

  ● 具体运行结果如下

在这里插入图片描述
  ◆ 说明:上面的倒数第二个箭头的命令行 “vi test_file,c” 写错了,改成 “vi test_file.c

在这里插入图片描述

  ● 静态库的特点
    ① 运行时无需外部库的支持。
    ② 较高的运行速度。
    ③ 可执行文件具有较大的体积。
    ④ 不容易维护。


4.2 共享库

  ● 经过编译后的应用程序在和共享库链接时,与静态库不同,没有将共享库中的目标对象嵌入至映像文件,而是只在生成的可执行映像文件时嵌入。因此,离开了共享库的支持,应用程序就无法运行。

  ● 共享库文件的命名规则是libxxx.so

  ● 共享库的创建、使用和加载和静态库类似,这里不在赘述。

  ● 注意:若在当前目录下同时存在lib_test.alib_test.so,在默认情况下首先会使用共享库,若需要使用静态库则需要加上选项-static,示例如下:

gcc -static test_file.c -L. -lib_test.a -o test

  ● 共享库的特点
    ① 可执行的文件体积小。
    ② 容易维护
    ③ 不能离开动态库独立运行
    ④ 运行速度比较慢


4.3 动态链接库

  ● 动态链接库是运用共享库的一种方式,在运行的任何时刻都可以动态加载共享库。与一般使用的共享库不同,通常应用程序在启动时,不立即加载共享库,而是在需要时,动态加载共享库。在这种情况下,称共享库为动态链接库。(.dll



五、参考附录:

[1]《GNU/Linux编程》📚
人民邮电出版社

[2]GNU和Unix的区别
链接: https://www.vsdiffer.com/gnu-vs-unix.html.

上一篇文章链接: 【Linux学习笔记④】——Shell程序设计【变量 输入与输出 条件表达式 判断语句 循环语句 Shell函数】.

下一篇文章链接: 🚧 🚧…


⭐️ ⭐️

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值