基础
Linux系统上真正识别的可执行文件是二进制程序,比如usr/bin/passwd
这种。
用户一般用文本编辑器(比如vim)编写源代码,再通过编译器将源代码编译成操作系统能看懂的二进制程序,经过编译与链接后就能产生一个可以执行的二进制程序。
编译过程中会产生目标文件(.o结尾的文件),这是源代码(.c结尾的文件)经过编译后还未进行链接的中间文件。
总结一下就是:
- 开放源代码: 即程序代码,人类写的程序语言,但是电脑不认识
- 编译器:将程序代码“翻译”成就算是能看懂的语言
- 目标文件:源代码经过编译后还未进行链接的中间文件
- 可执行文件:经过编译器变成的二进制程序,计算机看得懂所以能执行
gcc -c hello.c
//仅将源代码编译为目标文件,但不产生二进制执行文件
gcc -O hello.c -c
//-O编译时根据操作环境给予最佳执行速度
//并且会生成hello.o这个文件
gcc -o hello hello.c
//-o后面跟着要输出的二进制文件名
gcc -o hello hello.c -Wall
//加入-Wall后程序编译更加严谨,会显示警告信息
单一程序
- 编写源代码
- 用gcc编译源代码
如果不加上任何参数,则执行文件的文件名会被自动设置为a.out - 最后测试
这里的./
意思是当前目录下。如果不加./
,系统会把我输入的东西当作一个命令执行,这将导致报错:找不到a.out这个命令;加上./
将指明相对路径,系统就知道这不是个命令,所以会执行当前目录下的a.out。
换一种方式
现在生成一个目标文件进行其他操作,而且执行文件的文件名也不用默认的a.out。
加上-c选项编译出目标文件
再将目标文件链接后生成可执行文件hello(绿色的那个),再执行该文件
这里实际只有一个目标文件,没有突出链接
的特点。给我一种不需要生成执行文件,用a.out就够了的感觉。其实不是,当有多个目标文件时就知道链接的重要性
主、子程序链接
接下来演示:
主程序:
主程序调用的函数
- 进行编译
- 将两个目标文件链接成可执行文件thanks
- 执行
这里如果哪次更新了thank2.c的内容,则我只要重新编译该.c文件,再重新编译一个.o文件,最后重新和thank.o进行链接即可
调用外部函数库
这里新版gcc可以直接gcc sin.c
,因为新版会主动帮你将需要的函数库放进来编译,所以只要加上#include <math.h>
就行。
以前这样加入额外函数库:
-l表示加入某个函数库
-m表示加入的是libm.so这个函数库
-L表示在/lib和/lib64这两个目录下找我要的函数库
make
假设执行文件有3个源代码文件,f1.c、f2.c、main.c。
如果想让这个程序运行就要一个个的执行gcc -c然后gcc -o再链接。而且如果其中一个文件重新编译,还得再执行一遍上述操作。但是利用make可以自动执行该操作。
- 先写一个makefile文件
- 执行make
可以看到make自动将源文件进行了编译然后链接,最后生成了test执行文件。
如果最后再执行一遍make
会发现系统显示test已经是最新的了。
makefile的基本语法
(这里讲的比较简洁,深入学习看这里:跟我一起写Makefile)
target : 目标文件1 目标文件2
【Tab】 gcc -o 欲建立的文件 目标文件1 目标文件2
target就是我们要建立的文件,目标文件是具有相关性的文件。注意命令行必须以【Tab】开头!
还可以编写clean操作将生成的执行文件和目标文件删除:
所以现在makefile里就有两个目标,分别是test和clean:想要建立test的话就执行make test
,想清除文件就执行make clean
。想先清除目标文件再编译test的话可以执行make clean test
使用变量
可以用变量简化makefile
和bash shell脚本呢的语法不太相同的几点是:
- 变量的变量内容以【=】隔开,同时两边可以有空格
- 变量左边不可以有【Tab】
- 变量欲变量内容在【=】两边不能有【:】
- 变量最好写成大写
- 运用变量时以${变量}或 $(变量)使用
- 在该shell里的环境变量可以被套用,比如CFLAGS
- 命令行模式也能设置变量
在gcc进行编译时会主动读取CFLAGS这个环境变量,所以我可以直接在shell中定义出这个环境变量,也能在makefile里定义,更可以在命令行中定义:
在makefile中:
如果CFLAGS 的内容在命令行中和环境变量中不相同,那以哪个方式输入的优先呢?环境变量使用的规则是这样的
- make命令行后面加上的环境变量优先
- makefile里面指定的环境变量第二
- shell原本具有的环境变量第三