9/24作业

1. 分文件编译

分什么要分文件编译?

防止主文件过大,不好修改,简化编译流程

1) 分那些文件

头文件:所有需要提前导入的库文件,函数声明

功能函数:所有功能函数的定义

主函数:main函数,所有的函数调用

2) 头文件的格式

头文件需要有防止头文件重复包含的机制

复制代码
#ifndef __文件名大写_H__
#define __文件名大写_H__

#endif

3) 如何编译

分文件编译的代码需要将两个.c联合编译

gcc main.c add.c

2. Makefile

Makfile是一个工程管理文件

作用:帮助程序员,简化编译流程

1) 理论基础

gcc分步编译:(分为4步)

预处理 -----> 编译 ------> 汇编 ------>链接

Makefile把编译过程分为两步:

  1. 生成二进制文件.o文件
  2. 使用.o文件完成最后的链接过程

2) Makefile文件的作用

简化了编译流程,可以完成每次不需要把全部的源文件都重新编译

如果源文件发生修改,只需要重新编译发生修改的源文件即可,节省了编译时间

Makefile会检查文件的时间戳,如果有文件时间戳更新(改文件就会重新生成)

3) make工具

sudo apt-get install make

make工具是读Makefile文件使用的,Makefile文件是make工具的唯一读入文件,

如果Makefile和makefile同时存在,make工具会自动读入小写的makefile文件

make的标准使用格式:

make 目标 ---> 直接运行Makefile中指定目标的那一条规则

如果直接在终端输入make并回车,make工具会自动执行Makefile中第一个目标

make -f 文件名 目标名 ---> 不读入默认文件,读入指定文件的指定目标

4) Makefile文件的书写

Makefile文件由:规则、变量、条件编译、函数构成

5) 规则的构成

Makefile由多条规则构成,每一条规则包含

目标:依赖

<tab>指令

#这是一条规则

目标:依赖

指令 #指令前面一定是一个tab键不是四个空格

#一般指令是依赖生成目标的过程

一条规则中一定要有一个目标,一条规则中可以有多个依赖

一条规则可以没有依赖,只执行某些指令

一条规则可以没有指令,只描述目标和依赖之间的关系

6) 第一个版本Makefile

复制代码
all:main    # 一般makefile中的第一个目标都是all:可执行文件
# 为了保证,最后Makefile文件执行后一定会生成一个可执行文件

main:main.o add.o 
    gcc main.o add.o -o main
main.o:main.c
    gcc -c main.c -o main.o
add.o:add.c
    gcc -c add.c -o add.o

clean:            # 删除生成的中间文件和可执行文件
    rm *.o main

makefile会自动进行推导(makefile没每次运行前,会自动生成文件依赖树)

7) Makefile中的变量

i) 自定义变量

变量名=变量的值 (Makefile中赋值运算两侧可以有空格也可以没有)

使用变量的值: {}、()、 ---\> 推荐使用 ()和shell做区分

= : 递归赋值 (以最后一次赋值为准)

+= : 追加赋值(追加新的值)

:= : 立即赋值(当前是什么值就立即赋值)

?= : 条件赋值(判断之前是否定义,如果定义,不重新赋值,否则赋值)

ii) 预定义变量

系统预先定义好的一些变量,可能有默认值可能没有

RM 文件删除程序的名称,默认值为 rm -f

CC C编译器的名称,默认值是cc

CPP C预编译器的名称,默认值是 $(CC) -E

CFLAGS C编译器的选项,无默认值

OBJS 生成的二进制文件或者目标文件,自己定义的

8) 第二个版本Makefile

引入变量

复制代码
EXE=main        # 保存可执行文件
OBJS=main.o add.o
CC=gcc            # Makefile中表示使用的编译器
CFLAGS=-c -g -Wall -o    # -g:调试    -Wall:显示警告


all:$(EXE)    
$(EXE):$(OBJS)
    $(CC) $(OBJS) -o $(EXE)
main.o:main.c
    $(CC) $(CFLAGS) main.o main.c
add.o:add.c
    $(CC) $(CFLAGS) add.o add.c

clean:
    $(RM) $(OBJS) $(EXE)

9) 引入自动变量和通配符

自动变量:

$@ 目标文件的完整名称

$< 第一个依赖文件

$^ 所有不重复的依赖文件。以空格分开

通配符:

*:通配所有的情况

%:是一种(字符串的)模式匹配,在Makefile中的作用是,有一个.o,就匹配一个同名的.c

%.o:%.c ------>从上一条规则中,获取到需要两个.o文件,fun.o和main.o,使用%进行模式匹配

10) 第三个版本Makefile

复制代码
EXE=main
OBJS=main.o add.o
CC=gcc
CFLAGS=-c -g -Wall -o

all:$(EXE)    
$(EXE):$(OBJS)
    $(CC) $^ -o $(EXE)
%.o:%.c
    $(CC) $(CFLAGS) $@ $<

clean:
    $(RM) $(OBJS) $(EXE)

11) 伪目标

在Makefile中,有些目标并不需要生成文件,也没有文件依赖,往往把这样的目标定义为伪目标,为了防止,因为存在和目标同名的文件而不能执行目标的情况发生。

直接将目标定义为伪目标.PHONY

.PHONY:目标

伪目标的作用:不会检查时间戳,直接执行规则中的内容

复制代码
EXE=main
OBJS=main.o add.o
CC=gcc
CFLAGS=-c -g -Wall -o

all:$(EXE)    
$(EXE):$(OBJS)
    $(CC) $^ -o $(EXE)
%.o:%.c
    $(CC) $(CFLAGS) $@ $<

.PHONY:clean
clean:
    $(RM) $(OBJS) $(EXE)

12) 引入函数

make中提供了内置函数

因为内置函数是帮助程序员查找文件信息的,所以要求在查找路径下,只要程序需要的.c文件,没有其他程序的.c文件

i) wildcard

功能:根据给定的条件,获取指定的文件名(找文件名的功能)

$(wildcard 指定字符串的格式)

$(wildcard *.c) ---> 找到当前路径下,所有.c文件的文件名

ii) patsubst

功能:模式匹配替换字符串

$(patsubst 源格式,目标格式,要替换的字符串)······

$(patsubst %.c,%.o,main.c add.c) ---> 获取到 main.c add.c字符串,根据模式匹配,得到 main.o add.o 字符串

每一个参数之间以逗号作为分隔,要替换的字符串之间以空格作为分隔

13) 第四版Makefile

复制代码
EXE=main
FILES=$(wildcard *.c)
OBJS=$(patsubst %.c,%.o, $(FILES))
CC=gcc
CFLAGS=-c -g -Wall -o

all:$(EXE)    
$(EXE):$(OBJS)
    $(CC) $^ -o $(EXE)
%.o:%.c
    $(CC) $(CFLAGS) $@ $<

.PHONY:clean
clean:
    $(RM) $(OBJS) $(EXE)

3. gdb调试工具

1) gdb调试的作用

gdb用于调试代码中逻辑错误,而非语法错误

2) gdb调试流程

  1. 生成可以使用gdb调试的只执行文件

gcc -g xxx.c ---> 生成的文件可以使用gdb调试

  1. 进入到gdb工具

gdb 可执行文件名 ---> 使用gdb工具开始调试可执行文件

r/run:运行代码

l/list:显示当前行下面的10代码

b/break 函数名/行号:添加断点

info b:查询断点信息

d/delete num:删除断点

p/print 变量名:查看变量的值

s/step:单步调试程序,如果是函数会进入

n/next:单步调试程序,如果是函数整体执行,不会进入

help:帮助

q:退出调试工具

i) 运行代码
ii) 查看代码
iii) 设置断点
  • 现在add函数处添加了断点,然后直接运行程序
  • 然后就停在了第5行(实际上是从第12行调用的),也就是在add中return的位置

● 执行 n 指令,继续走一步,来到第6行

● 再次执行 n 指令,也就是 num 赋值完毕,来到第13行

● 按下回车,执行上一次的指令,也就是 n,执行 pintf 语句打印信息,显示了下一行语句:return 0;

iV) 打印变量的值

V) 断点情况
  • 查看断点
  • 删除断点

3) 调试core文件

core何时生成:当程序出现重大错误时,会生成一个临时的镜像文件,保存程序状态(段错误)

由于系统的权限问题,不是每一次段错误都会生成core文件

ulimit -a 查看文件的权限

help ulimit

core file size如果为0该文件不会生成

ulimit -c unlimited 使用指令取消限制

如果使用了 ulimit -c unlimited 后,还不能在当前目录下生成core文件

在终端执行以下指令

sudo bash -c "echo core > /proc/sys/kernel/core_pattern"

需要同时gdb可执行文件和 core文件

gdb a.out core

程序会停在发生错误的一行

4) 调试正在运行的程序

需要在后台运行可执行文件

./a.out & ---> 会在终端回显进程号

gdb -p 进程号

补充

history-历史记录查询

直接执行history名显示HISTSIZE条历史记录。

history 10 -> 只显示10条历史记录

echo $HISTSIZE --> 在终端显示环境变量HISTSIZE的值

家目录下隐藏文件 .bash_history 保存历史记录,保存HISTFILESIZE 条

终端关闭,终端上执行的命令刷新到文件中。

环境变量的值可以被更改:

export HISTSIZE=20 临时修改,只有在本文件中打开这个终端有效

家目录下 文件 .bashrc 中修改就是永久修改,修改完成生效,从新打开的终端生效

相关推荐
xq514863几秒前
Linux系统下安装mongodb
linux·mongodb
柒七爱吃麻辣烫几秒前
在Linux中安装JDK并且搭建Java环境
java·linux·开发语言
孤寂大仙v37 分钟前
【Linux笔记】——进程信号的产生
linux·服务器·笔记
深海蜗牛1 小时前
Jenkins linux安装
linux·jenkins
愚戏师1 小时前
Linux复习笔记(三) 网络服务配置(web)
linux·运维·笔记
JANYI20182 小时前
嵌入式MCU和Linux开发哪个好?
linux·单片机·嵌入式硬件
熊大如如2 小时前
Java NIO 文件处理接口
java·linux·nio
晚秋大魔王2 小时前
OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——nettle库
linux·开源·harmonyos
农民小飞侠2 小时前
ubuntu 24.04 error: cannot uninstall blinker 1.7.0, record file not found. hint
linux·运维·ubuntu
某不知名網友2 小时前
Linux 软硬连接详解
linux·运维·服务器