1.安装Ubuntu18
- openssh-server
- net-tools
- pwd
- ls
- vscode
- 插件:remote
- c++
- 配置公钥
- windows 公钥复制到Linux的./ssh/authorized_keys
- ssh-keygen
- xshell
- xpt
2.gcc
- 安装
- 工作流程
- 源代码.c .h --预处理器(删除注释 宏处理替换,投文件展开)—处理后源代码.i – 编译器–汇编代码.s(汇编代码)–汇编器–目标代码.o–连接器 --可执行文件.out.exe
- gcc test.c -E -o test.i、
- gcc test.i -S -o test.s
- gcc test.s -o test.o
3.g++
类似gcc
g++ test.c -o test
4.gcc和g++的区别
gcc 和 g++都是GNU(组织)的一个编译器。
-
误区一:gcc
二只能编译c代码,g++只能编译c++代码。两者都可以,请注意:
后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序
后缀为.cpp的,两者都会认为是C++程序,C++的语法规则更加严谨
编译阶段,g++会调用gcc,对于C++代码,两者是等价的,但是因为gcc
命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的
-
误区二:gcc
实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释
如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则就是已定义
-
误区三:编译只能用gcc,链接只能用g++
严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc-1stdc++。
gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。
但在编译阶段,g++会自动调用gcc,二者等价
5. gcc编译选项
参数 | 描述 |
---|
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定的源文件,但是不进行链接 |
-o | [file1] [file2] / [file2] -o [file1] 将文件file2编译成可执行文件file1 |
-I | directory 指定 include 包含文件的搜索目录 |
-g | 在编译的时候,生成调试信息,该程序可以被调试器调试 |
-D | 在程序编译的时候,指定一个宏 gcc test02.c -o test -DDEBUG |
-w | 不生成任何警告信息 |
-Wall | 生成所有警告信息 |
-On | n的取值范围:0~3。编译器的优化选项的4个级别,-o0表示没有优化,-o1为缺省值,-o3优化级别最高 |
-l | 在程序编译的时候,指定使用的库 |
-L | 指定编译的时候,搜索的库的路径。 |
-fPIC/fpic | 生成与位置无关的代码 |
-shared | 生成共享目标文件,通常用在建立共享库时 |
-std | 指定c方言,如:-std=c99,gcc默认的方言是GNU C |
6.静态库制作
-
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行。
库文件有两种,静态库和动态库(共享库),区别是:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
库的好处:1.代码保密2.方便部署和分发
-
命名规则:
Linux:libxxx.a
lib:前缀(固定)
xxx:库的名字,自己起
.a:后缀(固定)
Windows:libxxx.lib
-
静态库的制作:
gcc获得.o文件 -c
将.o文件打包,使用ar工具(archive)
ar rcs libxxx.a xxx.o xxx.o
r - 将文件插入备存文件中
C - 建立备存文件
s - 索引
-
使用
- gcc -c add.c sub.c mult.c div.c -I …/include/ 编译生成.o
- ar rcs libcalc.a add.o sub.o mult.o div.o 生成libcal.a静态库文件
gcc main.c -o app -I ./include/calc -L ./lib -l calc
7.动态库的制作和使用
- 命名规则:
Linux:libxxx.so
lib:前缀(固定)
xxx:库的名字,自己起
.so:后缀(固定)
在Linux下是一个可执行文件
Windows:libxxx.dll - 动态库的制作:
gcc -c 得到.o文件,得到和位置无关的代码
gcc -c -fpic / -fPIC a.c b.c
gcc得到动态库
gcc -shared a.o b.o -o libcalc.so - 指定头文件目录和动态库文件目录编译得目标文件gcc main.c -o main -I include/ -L lib/ -l calc
- 配置动态库环境变量路径(仅在终端内生效):
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/绝对路径
- 查看环境变量:
echo $ LD _ LIBRARY _ PATH
- 查看依赖
ldd main
- 用户级环境变量配置:
- 在家目录下 编辑.bashrc文件
- source .bashrc 使生效
- 系统级别环境变量配置
- 编辑 /etc/profile 文件
- source .bashrc 使生效
- 修改/etc/ld.so.cache文件
- 间接修改 编辑/etc/ld.so.conf
- 将路径直接加入
- 更新 sudo ldconfig
- 不建议在把库文件放入/lib目录
8.库的工作原理
- 静态库:GCC进行链接时,会把静态库中代码打包到可执行程序中
- 动态库:GCC进行链接时,动态库的代码不会被打包到可执行程序中
- 程序启动之后,动态库会被动态加载到内存中,通过ldd(list dynamic
dependencies)命令检查动态库依赖关系 - 如何定位共享库文件呢?
当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是由ld-linux.so来完成的,它先后搜索elf文件的DT_RPATH段 -> 环境变量LD_LIBRARY_PATH —> /etc/ld.so.cache文件列表 —> /lib/, /usr/lib目录找到库文件后将其载入内存。
9.静态库和动态库的对比
1.
-
静态库
- 优点:
静态库被打包到应用程序中加载速度快
发布程序无需提供静态库,移植方便 - 缺点:
消耗系统资源,浪费内存
更新、部署、发布麻烦
-
动态库
- 优点:
可以实现进程间资源共享(共享库)
更新、部署、发布简单
可以控制何时加载动态库 - 缺点:
加载速度比静态库慢
发布程序时需要提供依赖的动态库
10.makefile
-
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,Makefile 文件定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为Makefile文件就像一个 Shell 脚本一样,也可以执行操作系统的命令。Makefile 带来的好处就是“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释Makefile文件中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如 Delphi 的 make, Visual C++的 nmake, Linux 下 GNU的make。
-
命名规则
-
makefile 或Makefile
-
makefile规则
-
一个文件中可以有一个或多个规则
-
目标…:依赖…
命令(shell命令)
…
- 目标:最终要生成的文件(伪目标除外)
- 依赖:生成目标所需要的文件或是目标
- 命令:通过执行命令对依赖操作生成目标(命令前必须Tab缩进)
-
其他规则为第一规则服务
-
makefile文件编写
-
makefile工作原理
-
工作原理
-
命令在执行之前,需要先检查规则中的依赖是否存在
- 如果存在,执行命令
- 如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,如果找到了,则执行该规则中的命令
-
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
- 如果依赖的时间比目标的时间晚,需要重新生成目标
- 如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行
-
变量
- 自定义变量
变量名=变量值 var=hello - 预定义变量
AR:归档维护程序的名称,默认值为ar
CC:C编译器的名称,默认值为cc
CXX:C++编译器的名称,默认值为g++
$@:目标的完整名称
$<:第一个依赖文件的名称
$^:所有的依赖文件 - 获取变量的值
$(变量名) - 模式匹配
- %.o:%.c
:通配符,匹配一个字符串
-两个%匹配的是同一个字符串 - %.o:%.c
gcc -c $< -o $@
-
函数
-
$(wildcard PATTERN...)
//函数名 函数参数
- 功能:获取指定目录下指定类型的文件列表
- 参数:PATTERN指的是某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般使用空格间隔
- 返回:得到的若干个文件的文件列表,文件名之间使用空格间隔
- 示例:
$(wildcard *.c ./sub/*.c)
返回值格式:a.c b.c c.c d.c e.c f.c
-
$(patsubst <pattern>,<replacement>,<text>)
- 功能:查找<text>中的单词(单词以“空格”、“Tab"或“回车”换行“分隔)是否符合模式,如果匹配的话,则以替换。
- 可以包括通配符%,表示任意长度的字串。如果中也包含%,那么,中的这个%将是中的那个%
所代表的字串。(可以用\来转义,以%来表示真实含义的“字符) - 返回:函数返回被替换过后的字符串
- 示例:
$(patsubst %.c,%.o,x.c bar.c)
返回值格式:x.o bar.o
-
clean函数
-
clean:
rm $(objs) -f
-
make clean 不能有clean文件
- 在之前加.PHONY:clean声明不生成clean文件
11. GDB调试
-
通常,在为调试而编译时,我们会()关掉编译器的优化选项(-0),并打开调试选项(-g)。另外,——Wa11在尽量不影响程序行为的情况下选项打开所有warning,也可以发现许多问题,避免一些不必要的BUG。
gcc -g -Wall program.c -o program
-
-g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。
-
GDB命令
-
启动和退出
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文件名:函数
查看断点
i/info b/break
删除断点
d/del/delete 断点编号
设置断点无效
dis/disable 断点编号
设置断点生效
ena/enable 断点编号
设置条件断点(一般用在循环的位置)
b/break 10 if i==5
-
运行GDB程序
start(程序停在第一行)
run(遇到断点才停)
继续运行,到下一个断点停
c/continue
向下执行一行代码(不会进入函数体)
n/next
变量操作
p/print变量名(打印变量值)
ptype变量名(打印变量类型)
向下单步调试(遇到函数进入函数体)
s/step
finish(跳出函数体)
自动变量操作
display num(自动打印指定变量的值)
i/info display
undisplay编号
其它操作
set var 变量名=变量值
until(跳出循环)
12. 标准C库IO函数和Linux系统IO函数对比
13. 虚拟地址空间
14.文件描述符
15. Linux系统IO函数
1. 文件打开关闭open close
int open(const char *pathname, int flags);
//文件路径 ,权限,返回文件描述符,失败返回-1
int open(const char *pathname, int flags, mode_t mode);
//mode 八进制数:创建的新文件操作权限
int close(int fd);
关闭文件
perror,
打印错误描述
umask
权限掩码 默认0002
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int fd = open("create.txt",0_RDWR|O_CREAT,0777);
if(fd==-1)
{
perror("open");
}
close(fd);
}
2. 文件读写read write
ssize_t read(int fd, void *buf, size_t count);
//文件描述符,读取数据存放的地方数组地址,数组大小,返回值:读取成功返回读取字节数,失败返回-1
ssize_t write(int fd, const void *buf, size_t count);
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int srcfd = open("english.txt", O_RDONLY);
if(srcfd == -1) {
perror("open");
return -1;
}
int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if(destfd == -1) {
perror("open");
return -1;
}
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0) {
write(destfd, buf, len);
}
close(destfd);
close(srcfd);
return 0;
}
3. 文件指针偏移fseek
off_t lseek(int fd, off_t offset, int whence);
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.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;
}
4. 获取文件相关的信息stat
int stat ( const char * pathname , struct stat * statbuf ) ;
int lstat ( const char * pathname , struct stat * statbuf ) ;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main() {
struct stat statbuf;
int ret = stat("a.txt", &statbuf);
if(ret == -1) {
perror("stat");
return -1;
}
printf("size: %ld\n", statbuf.st_size);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
int main(int argc, char * argv[]) {
if(argc < 2) {
printf("%s filename\n", argv[0]);
return -1;
}
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) ? 'w' : '-';
perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
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;
}
4. 文件属性操作函数
int access (const char *pathname, int mode);
//判断权限
int chmod(const char*filename,int mode);
//修改权限
int chown(const char*path,uid_t owner,gid_t group);
//设置文件所有者
int truncate(const char*path,off_t length);
//扩展文件
#include <unistd.h>
#include <stdio.h>
int main() {
int ret = access("a.txt", F_OK);
if(ret == -1) {
perror("access");
}
printf("文件存在!!!\n");
return 0;
}
#include <sys/stat.h>
#include <stdio.h>
int main() {
int ret = chmod("a.txt", 0777);
if(ret == -1) {
perror("chmod");
return -1;
}
return 0;
}
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main() {
int ret = truncate("b.txt", 5);
if(ret == -1) {
perror("truncate");
return -1;
}
return 0;
}
5. 目录操作函数
int mkdir (const char *pathname, mode_t mode);
//创建目录
int rmdir(const char *pathname);
//删除
int rename(const char*oldpath,const char*newpath);
//修改name
int chdir(const char *path);
//切换到目录
char*getcwd(char*buf,size_t size);
//获取绝对路径buf以及空间大小。
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main() {
int ret = mkdir("aaa", 0777);
if(ret == -1) {
perror("mkdir");
return -1;
}
return 0;
}
#include <stdio.h>
int main() {
int ret = rename("aaa", "bbb");
if(ret == -1) {
perror("rename");
return -1;
}
return 0;
}
#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/nowcoder/Linux/lesson13");
if(ret == -1) {
perror("chdir");
return -1;
}
int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
if(fd == -1) {
perror("open");
return -1;
}
close(fd);
char buf1[128];
getcwd(buf1, sizeof(buf1));
printf("当前的工作目录是:%s\n", buf1);
return 0;
}
6. 目录遍历函数
DIR *opendir (const char *name);
struct dirent *readdir (DIR *dirp);
int closedir (DIR *dirp);
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.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) {
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;
}
7.复制文件描述符 dup dup2
int dup (int oldfd) ;
复制文件描述符
int dup2(int oldfd, int newfd);
重定向文件描述符
#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;
}
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.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);
if(fd1 == -1) {
perror("open");
return -1;
}
printf("fd : %d, fd1 : %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
if(fd2 == -1) {
perror("dup2");
return -1;
}
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);
close(fd);
close(fd1);
return 0;
}
8. fcntl文件描述符操作
int fcntl (int fd, int cmd, ... /* arg */ );
复制文件描述符
设置/获取文件的状态标志
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main() {
int fd = open("1.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
int flag = fcntl(fd, F_GETFL);
if(flag == -1) {
perror("fcntl");
return -1;
}
flag |= O_APPEND;
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1) {
perror("fcntl");
return -1;
}
char * str = "nihao";
write(fd, str, strlen(str));
close(fd);
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)