一 GCC
1 概念
可以在VS code里面进行终端调试
编译c文件:生成的app是目标文件
执行程序:./app
如果直接编译c文件不给目标名称,默认生成a.out(a是名称 out是后缀)
注意:Linux中可执行程序为绿色
2 编程语言的发展
3 GCC工作流程
预处理:将头文件复制到源代码中、删除注释、宏替换等
可执行程序:windows .exe Linux .out
流程展示:
1 将源代码-E预处理生成预处理后的源代码:gcc test.c -E -o test.i
2 将预处理后的源代码-S编译生成汇编代码:gcc test.i -S -o test.s
3 将汇编代码通过-c汇编生成目标代码:gcc test.s -c -o test.o
4 将目标代码进行链接生成可执行程序:./test.o
注意:也可以gcc test.c变成可执行程序,也会包含前面四个阶段
4 GCC常见参数选项
gcc test.c -o test -D 宏名
5 g++和gcc的区别
一样的效果
1 将c文件生成可执行程序test:gcc test.c -o test
2 生成app需要test.c:gcc -o app test.c
二 静态库
1 概念
2 静态库的制作
案例:想要将加减乘除打包成静态库(JTK)供别人使用
查看当前文件夹的目录数:tree
步骤:
1 利用-c获得.o文件 :gcc -c add.c sub.c mult.c div.c (main和h文件不需要)
2 创建静态库(JTK)
3 静态库的使用
Linux中文件夹表示为蓝色
src 源代码文件
lib 库文件
步骤:
1 可以在library中重新制作一个静态库或者把上面我们已经在calc文件夹的静态库拷贝过来
拷贝:cp 想要复制文件路径 复制到的文件路径
把上一级../目录下的calc文件中的libJTK.a拷贝到这一级./目录下library文件下的lib目录中
注意:分发的时候一定要把头文件和静态库给别人,否则无法使用
2 编译main.c
如果直接对main进行编译的话,由于h头文件不在main中所以会报错
解决方法:
1 将h头文件移到main中
2 采用GCC的参数命令:在末尾加 -I 头文件路径(由于头文件和main在同一级也就是当前文件)
解决了头文件的报错发现add 类的c文件是未定义的使用(因为在编译main的时候只有头文件和库,但库里面的函数没有找到)
解决方法:GCC参数命令 -l 库名称 -L 库路径 (一定要指定库名称,可能有多个库)
易错:
库名称:JTK
库文件名称:libJTK.a
三 动态库(共享库)
1 动态库的制作
递归拷贝:文件夹之间的拷贝 cp 文件名 拷贝到的文件夹路径 -r
将JTK文件夹中的calc和library拷贝到DTK文件夹中:
现在在JTK文件夹中输入命令:cp calc library ../DTK -r
删除文件夹:rm -rf 想删除的文件
动态库制作步骤:
1 通过gcc -fpic生成与位置无关的代码 gcc -c -fpic add.c sub.c mult.c div.c
2 通过gcc -shared 得到动态库 gcc -shared add.o sub.o mult.o div.o -o libDTK.so
2 动态库的使用
1 把上面制作的动态库拷贝到library下或者自己制作一个动态库
2 编译main文件生成可执行程序
会出现和静态库一样的问题:头文件报错、加减乘除c文件未定义的使用
报错! 动态库加载失败(因为运行的时候,可执行程序找不到动态库的代码)
四 动态库加载失败
1 动态库加载失败原因(原理)
注意:程序都是放在内存中运行的,所以动态库也要加载到内存中
检查可执行程序的依赖关系:ldd 可执行程序名称
解决方法:动态载入器 ld-linux.so
2 解决动态库加载失败问题
可以把终端看成一个程序,ls命令会被shell解析器解析,shell解析器怎么找到命令的路径?环境变量
查看环境变量:env(键值形式)
获取动态库的路径:最后所在位置 pwd
两种方法:
1 方法一:终端配置
1 配置环境变量 export 环境变量名称=$获取环境变量的内容:动态库路径
配置环境变量的内容然后拼接动态库的路径
2 查看环境变量 echo $环境变量名称
3 tree
4 查看可执行程序的依赖关系 ldd 可执行程序名称
之前no found
5 动态库加载成功可以运行程序
总结:在终端解决动态库加载失败的问题,在关闭终端之后这个配置的环境变量也就失效了
2 方法二 :永久配置
1 用户级别配置 .bashrc
bashrc:配置文件
回到home目录:cd
拓展:vim编辑器跳转最后一行:shift+g
vim编辑器保存并退出::wq
步骤:
1 进入vim 编辑器 .bashrc 里面配置 export 环境变量名称=$获取环境变量的内容:动态库路径
2 使得vim的修改生效
1 . . bashrc
2 source .bashrc
3 动态库加载成功
2 系统级别配置
拓展:家目录 ~/
加sudo权限 不推荐使用
五 静态库与动态库的对比
1 程序编译成可执行程序的过程
2 静态库的制作过程
3 动态库的制作过程
4 静态库的优缺点
5 动态库的优缺点
速度:因为静态库已经加载到程序中,程序一启动就调用静态库,但是动态库只有加载的时候才调用,所以速度相比静态库要慢一点。
六 Makefile
1 概念
红色的表示压缩文件
2 Makefile文件的命名和规则
步骤:
1 由Vim进入到Makefile文件
2 在Makefile文件中写入
3 执行make命令,生成app 最后执行
Makefile有什么作用?
答:如果不用make的话直接在Linux中输入命令gcc c文件 -o 可执行文件名称即可,可以看出使用Makefile更加繁琐,但是如果我们更改了C文件中的某一个或者多个就要重复输入gcc c文件 -o 可执行文件名称的命令,如果利用make的话就不需要更改了,会大大减少复杂度。
3 工作原理
1 检查依赖
例:
一个Makefile下面有很多的规则,在执行红色部分的命令的时候,会先去查找依赖存不存在,如果存在就执行,如果不存在就向下继续寻找
2 检测更新
例:
Makefile
Makefile1
Makefile比Makefile1简单,所用的时间也短,如果两者同时存在的时候,不会执行Makefile1,会显示app已是最新,如果现在更改其中c文件中的一个,就会更新一下那个c文件
4 变量
例子:改写
定义变量:
src 所有依赖文件
target 可执行文件
注意:这个改写只是把一行app的规则改了,下面的还是要写,增加了复杂度
解决方法:模式匹配
5 模式匹配
原始:
改写后:
$< 第一个依赖名称
$@ 目标名称
利用通配符减少后面四段的重复写法,那么变量的获取能否简写?
解决:函数
6 函数
1 找到所有c文件 $(wildcard 所寻找的c文件路径 可多个参数)
2 找到所有o文件 $(patsubst %.c, %.o ,所有被替换文件)
例子:
原始:
改写:
7 清除
Makefile中如果加入清除指令,他是没有依赖的,可以理解为无条件成立,所以永远都是最新的。如果在Makefile以外的地方创建一个clean,则不会执行。
由于Makefile里面的clean是无条件成立,永远都是最新的,那么也就意味着在Makefile以外的地方不会再生成clean文件和命令,这种情况我们可以将clean定义为伪目标。
8 伪目标: .PHONY:伪目标名称
七 GBD调试
1 概念
2 准备工作
调试:gcc test.c -o test -g
3 GDB命令
例子:调试和正常执行的区别
调试:gcc test.c -o test -g 生成test
执行:gcc test.c -o test 生成test1
对比文件大小:
发现经过调试的test比未经调试的test1会更大一点,说明加入了调试信息
命令:
1 启动GDB gdb 调试文件名称
如果test程序里面main函数由两个参数,执行程序可以 ./teat 参数1 参数2
这是在Linux命令界面操作,如果在GDB调试中如何使用?
2 给程序设置参数 set args 参数1 参数2 ....
3 获取设置参数 show args
一般23结合使用
注意:Ctrl + L 清屏指令
4 退出GDB quit or q
GDB调试结束后退出回到shell解析器终端
5 查看GDB 使用手册 help
命令很多,回车展现下一屏
6 查找特定命令 help 命令名称
7 查看当前文件代码
查看当前文件代码由两种方式:
1 vim编辑器中查看,没有显示行数 :set nu
注意:vim编辑器中查看 vim test.c 如果是vim test 会出现乱码(因为test是可执行程序,我们看的是代码)而且设置行数的时候不是insert是命令
2 可以利用GDB调试工具命令实现
注意:利用方式二的时候要先进行生成具有调试信息的test gcc teat.c -o test -g,否则进行GDB会报错,生成调试信息的test的前提是test.c文件和test必须在一起
1 从默认位置显示 list or l
一般是默认打开main所在的文件的代码,多次list或者enter显示完全
2 从当前文件指定的行显示 list 行号
3 从当前文件指定的函数显示 list 函数名
对于cpp文件来说一定要用g++编译,否则找不到C++的库,报错
把cpp的源文件编译成带调试信息的可执行文件main
如果是在此文件下查看其他文件的代码
8 查看非当前文件的代码
1 指定文件名 list 文件名:行号
2 指定函数名 list 文件名:函数名
list默认显示十行代码,但是可以自定义
9 设置显示的行数
1 默认十行 show list or show listsize
2 自定义 set listsize 15
4 GDB命令——断点操作
5 GDB命令——调试命令
八 标准C库IO函数和Linux系统IO函数对比
I input O output
以文件的角度:从内存写东西到文件中称为输入,从文件中读取信息到内存中称为输出
以内存的角度:从文件中读取数据到内存中称为输入,从内存中把数据写到文件中称为输出
一般我们按照内存角度考虑,因为程序在内存中运行
1 标准C库IO函数
注意:标准C库IO函数可以跨平台,可以在Windows和Linux系统下运行
跨平台运行的两种方式:
1 不同平台有不同的虚拟机
2 比如要使用C库中的fopen函数,Windows平台要使用就调用Windows系统的API LInux 就调用Linux系统API
文件描述符:指向一个已经打开的文件,Windows中称为文件句柄
IO函数帮助文档 man 3
缓冲区的作用:
答:缓冲区可以提高执行程序的效率,举个例子,要想把数据写入磁盘有两种方式,方法一:写一次运一次到磁盘中,方法二:先把数据写到缓冲区中,之后依次写到磁盘中,明显发现方法二更适合,因为方法一频繁的操作硬件磁盘会更慢。当缓冲区满了、关闭文件或者强制调用fflush都可以向磁盘中写数据。(降低写数据的次数)缓冲区默认大小 8k=8192b
标准C库IO函数和Linux系统的IO函数的区别:
答:标准C库IO函数是带有缓冲区的,Linux系统IO函数是没有缓冲区的,调用一次write就会写入一次磁盘,调用一次read就会读一次磁盘
例子:
1 如果进行网络通信,是需要追求效率的,如果给对方发数据,结果数据还在缓存区,效率太慢,需要选用Linux系统的IO函数
2 如果对磁盘进行读写操作的话,需要缓冲区提高效率,需要调用标准C库的IO函数
2 标准C库IO和Linux系统IO函数的关系 调用与被调用
3 虚拟地址空间
注意:虚拟地址空间是不存在的,是程序员想象出来的
程序:磁盘上的代码
进程:运行中的程序,会加载到内存中
有一个进程就会有一个虚拟地址空间,它的大小由电脑的cpu决定,32位大概是2
的32次方大概4G
内存管理单元 MMU
4 文件描述符
1 可执行文件是不占用内存的,他在磁盘里面,但是进程也就是开始运行的可执行程序是占内存的
每个进程里面有1024个文件描述符,也就是说一个进程里面最多能打开1024个文件
2 PCB process control break 进程控制块
3 在Linux中,一切皆文件
4 一般来说文件描述符是指向一个文件的,但是指向终端的时候,可以把终端看成一个设备文件
5 一个文件可以被打开多次,返回的文件描述符也是不一样的,被close释放之后可以重新使用文件描述符,找最小的文件描述符来使用
九 Linux系统IO函数
手册:
Linux系统IO函数 man 2 函数名
标准C库IO函数 man 3 函数名
系统调用 system call
1 open打开文件
前提:打开的是一个已经存在的文件
两种open函数:头文件都相同
int open(const char *pathname, int flags);
运行报错 因为没有a.txt文件 返回-1 显示perror
如果创建a.txt 调用成功就关闭了
2 open创建新文件
不同用户umask不同,普通用户默认0002 root用户默认0022 也可以自己设置
头文件:
int open(const char *pathname, int flags, mode_t mode);
提问:为什么位或?
答:flags参数是一个int类型的数据,占4个字节,32位, flags 32个位,每一位就是一个标志位。位或是有1即1,位或一个O_CREAT实际上就是将O_CREAT加到之前的O_RDWR上。
3 关闭文件
4 read函数
ssize_t read(int fd, void *buf, size_t count);
5 write函数
ssize_t write(int fd, const void *buf, size_t count);
例子:将a.txt拷贝到b.txt
本质:读取a.txt文件的数据写入到b.txt中,利用读写函数
思路:
头文件:
两个文件大小相同,拷贝成功
6 lseek函数
对比:标准C库是通过指针访问,Linux系统函数通过文件描述符访问
off_t lseek(int fd, off_t offset, int whence);
四个作用:
例子:扩展文件的长度
eg:本来11个字节,扩展100 加一个空数据 112个字节
看hello.txt字节数
7 stat函数 查看文件信息
stat 文件名
int stat(const char *pathname, struct stat *statbuf);
1 stat结构体
2 st_mode变量 16位
程序员计算器电脑自带calc
通过a.txt创建软链接b.txt:
通过stat查看b.txt信息:
8 lstat函数 获取 软链接函数信息
int lstat(const char *pathname, struct stat *statbuf);
十 案例:模拟实现 ls -l 命令 列出当前文件所有信息
列出某个文件所有信息有两种方法:
1 ls -l a.txt
2 模拟实现 ./app a.txt 可以显示文件信息