C# C++ 笔记

时间:2024-10-01 20:50:22

第一阶段知识总结

lunix系统操作

1、基础命令

(1)cd

cd /[目录名] 打开指定文件目录

  • cd .. 返回上一级目录

  • cd - 返回并显示上一次目录

  • cd ~ 切换到当前用户的家目录

(2)pwd

pwd 查看当前所在目录路径

  • pwd -L 打印当前物理路径

(3)ls

ls 查看当前目录下的文件和目录

:部分系统文件和目录会用不同颜色显示

  • ls [目录名] : 显示目录下的文件(需要有查看目标目录的权限)

  • ls -a: 显示全部文件(包括文件名以“.”开头的隐藏文件)

  • ls -alh

  • ls -alh 查看时显示详细信息

    • d 指该内容为目录

    • x 指该内容为可执行

    • r 指可读

    • w 指可写

    • . 指当前目录

    • .. 指上一级目录

(4)touch

  • touch [文件名] 新建文件

(5)rm

  • rm [文件名] : 删除文件

    • r 强制删除

    • f 允许删除目录

(6)mv

  • mv [文件原名] [新的文件名] 重命名文件|目录

(7)mkdir

  • mkdir [目录名] 新建目录

    • mkdir -p [目录名]/[目录名]/…… 将不存在的目录全部创建

    • mkdir -v [目录名] 创建时打印目录信息

    • mkdir -m [目录名] 设置目录权限

 

(8)rmdir

  • rmdir [目录名] 删除目录(只能删除空目录)

    • rmdir /s [目录名] 删除非空目录

(9)cp

  • cp [被复制的文件名] [新的文件名] 复制文件|目录

    • cp -i [被复制的文件名] [新的文件名] 若新文件重名系统会询问是否覆盖,默认覆盖

    • cp -n [被复制的文件名] [新的文件名] 若新文件重名系统不会覆盖

    • cp -u [被复制的文件名] [新的文件名] 若新文件重名,只有被复制的文件的时间属性比重名文件新的时候才覆盖

    • cp -p [被复制的文件名] [新的文件名] 连同文件属性一起复制,默认只复制内容

(10)vi

  • vi [文件名] 编写文件

  • 按“i”键后可以开始修改内容,按之前只有delete键有效,按之后delete失效,backspace有效

编写时按“Esc”键退出编写,之后按“shift”+“:”输入命令  

  • w filename 保存

  • wq 保存并退出

  • q! 不保存强制退出

  • x 执行、保存并退出

(11)cat

  • cat [文件名] 读取文件

(12)echo

  • echo “[输入内容]” > [文件名] 清空文件并输入内容

(13)chmod

  • chmod 是 Unix 和 Linux 系统中的命令,用于更改文件或目录的权限。权限定义了哪些用户可以对文件或目录执行哪些操作。

  • chmod 命令的基本语法如下:

chmod [选项] 模式 文件名
  • 使用符号模式,你可以通过添加(+)、删除(-)或设置(=)权限来修改文件或目录的权限。权限符号可以是 r(读)、w(写)或 x(执行)。

(14)man

  • 在使用 man 命令时,可以在命令后面加上一个数字,以指定查看哪个手册页面节(man page section)。这个数字告诉系统应该搜索哪个手册页面的部分,因为一个命令或函数可能在不同的上下文中有多个手册页面。在 Unix 和类 Unix 系统中,手册页一般被分成以下几个节(sections):

  • General commands (通用命令):主要包含系统管理员和普通用户可以使用的命令。

    • 例如:man 1 printf 可以查看 printf 命令的手册页面。

  • System calls (系统调用):这些是操作系统提供的服务和功能的编程接口。

    • 例如:man 2 open 可以查看 open 系统调用的手册页面。

  • Library functions (库函数):包括标准 C 库和其他库的函数。

    • 例如:man 3 strlen 可以查看 strlen 函数的手册页面。

  • Special files (特殊文件):通常是设备文件和文件系统。

    • 例如:man 4 tty 可以查看关于 tty 特殊文件的手册页面。

  • File formats and conventions (文件格式和约定):包括配置文件和文件格式的描述。

    • 例如:man 5 passwd 可以查看 passwd 文件的手册页面。

  • Games (游戏):关于游戏的手册页面。

    • 例如:man 6 tetris 可以查看关于 tetris 游戏的手册页面。

  • Miscellaneous (杂项):其他的手册页面。

    • 例如:man 7 regex 可以查看关于正则表达式的手册页面。

  • System administration commands (系统管理命令):主要用于系统管理员的命令和工具。

    • 例如:man 8 iptables 可以查看关于 iptables 命令的手册页面。


2、C语言环境下的相关命令

(1)gcc

gcc [文件名] 编译C语言文件生成a.out执行文件 gcc -g [文件名] 编译C语言文件生成可调试的a.out执行文件

(2)./

./[文件名] 运行文件

:不需要空格

3、DOS通用注意点

(1)--help

  • “命令 --help”为该命令的帮助文档

(2)sudo

  • sudo [指令] 以管理员身份执行某一指令(需输入密码)

  • sudo passwd root 修改管理员用户密码且之后带sudo的命令不需要再输入密码

  • su 登陆管理员账户


4、makefile

(1)编译步骤及原理

  • 1、预编译

    • gcc -E [文件名.c] -> [预处理文件名.i],编译前的一些工作,生成.i文件

    • 作用:展开头文件;宏替换;去掉注释;条件编译。

    • .i文件是用于c语言的,.ii是用于c++语言

  • 2、编译

    • gcc -S [预处理文件名.i],生成对应的汇编文件,生成.s文件

    • 作用:将代码转成汇编代码。

  • 3、汇编

    • gcc -c [编译文件名.s],生成对应的二进制文件,生成.o文件

    • 作用:将汇编代码转成机器码

  • 4、链接

    • gcc [汇编文件名.o]

    • 作用: 将所有的.o文件链接成a.out文件。

(2)makefile核心目的

  • makefile文件指导系统如何编辑,节省大项目局部修改后的编译时间。局部修改后之后修改处需要重新编译。

  • 如果一个文件中有makefile和Makefile文件,在命令行输入make,优先执行makefile。如果执行大写的Makefile,需要加上-f:make -f Makefile。

(3)makefile运行逻辑

(4)makefile基本语法

#[目标]:[依赖1] [依赖2] …
#	[命令1] [命令2] …
#例如
main:main.o
	gcc main.o -o main
  • 目标: 一般是指要编译的目标,也可以是一个动作

  • 依赖: 指执行当前目标所要依赖的选项。包括其他目标,某个具体文件或库等,一个目标可以有多个依赖。

  • 命令:该目标下要执行的具体命令,可以没有,也可以有多条。

(5)makefile编译流程

main:main.o myAdd.o myMinus.o myMulti.o myDiv.o
	gcc main.o myAdd.o myMinus.o myMulti.o myDiv.o -o main

# -c 生成二进制文件 -o 指定输出的文件名
myAdd.o:myAdd.c
	gcc -c myAdd.c -o myAdd.o

myMinus.o:myMinus.c
	gcc -c myMinus.c -o myMinus.o

myMulti.o:myMulti.c
	gcc -c myMulti.c -o myMulti.o

myDiv.o:myDiv.c
	gcc -c myDiv.c -o myDiv.o

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

clean:
	@rm -rf *.o main

@表示执行但不输出这条命令

在终端输入 make 从上至下后执行文件命令 输入 make [目标] 仅执行对应命令


(6)makefile变量及文件精简过程

6.1 $@
# 针对上一张的图片,用$(CC)替换gcc命令
main:main.o myAdd.o myDiv.o myMinus.o myMulti.o
    $(CC) $^ -o $@
​
myAdd.o:myAdd.c
    $(CC) -c $^ -o $@
​
myMinus.o:myMinus.c
    $(CC) -c $^ -o $@
​
myMulti.o:myMulti.c
    $(CC) -c $^ -o $@
​
myDiv.o:myDiv.c
    $(CC) -c $^ -o $@
​
main.o:main.c
    $(CC) -c $^ -o $@
# 用$(RM)替换rm命令
clean:
    @$(RM) *.o main
6.4 自定义常量

# 变量自定义赋值
OBJS=main.o myAdd.o myDiv.o myMinus.o myMulti.o
TARGET=main
​
# 变量取值用$()
$(TARGET):$(OBJS)
    $(CC) $^ -o $@
​
myAdd.o:myAdd.c
    $(CC) -c $^ -o $@
​
myMinus.o:myMinus.c
    $(CC) -c $^ -o $@
​
myMulti.o:myMulti.c
    $(CC) -c $^ -o $@
​
myDiv.o:myDiv.c
    $(CC) -c $^ -o $@
​
main.o:main.c
    $(CC) -c $^ -o $@
​
clean:
    @$(RM) *.o $(TARGET)

  • 表示目标文件的完整名称。
  • # 针对上一小节的图片,用$@替换目标文件
    main:main.o myAdd.o myDiv.o myMinus.o myMulti.o
    	gcc main.o myAdd.o myDiv.o myMinus.o myMulti.o -o $@
    
    myAdd.o:myAdd.c
    	gcc -c myAdd.c -o $@
    
    myMinus.o:myMinus.c
    	gcc -c myMinus.c -o $@
    
    myMulti.o:myMulti.c
    	gcc -c myMulti.c -o $@
    
    myDiv.o:myDiv.c
    	gcc -c myDiv.c -o $@
    
    main.o:main.c
    	gcc -c main.c -o $@
    
    clean:
    	@rm -rf *.o main
    6.2 $^
  • 表示所有不重复的依赖文件
  • # 针对上一张的图片,用$^替换依赖文件
    main:main.o myAdd.o myDiv.o myMinus.o myMulti.o
    	gcc $^ -o $@
    
    myAdd.o:myAdd.c
    	gcc -c $^ -o $@
    
    myMinus.o:myMinus.c
    	gcc -c $^ -o $@
    
    myMulti.o:myMulti.c
    	gcc -c $^ -o $@
    
    myDiv.o:myDiv.c
    	gcc -c $^ -o $@
    
    main.o:main.c
    	gcc -c $^ -o $@
    
    clean:
    	@rm -rf *.o main
    6.3 系统常量
  • RM:删除

  • CC:C语言编译程序

  • [常量名]=[值]赋予自定义常量值

  • $() 取自定义变量的值

  • ​编辑

6.5 makefile伪目标
  • .PHONY: [目标] 当只想执行目标命令而不希望生成目标文件时使用

  • :后有个空格

OBJS=main.o myAdd.o myDiv.o myMinus.o myMulti.o
TARGET=main

$(TARGET):$(OBJS)
	$(CC) $^ -o $@

myAdd.o:myAdd.c
	$(CC) -c $^ -o $@

myMinus.o:myMinus.c
	$(CC) -c $^ -o $@

myMulti.o:myMulti.c
	$(CC) -c $^ -o $@

myDiv.o:myDiv.c
	$(CC) -c $^ -o $@

main.o:main.c
	$(CC) -c $^ -o $@

# 伪目标(伪文件),指执行命令,不生成文件
.PHONY: clean

clean:
	@$(RM) *.o main
6.6 模式匹配
  • %[目标]:%[依赖] 匹配目录下所有符合命令的文件,批量执行命令

 

OBJS=$(patssubst %.c, %.o, $(wildcard ./*.c))
TARGET=main

$(TARGET):$(OBJS)
    $(CC) $^ -o $@

# 模式匹配 %[目标]:%[依赖]
%.o:%.c
    $(CC) -c $^ -o $@
    
.PHONY: clean

clean:
    @$(RM) *.o main

6.7 总代码
OBJS=$(patsubst %.c, %.o, $(wildcard ./*.c))
# 变量定义赋值
TARGET=main
​
LDFLAGS=-L./src_so -L./src_a
LIBS=-lMyAdd -lMyDiv
​
SO_DIR=./src_so
A_DIR=./src_a
​
#变量取值用$()
$(TARGET):$(OBJS)
    $(CC) $^ $(LIBS) $(LDFLAGS)  -o $@
​
# 模式匹配: %目标:%依赖
%.o:%.c
    $(CC) -c $^ -o $@
​
all:
    make -C $(SO_DIR)
    make -C $(A_DIR)
​
# 伪目标/伪文件
.PHONY: clean
​
clean:
    $(RM) $(OBJS) $(TARGET)
    make -C $(SO_DIR) clean
    make -C $(A_DIR) clean
    
# wildcard : 匹配文件           (获取指定目录下所有的.c文件)
# patsubst : 模式匹配与替换    (指定目录下所有的.c文件替换成.o文件)
show:
    @echo $(wildcard ./*.c)
    @echo $(patsubst %.c, %.o, $(wildcard ./*.c))


(7)makefile动态库与静态库

   7.1 动态库

  • 作用:用于打包整合所有的 .c 源文件,同时使用者也无法通过动态库还原代码

  • :windows 中是 .dll 文件;linux 中是 .so 文件

    • 1、生成 .o 二进制文件

      • gcc -c -fPIC myAdd.c -o myadd.o

- 2、生成动态库
  - `gcc -shared myAdd.o -o libMyAdd.so`
- **可放在一起,**`gcc -shared -fPIC myAdd.c -o libMyAdd.so`
  • 使用动态库:

  • gcc -ImyAdd -L./src.so -o main

    • libMyAdd.so由 lib + 函数名 + .so 组成

    • myAdd是函数名

    • src.so是动态库文件目录

  • 提供给客户的只有libMyAdd.so和MyAdd.h文件

:上图myadda没有大写导致报错

  • 3、复制到没有 .c 源文件的文件夹下

  • 4、生成main.o文件

    • gcc *.c -lMyAdd -L./src_so -o main

  • 5、运行main.o文件

动态库内部makefile

  • 运行前先执行内部makefile生成动态库

  • 然后再外部调用动态库

OBJS=$(patsubst %.c, %.o, $(wildcard ./*.c))
TARGET=libMyAdd.so

PATHS=/usr/lib/

$(TARGET):$(OBJS)
    $(CC) -shared -fPIC $^ -o $@
    cp $(TARGET) $(PATHS)

%.o:%.c
    $(CC) -c $^ -o $@

clean:
    $(RM) $(OBJS) $(TARGET)

show:
    @echo $(RM)
    @echo $(OBJS)

7.2 静态库
  • 以下是使用静态库的基本步骤:

  • 创建静态库

  • 通常,你会有一系列的源文件(.c.cpp 等),这些源文件会被编译成目标文件。然后,这些目标文件会被打包成一个静态库文件。在Linux中,这通常是一个以 .a 为扩展名的文件。

  • 例如,假设你有两个源文件 file1.cfile2.c,你可以这样创建静态库:

gcc -c file1.c -o file1.o  
gcc -c file2.c -o file2.o  
ar rcs libmystatic.a file1.o file2.o// rcs 换成 -r 也行
  • 这里,ar 命令用于创建静态库,rcs 是其选项,表示替换现有的库文件(r),创建库文件(c),并且指定库文件的索引(s)。

  • 使用静态库

    • 当你有一个或多个源文件需要使用静态库中的代码时,你需要在编译和链接阶段指定这个静态库。链接器会将静态库中的目标文件与你的源文件编译出的目标文件合并,生成最终的可执行文件。

  • 例如,假设你有一个源文件 main.c,它调用了静态库 libmystatic.a 中的函数。你可以这样编译和链接:

gcc main.c -L. -lmystatic -o myprogram
  • 这里,-L. 告诉链接器在当前目录(. 表示当前目录)中查找库文件,-lmystatic 指定链接到 libmystatic.a 库(注意,链接时不需要库文件的前缀 lib 和扩展名 .a)。

  • 静态库内部makefile

    • 运行前先执行内部makefile生成动态库

    • 然后再外部调用动态库

OBJS=$(patsubst %.c, %.o, $(wildcard ./*.c))
TARGET=libMyDiv.a
​
$(TARGET):$(OBJS)
    $(AR) -r $(TARGET) $^
​
# 模式匹配
%.o:%.c
    $(CC) -c $^ -o $@
​
clean:
    $(RM) $(OBJS) $(TARGET)
7.3 动态库与静态库外部makefile
  • 采用make -C 命令就可以执行库里面内部的makefile,可以不用先在内部执行生成库文件。

  • 先输入make all命令执行all中的代码生成库

  • 再输入make

  • 或者直接一条命令make all && make。

OBJS=$(patsubst %.c, %.o, $(wildcard ./*.c))
# 变量定义赋值
TARGET=main
​
LDFLAGS=-L./src_so -L./src_a
LIBS=-lMyAdd -lMyDiv
​
SO_DIR=./src_so
A_DIR=./src_a
​
#变量取值用$()
$(TARGET):$(OBJS)
    $(CC) -g $^ -o $@
​
# 模式匹配: %目标:%依赖
%.o:%.c
    $(CC) -g -c $^ -o $@
​
all:
    make -C $(SO_DIR)
    make -C $(A_DIR)
​
# 伪目标/伪文件
.PHONY: clean
​
clean:
    $(RM) $(OBJS) $(TARGET)
    
# wildcard : 匹配文件           (获取指定目录下所有的.c文件)
# patsubst : 模式匹配与替换    (指定目录下所有的.c文件替换成.o文件)
show:
    @echo $(wildcard ./*.c)
    @echo $(patsubst %.c, %.o, $(wildcard ./*.c))
7.4 区别
  • 链接方式

    • 静态库在编译时被链接到程序中,而动态库在运行时被加载到内存中。

    • 使用静态库的程序在编译时会将库的内容直接合并到最终的可执行文件中,而使用动态库的程序则会在运行时根据需要从库中加载所需的代码。

  • 更新和维护

    • 静态库,如果需要更新库中的代码,必须重新编译并重新链接所有依赖于该库的程序

    • 动态库可以独立更新,而不需要重新编译程序,这使得库的维护更加方便。动态库更适合多文件场合。

(8)Makefile的常用选项

  • -f file指定Makefile文件。默认情况下,make会在当前目录中查找名为GNUmakefile、makefile或Makefile的文件作为输入。使用-f选项,你可以指定其他名称的文件作为Makefile。

  • -v显示make工具的版本号

  • -n只输出命令,但不执行。这个选项通常用于测试Makefile,查看make会执行哪些命令,而不真正执行它们。

  • -s只执行命令,但不显示具体命令。这跟makefile中的(@+命令行)符号作用一样。这个选项在需要执行命令但不需要看到详细输出时很有用。

  • -w:显示执行前和执行后的路径。

  • -C dir:指定Makefile所在的目录。如果Makefile不在当前目录中,可以使用这个选项来指定Makefile的目录。

(9)makefile中shell的使用

  • 所有在命令行输入的命令都是shell命令

9.1直接执行shell命令
  • 在Makefile的规则中,直接写shell命令,并在命令前加上$(shell ...)或者反引号...来执行。例如:

FILES = text.txt
​
A=$(shell ls ./)
B=$(shell pwd)
C=$(shell if [ ! -f $(FILE) ]; then touch $(FILE); fi;)
  
show:  
    @echo $(A)
    @echo $(B)
    @echo $(C)
  • 在这个例子中,$(shell ls ./)会执行ls ./命令,并将结果文件显示输出以及创建test.txt文件。然后,在show规则的命令部分,我们使用@echo来打印这些文件名。

9.2 shell 中 -f 与 -d 指令
  • 在Unix和Linux shell中,-f-d 是用于测试文件类型的条件表达式(也称为测试运算符)。这些通常与 if 语句或 while 循环等控制结构一起使用,以根据文件的存在和类型来执行不同的操作。

  • -f 测试

    • -f 测试用于检查指定的路径是否为一个常规文件(即不是目录、设备文件、符号链接等)

  • 示例:

if [ -f /path/to/file ]; then  
    echo "The path is a regular file."  
else  
    echo "The path is not a regular file."  
fi
  • -d 测试

    • -d 测试用于检查指定的路径是否为一个目录

  • 示例:

if [ -d /path/to/directory ]; then  
    echo "The path is a directory."  
else  
    echo "The path is not a directory."  
fi
  • 在上面的示例中,如果指定的路径是一个常规文件,那么 -f 测试将返回真(true),并且会执行 then 部分的代码。如果指定的路径是一个目录,那么 -d 测试将返回真,并执行相应的代码。

(10) makefile条件判断

  • Makefile支持使用条件语句来根据某些条件执行不同的shell命令。这通常使用ifeqifneqifdefifndef等指令来实现。例如:

OS = $(shell uname -s)  
  
ifeq ($(OS), Linux)  
    CC = gcc  
else  
    CC = clang  
endif  
  
all:  
    $(CC) -o myprogram myprogram.c
  • 在这个例子中,我们首先使用$(shell uname -s)来获取操作系统类型,并将其赋值给变量OS。然后,我们使用ifeq来判断OS的值,如果是Linux,则使用gcc作为编译器;否则,使用clang。最后,在all规则的命令部分,我们使用选定的编译器来编译程序。

(11) makefile命令行参数

(12)Makefile中install

  • 源码安装,不通过apt-get install安装。

12.1 功能作用
  • 创建目录,将可执行文件拷贝到指定目录(安装目录)

  • 加全局可执行的路径

  • 加全局的启停脚本

  • cp一行将生成的目标拷贝至该文件路径中

  • sudo一行是软链接

linux设备启动时会将这些文件启动:  

12.2 主要目的
  • Makefile中的install目标的主要目的是提供一个标准化的方式来安装编译后的程序、库、文档以及其他相关文件到用户的系统上。当开发者构建了一个软件项目后,他们通常希望用户能够轻松地将其安装到他们的系统上,并使其能够正常运行。install目标就是用来完成这一任务的。

  • 具体来说,install目标通常会执行以下操作:

    • 复制文件:将编译后的可执行文件、库文件、头文件等复制到指定的安装目录。这些目录通常是系统级的目录,如/usr/local/bin用于存放可执行文件/usr/local/lib用于存放库文件等。

    • 设置权限:确保复制的文件具有正确的权限,以便用户可以正常访问和使用它们。

    • 创建目录:如果需要,install目标还可以创建必要的目录结构,以便将文件放置到正确的位置。

    • 安装文档:除了程序本身,install目标还可能包括安装相关的文档、手册页等。

    • 执行其他安装步骤:根据项目的具体需求,install目标还可以包含其他必要的安装步骤,如创建配置文件、设置环境变量等。

  • 可以将文件做成全局的,比如自实现mycp命令,做成全局后,可以在任意位置使用mycp命令

12.3 软链接与硬链接
  • Linux软链接(Symbolic Link)是一种特殊的文件类型,它可以创建一个指向另一个文件或目录的链接。软链接不是实际的文件或目录,而是一个指向实际文件或目录的指针。当我们访问软链接时,实际上是访问被链接的文件或目录

  • 软链接在Linux系统中非常常见,并且被广泛应用于各种场景。其主要特点和应用包括:

    • 快速访问文件:当某个文件位于深层次的目录中时,可以通过创建软链接到其他位置来方便快速访问。

    • 管理共享库:在Linux系统中,软链接常用于管理共享库。通过创建共享库的软链接,可以实现不同版本之间的切换和共存。

    • 创建快捷方式:软链接可以被视为Linux系统中的快捷方式,它允许用户为常用文件或目录创建一个指向它的链接,从而方便快速访问。

  • 创建软链接的常用方法是使用 ln 命令,具体语法为 “ln -s target source” ,其中 “target” 表示目标文件(夹),即被指向的文件(夹),而 “source” 表示当前目录的软连接名,即源文件(夹)。

  • 通过软链接指令生成的软链接文件mycp,生成的文件属性为软链接

  • 实体没有了,那只是快捷方式,因此在使用mycp命令会显示没有该文件

12.4 ln -sv命令
  • ln -sv 命令在 Linux 中用于创建符号链接(软链接)。这里的 -s 表示创建软链接,而 -v 表示详细模式(verbose),即会显示创建的链接的详细信息。

  • 具体解释如下:

    • ln: 这是链接命令,用于创建链接。

    • -s: 表示创建软链接(符号链接)。如果不加 -s,那么默认创建的是硬链接。

    • -v: 详细模式,会显示命令执行过程中的信息,例如正在创建哪个链接

  • 例如,假设你有一个文件叫做 original.txt,并且你想要为它创建一个名为 link.txt 的软链接,你可以使用以下命令:

    • ln -sv original.txt link.txt
  • 执行这条命令后,你会看到类似以下的输出:

    • 'link.txt' -> 'original.txt'
    • 这意味着 link.txt 现在是一个指向 original.txt 的软链接。之后,如果你通过 link.txt 访问文件,实际上你会访问到 original.txt

12.5 软链接makefile操作
OBJS=$(patsubst %.cpp, %.o, $(wildcard ./*.cpp))
# 变量定义赋值
TARGET=mycp
​
LDFLAGS=-L./src_so -L./src_a
LIBS=-lMyAdd -lMyDiv
​
SO_DIR=./src_so
A_DIR=./src_a
​
PATHS=/tmp/demoMain/
BIN=/usr/local/bin/
​
#变量取值用$()
$(TARGET):$(OBJS)
    $(CXX) $^ -o $@
​
# 模式匹配: %目标:%依赖
%.o:%.cpp
    @$(CXX) -c $^ -o $@
​
all:
    make -C $(SO_DIR)
    make -C $(A_DIR)
​
install:$(TARGET)
    @if [ -d $(PATHS) ];                    \
        then echo $(PATHS) exist;       \
    else                                \
        mkdir $(PATHS);                 \
        cp $(TARGET) $(PATHS);          \
        sudo ln -sv $(PATHS)$(TARGET) $(BIN);   \
    fi
​
# 伪目标/伪文件
.PHONY: clean
​
clean:
    $(RM) $(OBJS) $(TARGET)
    make -C $(SO_DIR) clean
    make -C $(A_DIR) clean
    
# wildcard : 匹配文件           (获取指定目录下所有的.c文件)
# patsubst : 模式匹配与替换    (指定目录下所有的.c文件替换成.o文件)
show:
    @echo $(wildcard ./*.cpp)
    @echo $(patsubst %.cpp, %.o, $(wildcard ./*.cpp))
12.6 软硬链接的区别

软链接与硬链接(Hard Link)有所不同。硬链接是直接指向文件的物理位置。而软链接则是指向文件名的路径,如果原始文件被移动、重命名或删除,软链接将会失效(即所谓的“死链接”)。此外,软链接可以跨越不同的文件系统,而硬链接只能在同一文件系统内使用。

5、Gdb调试

(1)基本调试步骤

  • 1、gdb [文件名] 调试文件,需事先 gcc -g .c文件

  • 2、gdb a.out

  • 3、run

  • 4、bt

  • 若没有-g就直接编译了,使用gdb就会出现以下信息:没有bug信息

 :若报错 ‘gdb’ not found,输入指令 apt-get install gdb 一直回车即可

(2)常用gdb调试命令

常用指令 全称 指令效果
b [代码行数] break 在第几行添加断点,如果在指定文件打断点,则b 指定文件 : 行号
info b info break 显示所有断点的信息,一般按自然数排序
del [断点编号] delete 删除断点,示例:del 56 删除编号为56的断点
dis disable 禁用断点
ena enable 启用断点
p [变量] print 查询值,包括以下形式,vary,&vary,*ptr,buffer[0]
run / 执行程序,直到结束或断点
n next 执行下一条语句,会越过函数
s step 执行下一条语句,会进入函数
c continue 继续执行程序,直到遇到下一个断点
call / 直接调用函数并查看其返回值
q quit 退出 gdb 当前调试
bt backtrace 查看函数的栈调用
f frame 到指定的栈帧,配合bt使用显示当前选中的堆栈帧的详细信息包括帧编号、地址、函数名以及源代码位置
where / 显示当前线程的调用堆栈跟踪信息
ptype / 查看变量类型
thread / 切换指定线程
set br br / 将信息按标准格式输出,这样信息就显示不乱
set args / 设置程序启动命令行参数
show args / 查看设置的命令行参数
l list 显示源代码。
watch / 监视某一变量或内存地址的值是否发生变化
u until 运行程序直到退出当前循环。 快速跳过循环的剩余迭代,以便更快地到达循环之后的代码。
fi finish 执行完当前函数的剩余部分,并停止在调用该函数的地方。 示例:finish 执行完当前函数的剩余部分。
return / 结束当前调用函数并返回指定值,到上一层函数调用处
display / 每次程序停止时自动打印变量的值。 示例:display name 每次程序停止时自动打印 name 的值。
undisplay / 取消之前用 display 命令设置的自动打印。
j jump 使程序跳转到指定的位置继续执行。
dir / 重定向源码文件的位置
source / 读取并执行(加载)一个包含GDB命令的脚本文件
set / set variable=newvalue,修改变量的值

(3)调试详解

3.1 call命令
  • call func:显示地址信息,因为是函数名,指向函数地址。

  • call func( ):无参调用,显示函数的返回值

  • call add(100,200 ):有参调用,返回300。

3.2 gdb attach
  • attach命令用于将一个正在运行的进程附加到GDB调试器中,以便你可以对该进程进行调试。这对于调试那些已经启动并且你希望动态地分析其行为的进程非常有用。

  • 使用attach命令的基本语法是:

    • gdb attach <进程ID>
    • 这里的<进程ID>是你想要附加的进程的ID。你可以通过ps - ef命令或者其他系统工具来获取进程ID。

    • 一旦进程被附加到GDB,你就可以使用GDB提供的各种命令来调试该进程了,比如设置断点、单步执行、查看变量值等。

    • 需要注意的是,当你附加到一个进程时,该进程会暂时被暂停执行直到你在GDB中继续执行它。此外,如果你尝试附加到一个没有调试信息的进程(比如没有编译为带调试信息的版本),你可能无法查看所有的源代码和变量信息。

3.3 core文件
  • Core文件是Unix或Linux系统下程序崩溃时生成的内存映像文件,主要用于对程序进行调试。当程序出现内存越界、bug或者由于操作系统或硬件的保护机制导致程序异常终止时,或者段错误时,操作系统会中止进程并将当前内存状态导出到core文件中。这个文件记录了程序崩溃时的详细状态描述,程序员可以通过分析core文件来找出问题所在。

  • 在Linux系统中,你可以使用GDB(GNU调试器)来调试core文件。GDB提供了一系列命令:

    • backtrace(或简写为bt)来查看运行栈

    • frame(或简写为f)来切换到特定的栈帧

    • info来查看栈帧中的变量和参数信息

    • print来打印变量的值等

    • 从而定位和解决问题。

  • 下图中,将出现的段错误导入至core文件中。随后执行gdb + 文件 + core文件

3.4 gdb中的堆栈帧
  • 在GDB中,堆栈(stack)是用于存储函数调用时局部变量、返回地址等信息的一段连续的内存空间。当我们在GDB中查看堆栈信息时,通常会看到一系列的堆栈帧(stack frames)每一个堆栈帧对应一个函数调用。这些堆栈帧按照函数调用的顺序排列,最新的调用在最顶部,而较早的调用则位于下方。

  • 每个堆栈帧都包含函数名、参数和返回地址等信息

3.5 backtrace 与 frame 命令
3.6 print 命令 和 ptype 命令

a) p 输出

b) ptype
  • ptype 是一个在 GDB(GNU 调试器)中使用的命令,用于查看变量的类型ptype 命令允许你在调试过程中查看某个变量的数据类型

  • 使用 ptype 命令的基本语法如下:

    • ptype 变量名
  • 例如,如果你有一个名为 my_variable 的变量,并想要查看它的类型,你可以在 GDB 中输入:

    • ptype my_variable
  • GDB 会返回该变量的类型信息。

    • 此外,ptype 命令还支持一些可选参数,用于调整输出格式或提供额外的信息。例如:

      • /r:以原始数据的方式显示,不会替换一些 typedef 定义。

      • /m:查看类时,不显示类的方法,只显示类的成员变量。

      • /M:与 /m 相反,显示类的方法(默认选项)。

      • /t:不打印类中的 typedef 数据。

      • /o:打印结构体字段的偏移量和大小。

  • 这些选项可以通过在 ptype 命令后附加相应的参数来使用,以便根据需要调整输出。

  • 需要注意的是,ptype 命令仅在 GDB 的上下文中有效,并且你需要在已经启动并加载了相应程序的 GDB 会话中使用它。此外,ptype 命令只能查看当前作用域内可见的变量的类型。如果变量不在当前作用域内,你可能需要切换到包含该变量的作用域(例如,通过进入函数或切换到特定的堆栈帧)才能使用 ptype 命令查看其类型。

3.7 info 命令 和 thread 命令

3.8 jump命令
基本命令
  • jump命令的基本用法如下:

    • jump <location>
  • 这里的<location>可以是程序的行号、函数的地址,或者是源代码文件名和行号的组合。例如:

    • 跳转到第100行:jump 100 ---->>>>这种适合代码少的跳转,当代码繁多时采用修改寄存器的方法

    • 跳转到函数my_function的开始处:jump my_function

    • 跳转到文件myfile.c的第20行:jump myfile.c:20

  • 使用jump命令时,需要注意以下几点:

    • 栈不改变jump命令不会改变当前的程序栈中的内容。这意味着,如果从一个函数内部跳转到另一个函数,当返回到原函数时,可能会因为栈不匹配而导致错误。因此,最好在同一函数内部使用jump命令。

    • 后续执行:如果jump跳转到的位置后面没有断点,GDB会在执行完跳转处的代码后继续执行。如果需要暂停执行,可以在跳转目标处设置断点。

    • 小心使用jump命令可以强制改变程序的执行流程,这可能导致未定义的行为或程序崩溃。因此,在使用jump命令时,需要谨慎并确保了解跳转的后果。

    • tbreak配合使用:由于jump命令执行后会立即继续执行,所以经常与tbreak命令配合使用,在跳转的目标位置设置一个临时断点,以便调试者可以检查程序的状态。

修改寄存器(pc)
  • $pc是一个特殊的变量,它代表程序计数器(Program Counter)的当前值。程序计数器是CPU中的一个寄存器,它存储了CPU将要执行的下一条指令的地址。换句话说,它指向了CPU当前正在执行的代码位置

  • 修改$pc的值

    你可以通过set命令来修改$pc的值,从而改变程序执行的流程。但请注意,这样做非常危险,除非你确切知道你要跳转到的地址,并且该地址包含有效的指令。

  • 情景:当需要停留在59行时,却多打了一步,在60行。

    • 获得当前行的汇编地址代码

    • 修改寄存器

  • 目的:获取的59行的汇编地址,通过修改该行,让其在59行继续运行。

3.9 display命令

3.10 source命令
  • 在Linux中,source命令用于在当前shell环境中执行指定的shell脚本文件,而不是创建一个新的子shell来执行。这意味着脚本中定义的任何变量或函数都会在执行完脚本后保留在当前shell环境中。

  • 使用source命令的基本语法如下:

    • source /path/to/script.sh
  • 或者,你可以使用.(点)作为source命令的简写:

    • . /path/to/script.sh
  • source命令通常用于以下场景:

    • 更新环境变量(加载配置文件)(主要用处):如果你修改了某个环境变量文件(如~/.bashrc~/.bash_profile),并且希望这些更改立即在当前shell会话中生效,而不是在打开新的shell会话时才生效,你可以使用source命令来加载这些更改。

      • source ~/.bashrc
    • 执行初始化脚本:某些应用程序或工具可能需要运行初始化脚本以设置环境或执行其他一次性任务。使用source可以确保这些更改在当前shell中生效。

    • 在当前shell中运行函数和别名:如果你在脚本中定义了一些函数或别名,并希望它们在当前shell中可用,那么使用source来执行脚本是合适的。

  • 使用source命令而不是直接运行脚本(如./script.sh)的主要区别在于环境变量的持久性。直接运行脚本会在子shell中执行,任何在脚本中定义的变量或更改的环境变量都不会影响父shell(即你当前所在的shell)。而使用source命令,脚本中的变量和更改会直接影响当前shell。

3.11 save保存断点文件
  • 提高调试的速度

其中,.txt中的文件的条件也可自行修改  

查看文件内容  

加载文件  

3.12 watch命令
  • watch 命令在多种场景下都非常有用,比如:

    • 监视日志文件的变化,以便实时查看新添加的行或错误消息。

    • 监视系统资源使用情况,如 CPU、内存或磁盘空间。

    • 跟踪进程状态或性能数据。

  • 请注意,watch 命令会不断地执行指定的命令,这可能会对系统性能产生一定的影响,特别是在执行复杂或资源密集型的命令时。因此,在使用 watch 命令时,请确保你了解其工作原理,并谨慎选择监视的命令和刷新间隔。

 

3.13 diff命令
  • diff 命令是 Linux 和类 Unix 系统中用于比较两个文件或目录的差异的实用工具。通过比较,diff 命令可以显示两个文件或目录之间的不同之处,以便用户可以了解它们之间的差异。

  • diff 命令的基本语法如下:

    • diff [选项] 文件1 文件2
  • 其中,选项 是可选的,用于调整 diff 命令的输出格式和行为,而 文件1文件2 是你想要比较的两个文件。

  • 以下是一些常用的 diff 命令选项:

    • -c--context:以上下文格式显示差异。这会显示文件之间的不同行,以及它们前后的几行上下文,有助于用户理解差异的具体位置。

    • -u--unified:以统一的格式显示差异。这种格式与上下文格式类似,但显示方式稍有不同,通常用于补丁文件(patch files)。

    • -r--recursive:递归比较目录及其子目录下的文件。当比较目录时,diff 会递归地遍历目录树,并比较其中的文件。

    • -i--ignore-case:忽略大小写的差异。在比较时,不考虑字母的大小写。

    • -w--ignore-all-space:忽略所有空格的差异。这包括空格、制表符等空白字符。

    • -B--ignore-blank-lines:忽略空白行的差异。即不将只包含空白字符的行视为差异。

  • diff 命令的输出结果通常以 <> 符号来表示差异。< 表示文件1中的内容,> 表示文件2中的内容。具体的差异行会以 -+ 符号开头,表示删除或添加的行。

  • 除了上述常用选项外,diff 命令还有其他一些选项,如 -a(将二进制文件视为文本文件进行比较)、-l(将结果交由 pr 程序来分页)等。

  • 图中的.bak文件是备份文件。

3.14 多线程gdb调试步骤
  • ps -ef | grep main:查看线程号

  • gdb attach + 线程号

  • info thread :查看线程数量

  • thread 2 :表示查看第2 个线程

  • b + 文件名:行数:打断点,随后直接run就行

  • info br:查看断点信息

  • p + 参数:表示想查看具体参数的细节

  • set pr pr:设置打印好看

  • thread apply all bt:所有线程都打印栈帧

  • f 3:进入第三个线程的栈帧

(4)wget命令(redis安装)

4.1 wget命令
  • get是一个常用的命令行工具,用于从网络上下载文件。它支持多种协议,包括HTTP、HTTPS、FTP等,并提供了丰富的选项和参数以满足不同的下载需求。

  • wget命令的基本格式如下:

    • wget [选项] [参数]
    • 其中,选项用于指定wget的行为,参数则用于指定要下载的文件或URL地址。

  • 以下是一些常用的wget命令示例:

    • 下载单个文件:例如在Downloads - Redis下载redis压缩包文件

      • wget https://download.redis.io/redis-stable.tar.gz

      • 这个命令将从https://download.redis.io/redis-stable.tar.gz下载文件到当前目录。

      • 然后开始使用tar zxvf redis-stable.tar.gz 进行解压

      • 上面就是源码安装的过程

    • 支持断点续传: wget -c http://example.com/largefile.iso 使用-c选项,如果下载过程中连接中断,wget可以从上次停止的地方继续下载。

    • 后台下载文件: wget -b http://example.com/background.mp3 使用-b选项,wget会在后台执行下载操作,即使关闭终端也不会影响下载进程。

    • 限速下载文件: wget --limit-rate=300k http://example.com/slowdownload.zip 使用--limit-rate选项,可以限制下载速度,这里限制为300k。

    • 下载到指定目录: wget -P /path/to/directory http://example.com/file.pdf 使用-P选项,可以指定下载文件的保存目录。

4.2 redis(了解即可)
  • Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。以下是Redis的一些主要特性和应用:

    • 速度快:由于Redis所有数据是存放在内存中的,并且其源代码采用C语言编写,距离底层操作系统更近,执行速度相对更快。此外,Redis使用单线程架构,避免了多线程可能产生的竞争开销。

    • 基于K_V的数据结构:Redis提供了丰富的数据类型,包括字符串、哈希、列表、集合、有序集合等,并且每种数据类型都提供了丰富的操作命令。

    • 功能相对丰富:Redis对外提供了键过期的功能,可以用来实现缓存。它还提供了发布订阅功能,可以用来实现简单的消息系统,解耦业务代码。此外,Redis还支持Lua脚本,提供了简单的事务功能(不能rollback),以及Pipeline功能,客户端能够将一批命令一次性传输到Server端,减少了网络开销。

    • 扩展模块:Redis提供了一个模块叫做RedisJSON,它允许你在Redis中存储和查询JSON文档,支持多种查询语法和索引类型。这使得Redis能够模拟MongoDB等文档数据库的功能,同时由于其高性能和低延迟,你可以获得更快的响应速度和更好的用户体验。