C語言read函式的那些坑

2022-08-27 00:36:12 字數 1903 閱讀 4932

今天在複習unix檔案系統,用到那個read函式,但是無意中卻掉到乙個坑里了,用了乙個多小時才找到問題根源,這裡記錄一下。

問題是這樣的:我需要使用read和write函式把鍵盤輸入的資訊複製到輸出。所以我寫了如下程式:

#include#define maxsize 10

int main(void

)

輸入」helloworld「檢測,好像沒問題。但是當我輸入」海燕高爾基在蒼茫的大海上狂風卷積「卻發現不太對了,這是個什麼操作??

helloworld

helloworld

海燕高爾基在蒼茫的大海上狂風卷積

海燕高爾基海上狂風卷

為什麼後面那個它中間有幾個字掉隊了?經過多次測試我發現,如果輸入的是英文本元就沒問題,而中文字元位元組數只要超過了那個maxsize就會出問題。我一度認為,是因為讀取一次read後,記憶體中資料對齊導致的,所以我換了幾種組合,中文加英文,但是還是有問題。於是沒辦法,只能去讀函式的原型和定義的相關描述了。函式的原型是長下面這樣的:

ssize_t read(int fd, void *buf, size_t count);

該函式每次呼叫成功返回讀取的位元組數,出錯返回-1並設定errno,如果在調read之前已到達檔案末尾,則這次read返回0。引數count是請求讀取的位元組數,讀上來的資料儲存在緩衝區buf中,同時檔案的當前讀寫位置向後移。注意這個讀寫位置和使用c標準i/o庫時的讀寫位置有可能不同,這個讀寫位置是記在核心中的,而使用c標準i/o庫時的讀寫位置是使用者空間i/o緩衝區中的位置。

為了檢測我讀取到的到底是多少個位元組,我把程式修改如下:

#include#define maxsize 10

int main(void

)

return0;

}

我很疑惑的發現:每次讀到的的確是maxsize個位元組的資料,也把這對應的資料輸出了,但是在一次迴圈後,中間還是跳過了maxsize個位元組的資料沒輸出。

海燕高爾基在蒼茫的大海上狂風卷積

10海燕高爾基

10海上狂風卷

到底是什麼導致了這一現象呢?我懷疑是字元與位元組的問題,但是說不上到底是那個函式在處理這個出問題了。所以我另外寫了乙個函式測試。

#includeint main(void

)}

我三次分別輸入 」好的\n"   "好的h"  「好hj」,其輸出如下:

好的

好好的h好h

好hj好

j

我發現,其實在讀取的時候,資料是沒有任何問題的:乙個中文佔了兩個位元組,所以第一組測試資料的兩個位元組讀了前兩個位元組 "好" 字並輸出了,但是它下乙個竟然不是讀第三個位元組和第四個位元組的 "的" 字,而是把回車讀進去並輸出了!(每次輸出都本來會輸出乙個空格,這裡共輸出了四個空格)

而第二組測試資料也是首先讀兩個位元組,輸出 "好" ,而第二次讀取的兩個位元組分別是 'h' 和 '\n' 。

第三組資料前兩個位元組讀的是 "好" ,而後兩個位元組讀的分別是 'j' 和 '\n' 。

其實到這裡,問題已經不難看出了,當我們使用read函式去讀取資料時,它會按照你提供的count去讀取count個位元組的資料,同時檔案指標後移,但是檔案指標移動並非是以位元組為單位來移動的!!!而是以字元為單位來移的。(這裡說的字元並非只是char型別的,還包括了寬字元。我把它們都叫字元)。所以這也就導致了我最開始的那個錯誤。

海燕高爾基在蒼茫的大海上狂風卷積

10海燕高爾基

10海上狂風卷

它讀取了10個位元組的資料,也就是讀到了5個中文字;然後它把檔案指標往後移動10個字元,所以在第一次呼叫read函式之後,檔案指標已經指向了第十乙個字元,也就是 "海" 字。所以下一次讀取就是從這個位置開始了。

C 函式中的那些坑

平時寫程式時,我們可能或多或少對一些用法感到朦朧,下面我對一些易困惑大家,或者易用錯的地方作點介紹。1.函式返回型別不能是陣列型別或函式型別,但可以是指向陣列或函式的指標。2.如果乙個函式永遠不會被用到,那麼可以只有宣告沒有定義。3.c 中建議用引用型別的形參代替指標。使用static關鍵字可以令區...

那些年C語言走過的坑

在c語言中,多個表示式可以用逗號分開,其中用逗號分開的表示式的值分別結算,但整個表示式的值是最後乙個表示式的值。例如 int a1,a2,b 2,c 7,d 5 a1 b,c d a2 b,c d 其中第 2,3行就是使用了逗號運算子。但是這兩個表示式表達的意思不盡相同。逗號運算的結合性是從左至右,...

Go語言的那些坑

golang的source檔案的命名和其他語言本無差別,但是golang自帶unit test,它的unit test有個小規範 所有unit test檔案都要以 test.go為結尾!所以,當你命名乙個非unit test檔案為 test.go,而且執意要編譯時,就會報錯 no buildable...