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

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
任何一個進程,在啟動的時候都會默認打開三個文件:
- ? ? ? ? 標準輸入 -- 設備文件 -> 鍵盤文件 0
- ? ? ? ? 標準輸出 -- 設備文件 -> 顯示器文件 1
- ? ? ? ? 標準錯誤 -- 設備文件 -> 顯示器文件 2
? ? ? ? 文件描述符也是open對應的返回值,用戶創建的文件返回的值是從3開始,因為0,1,2被默認打開的文件所占用了。fd的本質是數組下標。fd分配的原則是最小的 沒有被使用的數組元素分配給新文件。?
? ? ? ? 在進程中,每個進程都擁有task_struct進程控制塊, task_struct里面有個指針files_struct結構體該結構體里面維護一個fd 結構體, 用于指向一張表files_struct, 該表包含了一個指針數組,而這數組里每個指針指向一個內存中的文件struct file,從而實現了進程與文件的連接,而文件描述符就是該數組的下標,只要知道文件描述符就可以找到內存中的文件。內核里面file結構體對應的是具體的文件,他的讀寫位置有一個變量f_pos保存讀寫位置。
?
文件流指針和文件描述符的區別
????????文件流指針是高級文件操作接口的表示。在許多高級編程語言和庫中使用文件流指針來管理文件的讀寫操作。文件流指針封裝了文件的緩沖區管理、字符編碼處理等細節,使文件操作更加方便和安全。在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); ==== fgetcint 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); 等價于 fputcint 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博客