网络编程-Linux系统编程1
Linux系统编程1
快捷键
ctrl+a
:光标移到命令行最前面
ctrl+e
:光标移到命令行最后面
ctrl+u
:删除整行
gcc编译的四个阶段
预处理:
gcc -E helloworld.c -o helloworld.i
编译:
gcc -S helloworld.i -o helloworld.s
编译阶段出错,会给出行号汇编:
gcc -c helloworld.s -o helloworld.o
链接:
gcc helloworld.o -o helloworld
链接阶段出错,会有collect: error: ld
gcc编译常用命令
-o
:指定生成的文件-I
:指定头文件的路径-g
:编译时添加调试语句(要使用gdb调试时必须加这个参数)-Wall
:显示所有警告信息-D
:向当前程序中注册一个宏,如gcc hello.c -D HELLO
就是注册了一个HELLO的宏用作开关来输出调试信息
静态库和动态库
静态库会在链接的时候直接把库文件复制到程序中,运行的时候不再依赖库文件。以.a
为后缀
动态库是在运行时动态加载。以.so
为后缀
1 |
|
对于这个函数,使用了math.h中的exp函数
静态链接
1 | gcc -static main.c -o main -lm |
动态链接
1 | gcc main.c -o main -lm |
使用了-l
参数,后边的m
是math.h
的基本名称
可以使用ldd main
查看动态链接文件的都链接了哪些库
1 | wufang@wufang:~/C++_base_learning$ ldd test1 |
如何判断一个函数是否需要链接呢?
1 | man 3 函数名 |
静态库制作
- 将.c文件编译生成.o文件
gcc -c add.c -o add
- 使用ar工具制作静态库
ar rcs lib库名.a add.o sub.o
- 编译静态库到可执行文件中
gcc main.c lib库名.a -o main
按这样三部编译出可执行文件后,会出现如下警告
1 | test.c:8:33: warning: implicit declaration of function ‘add’ [-Wimplicit-function-declaration] |
这个警告告诉:对add的隐式声明,因此,我们需要额外对使用的库函数进行声明,写在一个库名.h
文件中,文件格式如下
1 |
|
这使用了头文件首位,目的是:防止重复引入和重复定义。https://www.cnblogs.com/flowingwind/p/8304668.html
制作好.h文件后,在.cpp文件中引入.h头文件(#include "mymath.h"
)。此时的编译命令为
1 | gcc main.c ./lib/lib库名.a -o main.out -I ./inc |
静态库文件放在lib中,头文件放在inc中
动态库制作
参考:https://cloud.tencent.com/developer/article/1711778
将.c文件编译生成与位置无关的二进制文件:
gcc -c add.c -o add.o -fPIC
使用
gcc -shared
制作动态库:gcc -shared -o lib库名.so add.o sub.o div.o
编译可执行程序,指定所使用的动态库。
-l
指定库名,-L
指定动态库路径,-I
指定库的头文件路径gcc test.c -o test.out -l库名 -L./lib -I ./inc
注意:-l后边紧跟库名,-L后紧跟库路径,空格可有可无
按上边步骤完成后,运行可执行文件,报错
1 | wufang@wufang:~/C++_base_learning/dynamic$ gcc test.c -o test.out -lmymath -L./lib -I./inc |
这个报错是因为动态链接器在运行程序的时候找不到需要链接的库。解决办法有三种,如下给出视频中的方法:
即 将动态库的路径写入用户终端配置文件.bashrc
中,
1 | vim ~/.bachrc #打开用户自己的终端配置文件 |
解决方法合集:https://www.jianshu.com/p/a3b2b6f5f0fc
gdb调试工具
基础指令:
-g
:使用该参数编译可执行文件,得到调试表gdb ./a.out
:set listsize 行数
:设置list打印行数list 1
:从第一行开始打印b/break
:设置断点,后边可以跟着行号或者函数名run
:直接运行程序,如果设置断点了,会运行到断点,如果程序出错了,会直接运行到出错位置s/step
:下一条指令(会进入函数)n/next
:下一条指令(越过函数)finish
:进入函数后,结束当前函数调用,即退出该函数p/print
:p i 查看变量i的值c/continue
:运行程序到下一个断点或者结束quit
:退出gdb
其它指令:
start
:从main函数的第一行开始set args
:设置main函数命令行参数info b
:查看所有断点信息b 20 if i = 5
设置条件断点,用在循环语句,嵌套递归中bt
:查看程序中正在存活的栈帧fram 栈帧编号
:切换栈帧display 变量
:跟踪变量undisplay 变量编号
:取消跟踪
Makefile
简易版
test.c中是这样的
1 |
|
1个规则:
目标:依赖条件
(一个Tab缩进)命令
1 | test:test.o add.o sub.o |
基本原则:
- 若想生成目标文件,检查规则中的依赖条件是否存在,如不存在,则寻找是否有规则用来生成该依赖文件
- 检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任何一项被更新,则目标更新
2个函数:
1 | src = $(wildcard *.c) #找到当前目录下所有后缀未.c的文件,赋值给src |
3个变量:
$@:在一条规则的命令中,表示该规则的目标
$^:在一条规则的命令中使用,用于表示这条规则的所有依赖条件
$<:在一条规则的命令中使用,用于表示这条规则的依赖条件列表中的第一个依赖条件
1 | src = $(wildcard *.c) |
模式规则:
1 | test.o:test.c |
最终的makefile代码可以这样写
1 | src = $(wildcard *.c) |
重构版
项目文件夹下有如下
1 | inc makefile obj src |
其中,inc
文件夹存放头文件
,src
文件夹存放.c或者.cpp源代码
,obj
文件夹存放.o目标文件
makefile
文件如下
1 | src = $(wildcard ./src/*.c) |