Linux C/C++学习之 环境搭建+GCC编译过程+Makefile编写+相关操作函数

环境搭建

具体下载安装完整教程后续会补上~

  • Ubuntu 18.04 桌面版系统 .iso (sudo apt install gcc g++、 vim)
  • VMware 16(创建虚拟机)
  • VScode (C/C++拓展包、 JSON文件配置)连接Ubuntu
  • Xshell 7 远程连接Ubuntu
  • Xftp 7 远程连接Ubuntu
  • ssh免密配置

gcc编译过程

  • 命令: 以test.c 为例
//用户名@主机名
yang@yang:~/Linux/lession02$ gcc test.c -E -o test.i //-E 生成预处理后的文件test.i
yang@yang:~/Linux/lession02$ gcc test.i -S -o test.s//-S 汇编文件.s
yang@yang:~/Linux/lession02$ gcc test.s -s -o test.o//-s 生成可执行程序 
//(注 .out 是linux系统中可执行文件类似 window下的.exe)

在这里插入图片描述

心态崩了 这csdn不会自动保存吗?!!!!(之前写的内容意外缺失:gcc、 静态库制作与使用 、动态库制作与使用、两者对比)
在这里插入图片描述

  • gcc编译选项汇总
    在这里插入图片描述
    在这里插入图片描述

静态库

制作

在这里插入图片描述
在这里插入图片描述

使用

在这里插入图片描述

  • -I 指明当前.c文件头文件的位置
  • -L 指明当前静态库的位置
  • -l指明当前静态库(libxxx)的名字xxx

动态库

制作与使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 生产.c 的运行文件
    在这里插入图片描述
  • 运行main ,出现报错 下节解决。
    在这里插入图片描述

动态库生成后,运行可执行文件报错解决

静态库下,gcc链接时,静态库文件打包到可执行文件里面
动态库下,gcc链接时,动态库文件并没有打包到可执行文件里面,只有动态库的名字
查询 main(可执行文件)的依赖关系,可通过命令 ldd 来查看。下图可以发现libcalc.so的路径配置并不存在。
在这里插入图片描述
解决方式

  • LD_LIBRARY_PATH 环境变量配置
  1. 暂时性配置(关闭连接后重新启动配置重置)直接在命令行中 输入export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:(/动态库路径/)
  2. 用户级配置
    在最顶层目录下的bashrc文件中最后一行添加下面这个。 然后:wq 写入后保存退出到命令行
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:/动态库路径/

注上述动态库的完整路径 可在库所在文件夹下通过命令 pwd来查询

  1. 系统级(永久)

用sudo 打开profile文件最后一行输入 export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:(/动态库路径/)在这里插入图片描述

  • 第二种方法
    在这里插入图片描述
    在文件中直接添加(/动态库路径/)

静态库与动态库优缺点及建议

当库较小时一般用静态库,反之用动态库
在这里插入图片描述
在这里插入图片描述

  • 动态库图解
    在这里插入图片描述

Makefile编写

在这里插入图片描述
首先vim Makefile 输入下图内容保存退出 ,输入make 运行
在这里插入图片描述
在这里插入图片描述

Makefile快捷编写

在这里插入图片描述

编写前

在这里插入图片描述

用预定义变量编写后

在这里插入图片描述
方便了很多!

GDB调试

放入调试信息到执行文件中,会比一般相同的可执行文件大

gcc test.c -o test -g

输入 gdb test 进入GDB调试页面(注意test 是可执行文件)
在这里插入图片描述
在这里插入图片描述

list 相关命令

在这里插入图片描述

断点操作命令

在这里插入图片描述

调试命令

在这里插入图片描述

文件IO

相对于内存来说的IO,输入就是内存从文件中读取,输出就是内存将数据输出到文件中

虚拟内存概念

某个文件存储的实际物理内存不是连续的,为了方便起见,使用虚拟内存,由CPU中的逻辑单元将虚拟内存映射到实际物理内存。
在这里插入图片描述

文件描述符

  • 文件描述符表:前三个默认被占用,每打开一个文件拥有一个描述符。一个文件可以打开多次,故有多个对应的描述符。
  • 每个进程默认分配1024给文件描述符表,所以一个进程可以打开1024个文件。
    在这里插入图片描述

open打开文件

  • open函数
int open(const char* pathname,int flag)//用于打开文件

参数:

  1. pathname :打开文件路径
  2. flag : 有三种分别是只读、只写、可读可写。 O_RONLY 、O_WONLY、O_RDWR 具有互斥性
  3. 返回值 :返回一个新的文件描述符,发生错误时返回 -1
 int open(const char* pathname, int flag,mode_t mode)//用于创建文件
  • errno是属于Linux的函数库,是库里面的全局变量,用来记录最近发生错误的错误号。
    下列代码属于 linux 里面文件下的, 运用命令man 3 perror查看

 #include <stdio.h>

       void perror(const char *s);  //作用:打印errno对应的错误描述

       #include <errno.h>
  • 整体代码如下 (文件打开关闭,错误描述打印
#include <sys/types.h> //调用open函数 需要用到的库   Linux 命令:man 2 open    即可查询到
#include <sys/stat.h>  //调用open函数 需要用到的库   Linux 命令:man 2 open
#include <fcntl.h>     //调用open函数 需要用到的库   Linux 命令:man 2 open
#include <stdio.h>     //perror 在该库下           Linux 命令:man 3 perror
#include <unistd.h>    //close在该库下             Linux 命令:man 2 close

int main() {

    // 打开一个文件
    int fd = open("a.txt", O_RDONLY);

    if(fd == -1) {
        perror("open");  //输出为 open:xxx。 xxx为错误描述
    }
    // 读写操作

    // 关闭
    close(fd);

    return 0;
}
  • 输出结果
 perror("open");  //输出为 open:xxx。 xxx为错误描述

在这里插入图片描述

copy文件内容

注意读写过程。

#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("open1");
        return -1;
    }
    //创建目标文件 检测是否成功打开
    int desfd = open("copyI.txt",O_WRONLY | O_CREAT,0664);
     if(srcfd  == -1) {
        perror("open2");
        return -1;
    }
    //读写操作 利用buf
    char buf[1024] = {0};
    int len = 0;
    while((len = read(srcfd,buf,sizeof(buf))) > 0) {  //注意括号
        write(desfd,buf,len);
    }
   
    //关闭文件,一定要记得
    close(srcfd);
    close(desfd);
    return 0;
}

lseek 函数 (man 3 lseek)

#include <sys/types.h>
#include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);

参数:

  • fd: 文件描述符
  • -offset: 偏移量,
  • whence:
    SEEK_SET
    设置文件指针的偏移量
    SEEK_CUR
    设置偏移量:当前位置 + 第二个参数offset的值
    SEEK_END
    设置偏移量:文件大小 + 第二个参数offset的值

函数作用:

  • 移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
  • 获取当前文件指针的位置
 lseek(fd, 0, SEEK_CUR);
  • 获取文件长度
lseek(fd, 0, SEEK_END);
  • 拓展文件的长度,当前文件10b, 110b, 增加了100个字节
 lseek(fd, 100, SEEK_END)

注意:需要写一次数据

stat、lstat 函数

  • int stat(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;
}
  • 在Linux下输入 stat filename也可直接查看信息
    在这里插入图片描述

  • int lstat(const char *pathname, struct stat *statbuf); //获取软连接文件相关信息 保存到结构体中(而且该文件是软链接文件,即该文件指向某源文件)
    在这里插入图片描述

  • 关于结构体stat的变量
    在这里插入图片描述

  • 关于stat结构体中 st_mode
    在这里插入图片描述
    S_IFSOCK 那些都是八进制

模拟 ls -l

lesson10 /ls-l.c

//模拟实现ls -l 指令
//两个参数 :一个为 argv[0] = "ls"  另一个argv[1] ="filename"
//-rw-rw-r-- 1 yang yang 1067 9月  28 18:03 stat.c
int main (int argc,char * argv[]) {
    //传入参数个数少于两个 输入错了
    if(argc < 2) {
        //给出提示
        printf("%s filename\n" , argv[0]);
        return -1;
    }
    //将传入的文件名进行处理
    struct stat statfile;
    int ret = stat(argv[1],&statfile);
    if(ret == -1) {
        perror("stat");
        return -1;
    }
    ..
    .
    .
    .
    .
    

文件属性操作函数

  #include <unistd.h>
  int access(const char *pathname, int mode);
  • 作用:判断某个文件是否有某个权限,或者判断文件是否存在
  • 参数:
    - pathname: 判断的文件路径
    - mode:
    R_OK: 判断是否有读权限
    W_OK: 判断是否有写权限
    X_OK: 判断是否有执行权限
    F_OK: 判断文件是否存在
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
  • 作用:修改文件的权限
  • 参数:
    pathname: 需要修改的文件的路径
    mode:需要修改的权限值,八进制的数
    返回值:成功返回0,失败返回-1
 #include <unistd.h>
 #include <sys/types.h>
    int truncate(const char *path, off_t length);
  • 作用:缩减或者扩展文件的尺寸至指定的大小
  • 参数:
    path: 需要修改的文件的路径
    length: 需要最终文件变成的大小
    返回值:成功返回0, 失败返回-1
#include <unistd.h>

       int chown(const char *pathname, uid_t owner, gid_t group);
  • 作用:更改由路径名指定的文件的所有权,如果它是符号链接,则取消引用。
  • Linux下etc/passwd 存放了所有用户的信息和用户组的信息

操作目录函数

在这里插入图片描述
其中 char* getcwd(char * buf,size_t size);是获取当前工作目录

目录遍历函数

在这里插入图片描述

  • dirent 结构体
    DIR * dir = opendir(Path);
    dir->xxx 即可输出 对应结构体内变量
    在这里插入图片描述

文件描述符 操作函数

  • 一个文件可以由多个文件描述符指向。
  • dup函数 创建一个 与oldfd 相同的文件描述符,且其在文件描述符表中的处于空闲处的最小处
    如 oldfd = 3 那么 使用 int newfd = sup(oldfd) 后 newfd = 4
  • duq2 函数 使用 int fd = duq2(fd1,fd2)后这个函数会先判断fd1和fd2是不是同一个值,如果是的就直接返回fd2。如果不是的,它会先把fd2指向的文件关闭,然后把fd1复制给fd2然后把fd2返回。
    在这里插入图片描述
  • 文件描述符表
    在这里插入图片描述

fcntl函数

功能很多(列举部分)

/*

    #include <unistd.h>
    #include <fcntl.h>

    int fcntl(int fd, int cmd, ...);
    参数:
        fd : 表示需要操作的文件描述符
        cmd: 表示对文件描述符进行如何操作
            - F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
                int ret = fcntl(fd, F_DUPFD);

            - F_GETFL : 获取指定的文件描述符文件状态flag
              获取的flag和我们通过open函数传递的flag是一个东西。

            - F_SETFL : 设置文件描述符文件状态flag
              必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
              可选性:O_APPEND, O)NONBLOCK
                O_APPEND 表示追加数据   // 不进行O_APPEND 会覆盖原先数据
                NONBLOK 设置成非阻塞
        
        阻塞和非阻塞:描述的是函数调用的行为。
*/

O_APPEND 表示追加数据 ,不进行 flag |= O_APPEND; // flag = flag | O_APPEND从头开始覆盖原先数据!,反之,会从尾部开始添加。

  // 获取文件描述符状态flag
    int flag = fcntl(fd, F_GETFL);
    if(flag == -1) {
        perror("fcntl");
        return -1;
    }
    flag |= O_APPEND;   // flag = flag | O_APPEND

    // 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
    int ret = fcntl(fd, F_SETFL, flag);
    if(ret == -1) {
        perror("fcntl");
        return -1;
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值