第一章
1、静态库制作和使用过程
-
先得到目标文件
g++ -c *.cpp
-
ar创建库文件
//xxx为库的名字 ar rcs libxxx.a ./*.o
-
生成可执行文件
//-o 后面为生成的可执行文件名 //-I 后面为头文件所在位置 //-L 后面为静态库所在位置 //-l 后面为静态库名称 g++ main.c -o main -I ./include/ -L ./lib/ -l xxx
2、动态库制作和使用过程
-
先得到目标文件(与位置无关)
g++ -c -fpic *.cpp
-
创建动态库文件
g++ -shared ./*.o -o libsss.so
-
生成可执行文件
//-o 后面为生成的可执行文件名 //-I 后面为头文件所在位置 //-L 后面为动态库所在位置 //-l 后面为动态库名称 g++ main.c -o main -I ./include/ -L ./lib/ -l xxx
要配置下动态库的环境变量才可以运行,在home文件夹下的.sshrc中加上下面这行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:(动态库所在位置)
3、静态库和动态库对比
-
静态库
-
优点
-
静态库打包到应用程序速度快
-
发布程序无需提供静态库,移植方便
-
-
缺点
-
消耗系统资源,浪费内存
-
更新、部署、发布麻烦
-
-
-
动态库
-
优点
-
可以实现进程间资源共享(共享库)
-
更新、部署、发布简单
-
可以控制何时加载动态库
-
-
缺点
-
加载速度比静态库慢
-
发布程序时需要提供依赖的动态库
-
-
4、Makefile
-
文件命名
makefile或者Makefile
-
规则
-
可以有一个规则或多个规则
目标... :依赖... 命令(shell 命令) ...
目标:最终要生成的文件(伪目标除外)
依赖:生成目标所需要的文件或目标
命令:通过执行命令对依赖操作生成目标(命令前必须Tab缩进)
-
其他规则都是为第一条规则服务
-
命令在执行前,需要先检查规则中的依赖是否存在
-
若存在,执行命令
-
若不存在,向下检查其他规则,检查有没有一个规则是用来生成这个依赖,若找到,则执行该规则中的命令
-
-
检查更新,在执行规则中的命令时,会比较目标进而依赖文件的时间
-
若依赖的时间比目标的时间晚,则要重新生成目标
-
若依赖时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行
-
-
-
变量
-
自定义变量
- 变量名 = 变量值 var = hello
-
预定义变量
-
AR:归档维护程序的名称,默认为ar
-
CC:C编译器的名称,默认为cc
-
CXX:C++编译器的名称,默认为g++
-
$@:目标的完整名称
-
$<:第一个依赖文件的名称
-
$^:所有的依赖文件
-
-
获取变量的值
- $(变量名)
-
-
函数
-
$(wildcard PATTERN…)
-
功能:获取指定目录下指定类型的文件列表
-
参数:PATTERN指的时某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般用空格间隔
-
返回:得到的若干个文件的文件列表,文件名之间使用空格间隔
-
示例:
-
$(wildcard .c ./sub/.c)
返回格式:a.c b.c c.c d.c e.c…
-
-
-
$(patsubst ,,
) -
功能:查找
中的单词(单词以空格、Tab、回车、换行分隔)是否符合模式,若匹配,则以替换。 -
可以包括通配符%,表示任意长度的字符串。若中也包含%,可以用\来转义。
-
返回:函数返回被替换过后的字符串
-
示例:
-
$(patsubst %.c, %.o, x.c bar.c)
返回格式:x.o bar.o
-
-
-
-
最终定义的makefile
#定义变量 src= $(wildcard ./*.cpp) obj=$(patsubst %.cpp, %.o, $(src)) target=main $(target):$(obj) $(CXX) $(obj) -o $(target) %.o:%.cpp $(CXX) -c $< -o $@ .PHONY:clean //伪目标 不会和外面的clean产生冲突 clean: rm $(obj) -f
5、GDB调试
-
功能
-
可以启动程序,按照要求运行程序
-
可以打断点
-
可以检查断点处的状态
-
可以修改BUG
-
-
注意点
-
通常会关闭优化选项 -o
-
打开调试选项 -g
-
在尽量不影响程序行为的情况下打开所有warning
-
-
具体操作
//-g是在可执行文件中加入源码信息,比如可执行文件中第几条机器指令对应源代码第几行 //但并不是把整个源文件嵌入到可执行文件中去,所以必须保证gdb能找到源文件 g++ -g -Wall program.cpp -o program
-
常见命令
查看信息
-
启动和退出
gdb 可执行程序 quit
-
给程序设置参数/获取设置参数
set args 10 20 show args
-
GDB使用帮助
help
-
查看当前文件代码
list/l //(从默认位置开始) list/l //行号(从指定的行显示) list/l //函数名(从指定的函数显示)
-
查看非当前文件代码
list/l //文件名:行号 list/l //文件名:函数名
-
设置显示的行数
show list/listsize set list/listsize //行数
断点相关
-
设置断点
b/break 行号 b/break 函数名 b/break 文件名:行号 b/break 文件名:函数
-
设置断点无效
dis/disable 断点编号
-
设置断点生效
ena/enable 断点编号
-
设置条件断点(一般用在循环的位置)
b/break 10 if i==5
-
查看断点
i/info b/break
-
删除断点
d/del/delete 断点编号
调试命令
-
运行GDB程序
start (程序停在第一行) run (遇到断点才停)
-
继续运行,到下一个断点停
c/continue
-
向下执行一行代码(不会进入函数体)
n/next
-
变量操作
p/print 变量名 (打印变量值) ptype 变量名 (打印变量类型)
-
向下单步调试(遇到函数进入函数体)
s/step finish (跳出函数体)
-
自动变量操作
display num (自动打印指定变量的值) i/info display (显示自动变量信息) undisplay 编号 (取消自动变量显示)
-
其他操作
set var 变量名=变量值 until (跳出循环)
-
6、文件IO
-
标准C库IO函数
-
标准C库IO和linux系统IO的关系
-
虚拟地址空间
-
文件描述符
-
open函数和close函数相关
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<stdio.h> // 打开一个已经存在的文件 /* int open(const char *pathname,int flags); 参数设置: - pathname:要打开的文件路径 - flags :对文件的操作权限设置还有其他设置 O_RDONLY, O_WRONLY, O_RDWR 三个设置互斥 返回值 : 返回一个新的文件描述符,若调用失败,返回-1 errno:属于Linux系统函数库,是一个全局变量,记录最近的错误号 perror:可以输出错误信息,如下: #include<iostream> void perror(const char *s); 作用:用户描述,比如hello,最终输出的内容是 hello.xxx (实际的错误描述) */ // /* int open(const char* pathname,int flags,mode_t mode); 参数设置: - pathname: 要打开的文件路径 - flags :对文件的操作权限设置还有其他设置 必选:O_RDONLY, O_WRONLY, O_RDWR 三个设置互斥 可选:O_CREAT 文件不存在,则创建 flag参数是一个int型的数据,占4个字节,32位 flags 32位,每一位就是一个标志位 - mode : 创建出新的文件的操作权限(八进制) 如:0777 最终的权限是:mode & ~umask 即0777 & 0775 = 0775 umask的作用是抹去某些权限 返回值 : 返回一个新的文件描述符,若调用失败,返回-1 */ int main(){ int fd = open("create.txt",O_RDWR | O_CREAT,0777); if(fd == -1){ perror("open"); } //关闭 close(fd); return 0; }
-
read和write函数
/* #include<unistd.h> ssize_t read(int fd,void *buf,size_t count); 参数: - fd : 文件描述符,open得到的,通过这个文件描述符操作某个文件 - buf:需要读取数据存放的地方,数组的地址(传出参数) - count:指定的数组大小 返回值: - 成功: >0:返回实际读取到的字节数 =0:文件读完 - 失败:-1,并且设置errno */ /* #include<unistd.h> ssize_t write(int fd,const void *buf,size_t count); 参数: - fd : 文件描述符,open得到的,通过这个文件描述符操作某个文件 - buf:要往磁盘写入的数据 - count:要写的数据实际大小 返回值: - 成功:返回实际写入的字节数 - 失败:-1,并且设置errno */ #include<unistd.h> #include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main(){ //1、通过open打开文件 int srcfd = open("./english.txt",O_RDONLY); if(srcfd==-1){ perror("open"); return -1; } //2、创建一个新文件(拷贝文件) int destfd = open("./cpy.txt",O_WRONLY|O_CREAT,0664); if(destfd==-1){ perror("open"); return -1; } //3、频繁地读写操作 char buf[1024]={0}; int len = 0; while((len = read(srcfd,buf,sizeof(buf)))>0){ write(destfd,buf,len); } //4、关闭文件 close(destfd); close(srcfd); }
-
lseek函数
/* 标准c库函数 #include<stdio.h> int fseek(FILE *stream,long offset,int whence) Linux系统函数 #include<unistd.h> #include<sys/types.h> int lseek(int fd,off_t offset,int whence) 参数: - fd :文件描述符,通过open获取,通过这个fd操作某个文件 - offset : 偏移量 - whence : SEEK_SET:设置指针偏移量 SEEK_CUR:设置偏移量--当前偏移量+offset的值 SEEK_END:设置偏移量--文件大小+offset的值 作用: 1、移动指针到文件开始位置 lseek(fd,0,SEEK_SET); 2、获取当前文件指针的位置 lseek(fd,0,SEEK_CUR); 3、可以获取文件长度 lseek(fd,0,SEEK_END); 4、扩展文件长度 lseek(fd,100,SEEK_END); 注意:要写入数据才生效 */ #include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> int main(){ int fd = open("hello.txt",O_RDWR); if(fd==-1){ perror("open"); return -1; } int ret = lseek(fd,100,SEEK_END); if(ret==-1){ perror("lseek"); return -1; } //必须写入空数据 write(fd," ",1); close(fd); return 0; }
-
实战—实现ls - l的功能
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pwd.h> #include <time.h> #include <grp.h> #include <string.h> // 模拟实现ls-l // 效果:-rwxrwxr-x. 1 wsx wsx 8889 4月 1 16:24 a.out int main(int argc, char *argv[]) { // 判断输入参数是否正确 if (argc < 2) { printf("%s filename\n", argv[0]); return -1; } // 通过stat函数获取用户传入的文件信息 struct stat st; int ret = stat(argv[1], &st); if (ret == -1) { perror("stat"); return -1; } // 获取文件类型和权限 char perms[11] = {0}; // 保存文件类型和权限的字符串 switch (st.st_mode & __S_IFMT) { case __S_IFLNK: perms[0] = 'l'; break; case __S_IFDIR: perms[0] = 'd'; break; case __S_IFREG: perms[0] = '-'; break; case __S_IFBLK: perms[0] = 'b'; break; case __S_IFCHR: perms[0] = 'c'; break; case __S_IFSOCK: perms[0] = 's'; break; case __S_IFIFO: perms[0] = 'p'; break; default: perms[0] = '?'; break; } // 判断文件访问权限 perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-'; perms[2] = (st.st_mode & S_IWUSR) ? 'r' : '-'; perms[3] = (st.st_mode & S_IXUSR) ? 'r' : '-'; // 判断文件所在组 perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-'; perms[5] = (st.st_mode & S_IWGRP) ? 'r' : '-'; perms[6] = (st.st_mode & S_IXGRP) ? 'r' : '-'; // 其他人 perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-'; perms[8] = (st.st_mode & S_IWOTH) ? 'r' : '-'; perms[9] = (st.st_mode & S_IXOTH) ? 'r' : '-'; // 获取硬连接数 int linkNum = st.st_nlink; // 文件所有者 char *fileUser = getpwuid(st.st_uid)->pw_name; // 文件所在组 char *fileGrp = getgrgid(st.st_gid)->gr_name; // 文件大小 long int fileSize = st.st_size; // 获取修改时间 char *time = ctime(&st.st_mtime); char mtime[512] = {0}; strncpy(mtime, time, strlen(time) - 1); char buf[1024]; sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]); printf("%s\n", buf); return 0; }
-
文件属性操作函数
-
access
/* #include<unistd.h> int access(const char*pathname,int mode); 作用:判断某个文件是否有某个权限,或者判断文件是否存在 参数: - pathname:判断文件路径 - mode : R_OK:判断是否有读权限 W_OK:判断是否有写权限 X_OK:判断是否有执行权限 F_OK:判断文件是否存在 返回值: -成功返回0 -失败返回-1 */ #include <unistd.h> #include <stdio.h> int main() { int ret = access("a.txt", F_OK); if (ret == -1) { perror("access"); return -1; } printf("文件存在!"); return 0; }
-
chomd
/* #include<sys/stat.h> int chmod(const char*pathname,mode_t mode); 作用:修改文件权限值 参数: - pathname:文件路径 - mode :需要修改的权限值,八进制的数 返回值: -成功返回0 -失败返回-1 */ #include <sys/stat.h> #include <stdio.h> int main() { int ret = chmod("a.txt", 0777); if (ret == -1) { perror("chmod"); return -1; } return 0; }
-
chown
/* int chown(const char *path,uid_t owner,gid_t group); 作用:用来修改所有者和组 参数: 路径和用户id和组id */
-
truncate
/* #include<unistd.h> #include<sys/types.h> int truncate(const char *path,off_t length); 作用:缩减或者扩展文件的尺寸至指定大小 参数: - path :需要修改的文件路径 - length :需要最终文件变成的大小 返回值: 成功返回0 失败返回-1 */ #include <unistd.h> #include <sys/types.h> #include <stdio.h> int main() { int ret = truncate("b.txt", 20); if (ret == -1) { perror("truncatm); return -1; } return 0; }
-
-
目录操作函数
-
mkdir
/* #include<sys/stat.h> #include<sys/types.h> int mkdir(const char *pathname,mode_t mode); 作用:创建一个目录 参数: - pathname :创建的目录的路径 - mode :权限,八进制的数 返回值: 成功返回0 失败返回-1 */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> int main() { int ret = mkdir("aaa", 0777); if (ret == -1) { perror("mkdir"); return -1; } return 0; }
-
rmdir
#include <unistd.h> #include <stdio.h> int main() { int ret = rmdir("aaa"); if (ret == -1) { perror("rmdir"); return -1; } return 0; }
-
rename
/* #include <stdio.h> int rename(const char *oldpath, const char *newpath); */ #include <stdio.h> int main() { int ret = rename("aaa", "bbb"); if (ret == -1) { perror("rename"); return -1; } return 0; }
-
chdir和getcwd
/* #include <unistd.h> int chdir(const char *path); 作用:修改进程的工作目录 比如在/home/work下面启动了一个可执行程序a.out,进程的工作目录就是/home/work 参数: - path :需要修改的工作目录 返回值: 成功返回0 失败返回-1 #include <unistd.h> char *getcwd(char *buf, size_t size); 作用:获取当前的工作目录 参数: - buf : 存储的路径,指向的是一个数组(传出参数) - size:数组大小 返回值: 返回的指向的一块内存,这个数据就是第一个参数 */ #include <unistd.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> int main() { // 获取当前工作目录 char buf[128]; getcwd(buf, sizeof(buf)); printf("当前工作目录是:%s\n", buf); // 修改工作目录 int ret = chdir("/home/wsx/learn/chapter1/dirFun/temp"); if (ret == -1) { perror("chdir"); return -1; } // 创建新文件 int fd = open("chdir.txt", O_RDWR | O_CREAT, 0664); if (fd == -1) { perror("open"); return -1; } // 获取当前工作目录 char buf1[128]; getcwd(buf, sizeof(buf1)); printf("当前工作目录是:%s", buf1); return 0; }
-
-
目录遍历函数
/* #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 作用:打开一个目录 参数: - name : 需要打开的目录名称 返回值: DIR + 类型,理解为目录流 错误返回NULL #include <dirent.h> struct dirent *readdir(DIR *dirp); 作用:读取目录中的数据 参数: - dirp : 是opendir返回的结果 返回值: struct dirent,代表读取到的文件的信息 读取到了末尾或者失败了,返回NULL #include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); 作用:关闭目录 参数: - dirp : 是opendir的返回值 */ // #define _DEFAULT_SOURCE #include <dirent.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int getFileNum(const char *path); // 读取某个目录下所有普通文件个数 int main(int argc, char *argv[]) { if (argc < 2) { printf("%s path\n", argv[0]); return -1; } int num = getFileNum(argv[1]); printf("普通文件个数位:%d\n", num); return 0; } // 用于获取目录下所有普通文件的个数 int getFileNum(const char *path) { // 1、打开目录 DIR *dir = opendir(path); if (dir == NULL) { perror("opendir"); exit(0); } struct dirent *ptr; // 记录普通文件个数 int total = 0; while ((ptr = readdir(dir)) != NULL) { // 获取名称 char *dname = ptr->d_name; // 忽略.和.. if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) { continue; } // 判断是否是普通文件 if (ptr->d_type == DT_DIR) { // 目录,需要继续读取这个目录 char newpath[256]; sprintf(newpath, "%s/%s", path, dname); total += getFileNum(newpath); } if (ptr->d_type == DT_REG) { // 普通文件 total++; } } closedir(dir); return total; }
-
文件描述符相关操作的函数
-
dup函数
/* #include <unistd.h> int dup(int oldfd); 作用:复制一个新的文件描述符 fd = 3,int fd1 = dup(fd), fd指向的是a.txt,fd1也是指向a.txt 从空闲的文件描述符中找一个最小的,作为新的最拷贝的文件描述符 参数: - oldfd : 原有的文件描述符 */ #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> int main() { int fd = open("a.txt", O_RDWR | O_CREAT, 0664); int fd1 = dup(fd); if (fd1 == -1) { perror("dup"); return -1; } printf("fd:%d , fd1:%d\n", fd, fd1); close(fd); char *str = "hello,world!"; int ret = write(fd1, str, strlen(str)); if (ret == -1) { perror("write"); return -1; } close(fd1); return 0; }
-
dup2函数
/* #include<unistd.h> int dup2(int oldfd, int newfd); 作用:重定向文件描述符 oldfd 指向 a.txt ,newfd 指向 b.txt 调用函数成功后:newfd 和 b.txt 做 close ,newfd指向了a.txt oldfd必须是有效的文件描述符 oldfd和newfd值相同,相当于什么都没做 */ #include <unistd.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = open("1.txt", O_RDWR | O_CREAT, 0664); if (fd == -1) { perror("open"); return -1; } int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664); printf("fd: %d, fd1: %d\n", fd, fd1); int fd2 = dup2(fd, fd1); if (fd2 == -1) { perror("dup2"); return -1; } // 通过fd1去写数据,实际操作的是1.txt,而不是2.txt char *str = "hello,dup2"; int len = write(fd1, str, strlen(str)); if (len = -1) { perror("write"); return -1; } printf("fd: %d, fd1: %d, fd2: %d\n", fd, fd1, fd2); // fd2和fd1是相同的 close(fd1); close(fd); return 0; }
-
-
fcntl函数
/* #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... ); 参数: -fd : 需要操作的文件描述符 -cmd: 表示对文件描述符进行如何操作 - F_DUPED: 复制的是第一个参数fd,得到一个新的文件描述符 int ret = fcntl(fd,F_DUPED); - F_GETFL:获取指定的文件描述符文件状态flag 获取的flag和我们通过open函数传递的flag是一个东西 - F_SETFL:设置文件描述符文件状态flag 必选项:O_RDONLY,O_WRONLY,O_RDWR 不可被修改 可选性:O_APPEND,O_NONBLOCK O_APPEND 表示追加数据 O_NONBLOCK 表示设置成非阻塞 (阻塞和非阻塞:描述的是函数调用的行为) */ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <string.h> int main() { // 1、复制文件描述符 // int fd = open("1/txt",O_RDONLY); // int ret = fcntl(fd,F_DUPFD); // 2、修改或获取文件状态flag int fd = open("1.txt", O_RDWR); if (fd == -1) { perror("open"); return -1; } // 获取文件描述符状态flag int flag = fcntl(fd, F_GETFL); flag |= O_APPEND; // 修改文件描述符状态flag,给flag加入O_APPEND这个标记 int ret = fcntl(fd, F_SETFL, O_APPEND); if (ret == -1) { perror("fcntl"); return -1; } char *str = "nihao"; write(fd, str, strlen(str)); close(fd); return 0; }