Linux 应用入门(一)

1. 交叉编译

        概念:在当前编译平台下,编译出来的程序能运行在体系结构不同的另一种目标平台上,但是编译平台本身却不能运行该程序。

为什么需要交叉编译?

速度:目标平台得运行速度比主机往往慢得多,因为许多专用得嵌入式硬件被设计为低成本和低功耗,没有太高的性能

容量:整个编译过程是非常消耗资源的,嵌入式系统往往没有足够的内存或磁盘空间

可移植性:一个完整的Linux编译环境需要很多支持包,交叉编译使我们不需要花时间将各种支持包移植到目标板上

交叉编译链

 

        交叉编译链就是为了编译跨平台体系结构的程序代码而形成的由多个子工具构成的一套完整的工具集,这个工具集主要由编译器,连接器和解释器组成。同时,它隐藏了预处理、编译、汇编、链接等细节,当我们指定了源文件(.c)时,它会自动按照编译流程调用不同的子工具,自动生成最终的二进制程序映像(.bin)。

交叉编译的命名规则:

arch-core-kernel-system

 arch: 用于哪个目标平台

core: 使用的是哪个CPU core, 如Cortex M3但是这一组命名好像比较灵活,在其它厂家提供的交叉编译链中,有以厂家名称命名的,也有以开发板命名的,或者直接是none或cross的.

Kernel: 所运行的操作系统, 常见的有Linux, uclinux, bare

system:交叉编译链所选择的库函数和目标映像的规范,如gnu,gnueabi等。其中gnu等价于glibc+oabi;gnueabi等价于glibc+eabi。

例子:

  如果我们想要在ARM板编译出hello程序,需要使用交叉编译工具链:

arm-buildroot-linux-gnueabihf-gcc -o hello hello.c
相关问题:
1. 怎么确定交叉编译中头文件的默认路径?
        进入交叉编译器的目录里,执行:find -name “stdio.h”, 它位于“include” 目录下的根目录。
2. 怎么指定头文件目录
        编译时加上“-I <头文件目录>”这样的选项
3. 怎么确定交叉编译器中库文件的默认路径
        进入交叉编译器的目录里, 执行:find -name lib, 可以得到xxx/lib、xxx/usr/lib  一般来说这两个目录就是要找的路径,有很多so文件的目录一般就是要找的路径。
4. 怎么自己指定库文件目录,指定要用的库文件
        编译时,加上“-L < 库文件目录 > ”这样的选项,用来指定库目录;
        编译时,加上“-labc ”这样的选项,用来指定库文件 libabc.so

2 GCC编译器

      gcc是GNU Complier Collection的缩写。最初是作为C语言的编译器(GNU C Compiler),现在已经支持多种语言了,如C、C++、Java、Ada、COBOL语言等。其实他就是转换成机器能够读懂的语言。

            一个C/C++文件要经过如上图步骤才能转换为可执行文件。

        1. 预处理: 预处理就是将要include的文件插入到源文件中,将宏定义展开,根据条件编译命令选择要使用的代码,删除注释,最后输出.i文件。

        2. 编译:将.i文件转换成汇编语言,进行语法检查和语义分析,优化代码并提高效率,生成.s汇编代码文件

        3. 汇编: 将汇编代码转换成机器语言,生成.o目标文件

        4. 链接:将多个目标文件以及库文件链接在一起,生成可执行文件,解决外部引用,即确保程序中的每个函数和变量调用都能找到正确的定义, 处理库依赖,链接系统库和数学库等。

常用选项描述
-E
预处理,开发过程中想快速确定某个宏可以使用“ -E -dM
-c
把预处理、编译、汇编都做了,但是不链接
-o
指定输出文件
-I  (大I)
指定头文件目录
-L
指定链接时库文件目录
-l (小l)
指定链接哪一个库文件

 库

        库(Library)是一组预先编译好的代码,这些代码可以被多个程序共享和重用。库的主要目的是提供一种方法来避免重复编写相同的代码,同时使得程序的编译和链接更加高效。

静态库:

        1. 静态库在程序编译时被链接到最终的可执行文件中

        2. 它们通常以.a为文件拓展名

        3. 因为静态库的代码被包含在最终的可执行文件中,所以程序的体积会更大,但运行时不需要额外的库文件。

动态库:

        1. 动态库在程序运行时被加载

        2. 通常以.so为文件拓展名

        3. 动态库允许多个程序共享同一份库代码,节省磁盘空间和内存

        静态库优点:程序运行时无需加载库,运行速度更快

        静态库缺点: 程序尺寸变大,静态库升级时程序需要重新编译链接

静态库的创建:

ar rcs libxxx.a xxx.o xxx.

静态库的使用:

gcc main.c -o app -I ./include/ -L ./lib -lxxx

   使用的时候-I是指定头文件引用的目录, -L是指定链接静态库的目录, -l是指定引用的静态库,通常可以省略lib前缀。

动态库的创建:

     制作动态库分为两个步骤,第一步是生成与位置无关的.o文件,与位置无关就是可以存在于内存中的任何地方执行,是通过间接寻址查询到的代码,有助于创建多个程序之间共享的代码。

gcc -c -fpic hello.c

gcc -shared -o libhello.so.1 hello.o    //  .1 是版本号

动态库的使用:

        需要动态库文件存在于可执行文件能够找到的路径中,通常包括标准库路径、环境变量LD_LIBRARY_PATH(在Linux上)或PATH(在Windows上),或者在程序启动时显式指定路径,如下所示

# ==============本终端回话使用======
 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/arm_c/HQ/1_lib/2_dynamic_lib/src/
 export # 查看是否成功
 # ==============本用户使用========
 ~/.bashrc 或~/.bash_profile 文件
 sudo vim ~/.bashrc 或者~/.bash_profile
 # 在尾部加一句 
 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/arm_c/HQ/1_lib/2_dynamic_lib/src/
 # 重启配置文件
 source ~/.bashrc
 # ==============所有用户使用=======
# 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64)

gcc -o myprogram main.c -L/path/to/library -lmylibrary

库的搜索顺序:

① 指定动态库的搜索路径:-Wl,-rpath=路径1 : 路径2,则有限查找。
② 查不到就到LD_LIBRARY_PATH 环境变量指明的路径中查找。
③ /ect/ld.so.conf 文件中指定的搜索路径查找。
④ /lib、/lib64、/usr/lib、/usr/lib64 中查找。

3 Makefile的使用

3.1 Makefile基本规则

基本规则:如果依赖文件比目标文件更加新,那么执行命令来重新生成目标文件(目标文件还没生成或依赖文件比目标文件新),注意命令行的起始字符必须为TAB字符。

目标(target) : 依赖(prerequiries) 
<tab>命令(command)

elample:

3.2 make是如何工作的

       1.  make首先会找到当前目录下名字叫“Makefile”和“makefile”

        2. 如果找到默认会寻找第一个目标文件,除非make 接相应的目标文件则会寻找相应的目标。 

        3. 如果第一个目标文件不存在或是依赖文件比目标文件新那么就会执行命令生成目标文件

        4. 如果依赖文件不存在或者依赖文件所依赖的比他新会执行相应的命令生成依赖文件 

 3.3 伪目标 

        “伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。  这么做的好处是避免文件系统中存在同名文件时的冲突,并且能确保每次执行假想目标时,不论目标文件是否存在,规则都会被执行。

声明假想目标
.PHONY: clean

3.4 makefile的函数 

1.  foreach

$(foreach var, list, text)

        for each var in list, change it to text, 对list中的每一个元素,取出来赋给var, 然后把var改为text所描述的形式。

objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d)  // 最终 dep_files := .a.o.d .b.o.d

2.   wildcard函数

$(wildcard pattern)
src_files := $( whildcard *.c) //  最终 src_files中列出了当前目录下的所有.c文件

3. patsubst

$(patsubst pattern, replacement, text)

        将搜索模式pattern 的格式替换成replacement的格式,替换的内容为text的内容,比如将.c结尾的文件替换成.o结尾的文件。

SRC_FILES := $(patsusbt %.c, %.o, $(wildcard src/*.c))

3.5 变量

$表示取变量的值,当变量名多于一个字符时,使用“()"

$符的其他用法

$^ 表示所有的依赖文件

$@ 表示生成的目标文件

$< 代表第一个依赖文件

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
 
ALL: hello.out
 
hello.out: $(OBJ)
        gcc $< -o $@
 
%.o: %.c
        gcc -c $< -o $@

3.6 变量赋值

 1. Makefile 使用"="进行赋值,但是要注意的是变量的值是整个Makefile最后被指定的值

VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA

       这里面要注意的是最后VIR_B的值是AA B,而不是 A B, 在make的时候makefile会整个展开决定变量的值。

2.   ":=" 表示直接赋值, 赋予当前位置的值, 就是变量会立即展开, 所以下面VIR_B 应该是A B

VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

3. "?="表示如果该变量没有被赋值,赋值于等号后面的值, 如果已经被赋值,该行就不会被执行

VIR ?= new_vlaue

4. “+=” 表示将符号后面的值添加到前面的变量上

3.7 通用makefile的模板

# 可执行文件名
TARGET = main

# gcc类型
CC = gcc

# 存放中间文件的路径
BUILD_DIR = build

#存放.c 源文件的文件夹
SRC_DIR = \
	./    \
	./src

# 存放头文件的文件夹
INC_DIR = \
	./inc

# 在头文件路径前面加入-I
INCLUDE	= $(patsubst %, -I %, $(INC_DIR))

# 得到带路径的 .c 文件
CFILES := $(foreach dir, $(SRC_DIR), $(wildcard $(dir)/*.c))

# 得到不带路径的 .c 文件
CFILENDIR := $(notdir  $(CFILES))

# 将工程中的.c 文件替换成 ./build 目录下对应的目标文件 .o
COBJS = $(patsubst %, ./$(BUILD_DIR)/%, $(patsubst %.c, %.o, $(CFILENDIR)))

# make 自动在源文件目录下搜索 .c 文件
VPATH = $(SRC_DIR)

$(BUILD_DIR)/$(TARGET).exe : $(COBJS)
	$(CC) -o $@ $^

$(COBJS) : $(BUILD_DIR)/%.o : %.c
	@mkdir -p $(BUILD_DIR)
	$(CC) $(INCLUDE) -c -o $@ $<

clean:
	rm -rf $(BUILD_DIR)

4. 文件IO

4.1 文件的来源

Linux的文件来源:

1.存储真实文件:磁盘、flash, SD卡, U盘

2. 内核提供的虚拟文件系统:需要先挂载

3. 特殊文件:/dev/xxx设备节点, 分为字符设备和块设备

4.2 文件I/O与标准I/O

文件I/O:

        文件I/O是操作系统封装了一系列open、close、write、read等API函数构成的一套用来读、写文件的接口供应用程序使用,通过这些接口可以实现对文件的读写操作,但是效率并不是最高的。(文件I/O采用系统直接调用的方式,向系统内核发出请求之后,系统内核会收到执行相关代码处理的请求,决定是否将操作硬件资源或返回结果给应用程序。)

标准I/O:

        应用层C语言库函数提供了一些用来做文件读写的函数列表,叫标准I/O。标准IO有一系列的C库函数构成(fopen,fclose,fwrite,fread),这些标准IO函数其实是由文件IO封装而来的(fopen内部还是调用了open);

文件I/O和标准I/O的本质区别:

 缓冲区:标准I/O存在缓冲区,进行操作的时候先操作缓冲区,满足一定条件才会执行系统调用。而文件I/O不存在缓冲区,直接执行系统调用。

系统开销:使用标准I/O可以减少系统调用的次数,提高系统效率。因为文件I/O会频繁调用系统调用,Linux从用户态切换到内核态,处理相应的请求,会增加系统开销。

执行开销:标准I/O每次调用写入字符,不着急写入文件,而是先放到缓冲区,直到缓冲区满足刷新的条件,再把所有数据写入文件,这个过程只用了一次系统调用,这样很大程度提高了执行效率。

4.3 文件调用的概念

1. 文件调用的基本原理

        文件IO open,read, write等函数是通过glibc实现的,它们本质上还是用户层的函数,这个函数通过触发异常也就是将原因存入R7/R8的寄存器,然后指向swi 0指令,内核会识别异常原因,调用不同的处理函数,比如open对应的是sys_call_tabele[_NR_open] 和sys_open函数

2. 文件描述符fd

任何一个进程,在启动的时候都会默认打开三个文件:

  1.         标准输入 -- 设备文件 -> 键盘文件 0
  2.         标准输出 -- 设备文件 -> 显示器文件 1
  3.         标准错误 -- 设备文件 -> 显示器文件 2

        文件描述符也是open对应的返回值,用户创建的文件返回的值是从3开始,因为0,1,2被默认打开的文件所占用了。fd的本质是数组下标。fd分配的原则是最小的 没有被使用的数组元素分配给新文件。 

        在进程中,每个进程都拥有task_struct进程控制块, task_struct里面有个指针files_struct结构体该结构体里面维护一个fd 结构体, 用于指向一张表files_struct, 该表包含了一个指针数组,而这数组里每个指针指向一个内存中的文件struct file,从而实现了进程与文件的连接,而文件描述符就是该数组的下标,只要知道文件描述符就可以找到内存中的文件。内核里面file结构体对应的是具体的文件,他的读写位置有一个变量f_pos保存读写位置。

 yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

文件流指针和文件描述符的区别

        文件流指针是高级文件操作接口的表示。在许多高级编程语言和库中使用文件流指针来管理文件的读写操作。文件流指针封装了文件的缓冲区管理、字符编码处理等细节,使文件操作更加方便和安全。在c语言中,使用FILE*类型的指针,在C++中使用“ifstream”和“ofstream"类。

        文件描述符是低级别操作系统接口的表示,通常用于系统调用级别的文件操作。文件流指针底层封装了文件描述符,在文件流指针结构体中可以看到 _fileno保存了fd。

3. 缓冲区的概念

        行缓冲:常见的是对显示器进行刷新数据时,即必须缓冲区中一行填满了或者遇到\n才会输出到显示器。

        全缓冲:对文件进行写入时采用全缓冲,即将整个缓冲区填满才会刷新到磁盘中。

        无缓冲:即刷新数据时没有缓冲区

4. 文件重定向

        文件重定向本质是修改files_struct表中的指针数组中指针所指向的内容。

        怎么做?

        在打开新的文件前,我们先close(1);关闭显示器,即上图打红叉的线被取消,所以当新的文件被打开它的文件描述符为1,即上图蓝色的线被连上,所以当往1号文件写入会被写入到log.txt而不会在显示器显示.

使用dup2系统调用实现重定向     

int dup2(int oldfd, int newfd)

         这个函数可以理解为newfd为oldfd的拷贝,即对后者的操作变为对前者的操作。

sample

dup(fd, 1)  // fd为某个新打开文件的描述符

        代表本来要输出到1的内容重定向到去。

重定向定位的问题

#include<stdio.h>    
#include<unistd.h>       
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
int main()    
{    
   //演示重定向基本原理                                                     
   umask(0);      
   close(1);//关闭显示器      
   int fd=open("log.txt",O_WRONLY|O_CREAT,0666);//fd=1      
   if(fd<0){ return 1;} //打开失败     
   write(1,"hello world!\n",13);//写到1号文件      
   write(1,"hello world!\n",13);      
   write(1,"hello world!\n",13);      
   write(1,"hello world!\n",13);      
   write(1,"hello world!\n",13);           
   close(fd);
   return 0;
}

        以下例子中,在第一个正常输出到显示器中,时,是正常显示三行的,但是重定向输出到文件中时,会多打印两行,当往显示器中打印时,系统采用的是行缓冲,即运行到printf等函数后,立马将数据刷新到显示器。

        当重定向到文件中时,如第二个例子,缓冲方式发生变化,变为全缓冲,全缓冲会等到程序结束时,一次性将缓冲区内容打印到文件中,程序最后创建了子进程,子进程会继承父进程的缓冲区(子进程的缓冲区和父进程缓冲区内容相同,但是不是一个缓冲区,进程的独立性,发生写实拷贝)所以父进程刷新一次缓冲区后,子进程也会刷新缓冲区,所以会打印两次 C 函数的内容。因为系统函数(系统接口)没有缓冲区,所以 write函数只打印一次,并且是第一个被打印。

4.4 文件操作

打开文件open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
打开⽂件:
int open(const char *pathname, int flags);
    参数:
        参数1:
            const char *pathname:字符指针,表⽰的是字符的地址,
                                  字符串的⾸地址,要打开的⽂件路径字符串的地址
        参数2:
            int flags:整数,打开⽂件的选项
                O_RDONLY:只读
                O_WRONLY:只写
                O_RDWR:读写
                O_TRUNC:清空⽂件(在有 写 ⽅式 有效)
                O_APPEND:追加⽂件(在有 写 ⽅式 有效),在写⽂件时,在
                          ⽂件末尾位置添加写
                O_CREAT:如果⽂件不存在则,创建⽂件,存在则直接打开,
                         如果要使⽤当前选择,则需要第三个参数:创建⽂件权限
   返回值:
        失败,返回-1
        成功打开⽂件,返回⽂件描述符 >= 0
 
 
创建且打开⽂件:
            int open(const char *pathname, int flags, mode_t mode);

读取文件 read

#include <unistd.h>
从打开的⽂件中读取⽂件内容,输⼊到程序中
 
ssize_t read(int fd, void *buf, size_t count);//从指定的
fd(打开的⽂件中,读取count个字节数据,存放到程序的内存buf地址开始位置)
    参数:
        参数1:
                int fd:⽂件描述符,表⽰打开的⽂件
        参数2:
                void *buf:指针,表⽰把从⽂件中读取的内容,存放到程序
                           指定的内存地址中
        参数3:
                size_t count:整数,表⽰从⽂件中读取多少个字节的数据内容
    返回值:
        成功:返回读取到的字节数,如果返回值为0表⽰本次读取是从⽂件末尾开始读取,没有内容
        失败:-1

写入文件 write

#include <unistd.h>
从程序中把内存数据写⼊到⽂件中,程序输出到⽂件中
 
ssize_t write(int fd, const void *buf, size_t count);//把buf这个内存地址的中的数据,拿出                                     count字节数,写⼊到fd⽂件中
    参数:
        参数1:
            int fd:要写⼊哪个⽂件
        参数2:
            const void *buf:要写⼊的内容在哪个内存地址(把哪个内存地址的内容,写⼊⽂件)
        参数3:
            size_t count:要写⼊内容的⼤⼩
    返回值:
        成功:返回写⼊的字节数
        失败:返回-1

关闭文件close

#include <unistd.h>
//把打开的⽂件关闭
int close(int fd);
    参数:
        参数1:
            int fd:⽂件描述符,表⽰关闭哪个打开的⽂件
    返回值:
        成功:返回0
        失败:返回-1

设置文件偏移位置Iseek

#include <sys/types.h>
#include <unistd.h>
重新设置⽂件当前操作位置(修改偏移位置)
off_t lseek(int fd, off_t offset, int whence);//设置打开的fd⽂件的偏移位置
        参数:
            参数1:
                int fd:表⽰打开的⽂件,要设置的⽂件
            参数2:
                off_t offset:整数,偏移量,表⽰偏移多少个字节
                            +:正数,向⽂件末尾偏移
                            -:负数,向⽂件开头偏移
            参数3:
                int whence:基准点,表⽰从哪个位置开始计算
                    SEEK_SET:从⽂件开始位置计算偏移
                    SEEK_CUR:从⽂件当前的操作位置计算偏移
                    SEEK_END:从⽂件末尾位置开始计算偏移
        返回值:
            成功:返回从⽂件开始位置到新偏移之后位置⼀共多少个字节
            失败:返回-1

 位置偏移   

#include <sys/types.h>
#include <unistd.h>
重新设置⽂件当前操作位置(修改偏移位置)
off_t lseek(int fd, off_t offset, int whence);//设置打开的fd⽂件的偏移位置
        参数:
            参数1:
                int fd:表⽰打开的⽂件,要设置的⽂件
            参数2:
                off_t offset:整数,偏移量,表⽰偏移多少个字节
                            +:正数,向⽂件末尾偏移
                            -:负数,向⽂件开头偏移
            参数3:
                int whence:基准点,表⽰从哪个位置开始计算
                    SEEK_SET:从⽂件开始位置计算偏移
                    SEEK_CUR:从⽂件当前的操作位置计算偏移
                    SEEK_END:从⽂件末尾位置开始计算偏移
        返回值:
            成功:返回从⽂件开始位置到新偏移之后位置⼀共多少个字节
            失败:返回-1

目录文件操作

创建目录mkdir

#include <sys/stat.h>
#include <sys/types.h>
在指定⽬录中创建⼀个⽬录⽂件
int mkdir(const char *pathname, mode_t mode);
        参数:
            参数1:
                const char *pathname:指针,字符串⾸地址,要创建的⽬录⽂件的路径
            参数2:
                mode_t mode:创建的⽬录的权限(读写执⾏)
        返回值:
            成功:返回0
            失败:返回-1

删除目录rmdir

#include <unistd.h>
int rmdir(const char *pathname);
    参数:
        参数1:
            const char *pathname:字符串⾸地址,表⽰要删除的⽬录
    返回值:
        成功:返回0
        失败:返回-1

打开目录文件

#include <sys/types.h>
#include <dirent.h>
去打开对应路径下的⽬录
DIR *opendir(const char *name);
    参数:
        参数1:
            const char *name:字符串⾸地址,表⽰要打开的⽬录⽂件路径
    返回值:
            DIR:⽬录信息结构体类型
            成功:返回⽬录信息结构体的地址(指针),标识打开的⽬录⽂件
            失败:返回NULL(空指针)

获取打开目录中的文件readdir

#include <dirent.h>
获取打开的⽬录中,⼀个⽂件
struct dirent * readdir(DIR *dirp);
    参数:
        参数1:
            DIR *dirp:获取哪个(打开的)⽬录中的⽂件
    返回值:
        成功:返回获取到的这个⽂件的描述(结构体)的地址NULL:表⽰本次获取已经获取到⽬录的结尾了没有⽂件了(已经获取完)
 
 
⽂件描述结构体
    struct dirent {
         ino_t d_ino;//inode号,⽂件系统中对⽂件的唯⼀编号
         off_t d_off;//偏移
         unsigned short d_reclen;//⻓度⼤⼩
         unsigned char d_type;//⽂件类型
         char d_name[256];//⽂件名
     };

关闭打开的目录文件closedir

#include <sys/types.h>
#include <dirent.h>
关闭打开的⽬录
int closedir(DIR *dirp);
    参数:
        参数1:
            DIR *dirp:表⽰要关闭的⽬录⽂件
    返回值:
        成功:返回0
        失败:返回-1

4.5 标准I/O

库函数:由计算机语⾔标准委员会审核通过,如 C 标准委员会,只要是使⽤ C语⾔就可以使⽤那⼀套函数

fopen打开文件

#include <stdio.h>
打开指定的⽂件
FILE *fopen(const char *pathname, const char *mode);
    参数:
        参数1:
            const char *pathname:字符串⾸地址,表⽰要打开的⽂件路径
    参数2:
            const char *mode:字符串⾸地址,通过通过字符串来表⽰打开⽂件的⽅式
 
"r":只读⽅式打开(⽂件必须存在)---------O_RDONLY
"r+":读写⽅式打开--------O_RDWR
"w":只写⽅式打开(清空⽂件,当⽂件不存在时创建)----O_WRONLY | O_CREAT | O_TRUNC
"w+":读写⽅式打开(清空⽂件,当⽂件不存在时创建)---O_RDWR | O_CREAT | O_TRUNC
"a":追加写⽅式打开(操作位置在⽂件末尾,当⽂件不存在时创建)---O_WRONLY | O_CREAT | O_APPEND
"a+":读写⽅式打开(写为追加写操作位置在⽂件末尾,当⽂件不存在时创建)-----O_RDWR | O_CREAT |             
      O_APPEND
 
如果上述字符串中 包含'b'表⽰打开⼆进制⽂件,否则打开是⽂本⽂件
    返回值:
        FILE:是⼀个结构体,描述打开的⽂件信息(包括了⽂件描述符)
        返回值就是返回FILE这个结构体类型变量的地址成功:返回FILE * 指针,⽂件信息结构体地址(能知            
             道打开的⽂件)
        失败:返回NULL(空指针)

关闭文件fclose

#include <stdio.h>
关闭⽂件,则会把当前打开的⽂件的缓冲区存放到⽂件中
int fclose(FILE *stream);
    参数:
        参数1:
            FILE *stream:关闭打开的哪个⽂件
    返回值:
        成功:返回0
        失败:返回-1(EOF)

写入文件fwrite

#include <stdio.h>
把数据写⼊到⽂件
size_t fwrite(const void *ptr, size_t size, size_tnmemb,FILE *stream);
    参数:
        参数1:
            const void *ptr:要写⼊⽂件的内容对应地址
        参数2:
            size_t size:每⼀个数据⼤⼩
        参数3:
            size_t nmemb:写⼊多少个数据
        参数4:
            FILE *stream:写⼊的⽂件
    返回值:
        成功:返回写⼊的数据的个数

读取文件fread

#include <stdio.h>
从⽂件中读取数据存放到ptr
size_t fread(void *ptr, size_t size, size_t nmemb, FILE*stream);
    参数:
        参数1:
            void *ptr:从⽂件中读取的数据存放的位置(指针)
        参数2:
            size_t size:每个数据⼤⼩(字节)
        参数3:
            size_t nmemb:读取多少个数据
        参数4:
            FILE *stream:读取的⽂件
    返回值:
        成功:返回读取的数据个数
        0:表⽰读取时没有数据可读(到达⽂件末尾) , 或 读取错误

读取单个字符fgetc

#include <stdio.h>
从⽂件中读取⼀个字符,以返回值形式,返回读取到的字符(int)
int fgetc(FILE *stream);
    参数:
        参数1:
            FILE *stream:从哪个⽂件中读取
    返回值:
        成功:返回读取到的字符,以int类型(字符对应的ASCII码)表⽰
              如果本次是在⽂件末尾位置读取(⽂件结束位置),返回EOF(-1)
              如果读取失败,返回EOF需要判断 EOF到底是失败还是读取到⽂件末尾
                int getc(FILE *stream); ==== fgetc
                int getchar(void); == fgetc(stdin):从终端⽂件读取(输⼊)⼀个字符

写入单个字符fputc

#include <stdio.h>
往⽂件中写⼊⼀个字符
int fputc(int c, FILE *stream);
    参数:
        参数1:
            int c:要写⼊的字符的ASCII码
        参数2:
            FILE *stream:要写⼊的⽂件
    返回值:
        成功:返回写⼊的字符的ASCII码
        失败:返回EOF(-1)
        int putc(int c, FILE *stream); 等价于 fputc
        int putchar(int c);等价于 ===== fputc(c,stdout),往终端⽂件写⼊⼀个字符

读取一个字符串fgets

#include <stdio.h>
从⽂件中读取⼀个字符串
char *fgets(char *s, int size, FILE *stream);从⽂件 stream中读取内容,最多读取size-1个字符,存储到s指针这个地址中。具体读取的字符⼤⼩:⼆选⼀
1、读取到⽂件结束
2、读取到⼀⾏结束(\n)
 
如果在读取过程中当读取到size-1时,两个都不满⾜,则读取size-1个字符(读取最⼤⼤⼩)
 
注意:在读取的字符串后,加上'\0'字符,表⽰字符串的结束
返回值:
    成功:返回 s 指针
    NULL:本次读取在⽂件结束位置读取(已经读取到⽂件末尾)
    char *gets(char *s);等价于 == fgets(s,,stdin),从终端上读取⼀个字符串,没有限制⼤⼩(没有size-1)容易越界

写⼊⼀个字符串

#include <stdio.h>
把s中的字符串('\0'为⽌),写⼊到⽂件中
int fputs(const char *s, FILE *stream);
    参数:
        参数1:
            const char *s:要写⼊的字符串,到'\0'为⽌
        参数2:
            FILE *stream:写⼊的⽂件
    返回值:
        成功:⾮负整数 >= 0
        失败:EOF(-1)
        int puts(const char *s);等价于 ==== fputs(s,stdout),往终端上写字符串

刷新标准io缓冲区

#include <stdio.h>
主动把缓冲区的内容写⼊⽂件
int fflush(FILE *stream);

判断是否错误或读取到⽂件结束

#include <stdio.h>
 
测试当前是否是⽂件末尾,如果是⽂件末尾返回⾮0(返回真)
int feof(FILE *stream);
 
测试当前是否是错误,如果是错误返回⾮0
int ferror(FILE *stream);

面经:

        如何操作文件以及判断文件是否存在?请说明open、read、write等函数的使用?

        首先使用access函数判断文件是否存在,然后使用open函数打开文件,可以使用O_CREAT标志在不存在的时候创建函数,使用’read'函数读取文件内容,使用‘write’函数向文件写入内容,使用‘close’函数关闭文件。open函数主要用于打开文件并返回文件描述符,可以指定打开的路径和文件的打开方式,如只读,只写,读写和创建文件等。read用于从文件描述符读取数据到缓冲区,write是将缓冲区的数据写入文件描述符,这三个函数都是系统级别的调用。

链接:

【Linux】深入理解文件IO操作_linux文件io球滚动-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/637573.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10个企业用的wordpress中文模板

移民wordpress主题 移民代办wordpress主题&#xff0c;适合做海外移民咨询的代理公司搭建wordpress企业官方网站使用。 https://www.jianzhanpress.com/?p5130 模特演出wordpress主题 暗黑风格的wordpress主题模板&#xff0c;适用于模特演出公司或艺人经纪公司搭建wordpre…

使用B2M 算法批量将可执行文件转为灰度图像

参考论文 基于二进制文件的 C 语言编译器特征提取及识别 本实验使用 B2M 算法将可执行文件转为灰度图像&#xff0c;可执行文件转为灰度图的流程如图 4-3 所示。将 可执行文件每 8 位读取为一个无符号的的整型常量&#xff0c;一个可执行文件得到一个一维向量&#xff0c; …

2.go环境配置与开发工具选择

go 环境配置 下载安装包 官网(https://go.dev/dl/) 下载地址(国内)(https://golang.google.cn/dl/) 根据自己的操作系统选择下载即可 下载后安装 记住地址 比如&#xff1a; D:\work\devtool\go 配置系统环境变量 PATH 指向 go 的安装 bin 目录 比如&#xff1a; D:\work…

面向Prompt编程

Prompt 就像和一个人对话&#xff0c;你说一句&#xff0c;ta 回一句&#xff0c;你再说一句&#xff0c;ta 再回一句…… Prompt 就是你发给大模型的指令&#xff0c;比如「讲个笑话」、「用 Python 编个贪吃蛇游戏」、「给男/女朋友写封情书」等 貌似简单&#xff0c;但意义…

【OpenGL纹理】纹理贴图基础知识(01/4)

文章目录 一、说明二、贴图的初始化处理2.1 贴图中的几种纹理2.2 原始数据处理 - 贴图的规格化 三、纹理对象生成和绑定&#xff08;选中&#xff09;3.1 生成纹理矩阵3.2 glGenTextures 函数明细3.2 glBindTexture函数明细 四、glTexParameteri函数4.1 贴放放法参数确定4.2 放…

字符串的周期:每一期都有那么几位

【题目描述】 如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例 如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。 输入一个长度不超过80的字符串(不含空格),输出其最小周期。 输入第一行表示有T组数据,后续是T行字符串。输出的每组…

【Qt 学习笔记】Qt窗口 | 浮动窗口 | QDockWidget的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 浮动窗口 | QDockWidget的使用及说明 文章编号&#xff1a;Q…

Vue 3 组件基础与模板语法详解

title: Vue 3 组件基础与模板语法详解 date: 2024/5/24 16:31:13 updated: 2024/5/24 16:31:13 categories: 前端开发 tags: Vue3特性CompositionAPITeleportSuspenseVue3安装组件基础模板语法 Vue 3 简介 1. Vue 3 的新特性 Vue 3引入了许多新的特性&#xff0c;以提高框…

路由引入实验(华为)

思科设备参考&#xff1a;路由引入实验&#xff08;思科&#xff09; 技术简介 路由引入技术在网络通信中起着重要的作用&#xff0c;能够实现不同路由协议之间的路由传递&#xff0c;并在路由引入时部署路由控制&#xff0c;实现路径或策略的控制 实验目的 不同的路由协议之…

【全网最全】2024电工杯数学建模A题21页初步参考论文+py代码+保奖思路等(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模A题21页初步参考论文py代码保奖思路等&#xff08;后续会更新成品论文&#xff09;「首先来看看目前已有的资料&#x…

yolov8seg 瑞芯微RKNN、地平线Horizon芯片部署、TensorRT部署,部署工程难度小、模型推理速度快

之前写过yolov8seg部署&#xff0c;但在实际项目中没有真正的用&#xff0c;最近在项目尝试使用yolov8seg&#xff0c;把之前的yolov8目标检测的优化给同步到yolov8seg中。 特别说明&#xff1a;如有侵权告知删除&#xff0c;谢谢。 模型和完整仿真测试代码&#xff0c;放在git…

一套车间生产管理和调度执行MES系统源码,采用springboot + vue-element+uniapp+mysql技术开发,适合二次开发项目使用。

MES系统源码&#xff0c;车间生产管理系统源码&#xff0c;商业源码&#xff0c;适合上项目 MES系统是制造企业中用于管理和监控生产过程的关键系统&#xff0c;它的核心功能包括生产调度、数据管理、计划排产管理、库存管理、质量管理、设备管理、采购管理、成本管理、项目看板…

《异常检测——从经典算法到深度学习》28 UNRAVEL ANOMALIES:基于周期与趋势分解的时间序列异常检测端到端方法

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …

TypeScript(持续更新中...)

1.TypeScript是什么&#xff1f; TypeScript是javaScript的超集。 2.使用TypeScript 1&#xff09;全局安装nodejs 2&#xff09;安装TypeScript编译器 npm i -g typescript 3.编译ts文件 //注意&#xff1a;需要在ts文件同级目录执行此命令&#xff0c;否则会报找不到…

Go语言(Golang)的开发框架

在Go语言&#xff08;Golang&#xff09;的开发中&#xff0c;有多种开发框架可供选择&#xff0c;它们各自具有不同的特点和优势。以下是一些流行的Go语言开发框架&#xff0c;选择Go语言的开发框架时&#xff0c;需要考虑项目需求、团队熟悉度、社区支持、框架性能和可维护性…

Java时间工具类(Date和LocalDateTime)

Date package com.qiangesoft.utils.date;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date;/*** 日期工具类** author lq* date 2024-05-23*/ public class DateUtil {public static final String[] C…

vue从入门到精通(四):MVVM模型

一,MVVM MVVM&#xff08;Model–view–viewmodel&#xff09;是一种软件架构模式。MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑&#xff08;数据模型&#xff09;的开发分离开来。详见MVVM 二,Vue中的MVVM Vue虽然没有完全遵循 MVVM 模型&#xff0c;但是 Vue 的设…

nacos-opera(k8s)安装问题解决

整理一些关于k8s部署nacos出现的一些恶心的问题 网上说其他说的更改数据库连接都未解决。 在用nacos-opera想安装高可用nacos时连接mysql数据库报错: 报错具体项: No DataSource set 具体就是说没找到数据源。 第一个 检查一下nacos连接数据库配置 : 第二个 检查一下数据库…

2024 电工杯高校数学建模竞赛(B题)| 平衡膳食食谱 |建模秘籍文章代码思路大全

铛铛&#xff01;小秘籍来咯&#xff01; 小秘籍团队独辟蹊径&#xff0c;运用负载均衡&#xff0c;多目标规划等强大工具&#xff0c;构建了这一题的详细解答哦&#xff01; 为大家量身打造创新解决方案。小秘籍团队&#xff0c;始终引领着建模问题求解的风潮。 抓紧小秘籍&am…

【MySQL数据库】CRUD 增 删 改 查 超详解,有这一篇就够了!

​ ​ &#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【MySQL】探秘&#xff1a;数据库世界的瑞士军刀 目录 ⚗️一.CRUD &#x1f9ea;二.新增&#xff08;Create&#xff09; &#x1f9eb;1.基本操作 &#x1f9ec;2.使用SELECT插入 &#x…