C 程式設計/流 I/O
stdio.h 標頭檔案聲明瞭大量執行檔案和裝置(如控制檯)輸入和輸出的函式。它是 C 庫中最早出現的標頭檔案之一。它宣告的函式比任何其他標準標頭檔案都多,並且由於函式背後的複雜機制,也需要更多的解釋。
輸入和輸出的裝置無關模型多年來得到了顯著的改進,並且對其成功獲得了很少的認可。FORTRAN II 在 20 世紀 60 年代被吹捧為一種機器無關的語言,但實際上在不進行一些更改的情況下,在架構之間移動 FORTRAN 程式幾乎是不可能的。在 FORTRAN II 中,您在 FORTRAN 程式碼中間的 FORTRAN 語句中直接命名您正在與之通訊的裝置。因此,您在基於磁帶的 IBM 7090 上說READ INPUT TAPE 5,但在其他機器上說READ CARD 以讀取卡片影像。FORTRAN IV 具有更通用的READ和WRITE語句,指定了邏輯單元號(LUN)而不是裝置名稱。裝置無關 I/O 的時代開始了。
外圍裝置(如印表機)仍然對它們被要求執行的操作有相當強的概念。然後,發明了外設交換實用程式來處理奇特的裝置。當陰極射線管出現時,每家控制檯製造商都以獨立的方式解決了諸如控制檯游標移動等問題,從而造成了進一步的麻煩。
Unix 正是在這種氛圍下誕生的。Unix 的開發者 Ken Thompson 和 Dennis Ritchie 應該得到讚譽,因為他們在作業系統中融入了許多巧妙的想法。他們對裝置獨立性的方法是最聰明的方法之一。
ANSI C <stdio.h> 庫基於原始的 Unix 檔案 I/O 原語,但範圍更廣,以適應不同系統之間的最小公分母。
輸入和輸出,無論是到物理裝置(如終端和磁帶驅動器),還是到結構化儲存裝置上支援的檔案,都對映到邏輯資料流中,其屬性比其各種輸入和輸出更統一。支援兩種對映形式:文字流和二進位制流。
文字流由一個或多個行組成。文字流中的一行由零個或多個字元加上一個終止換行符組成。(唯一的例外是在某些實現中,檔案的最後一行不需要終止換行符。)Unix 採用了所有文字流的標準內部格式。每一行文字都以換行符結尾。這就是任何程式在讀取文字時所期望的,也是任何程式在寫入文字時所產生的。(這是最基本的約定,如果它不滿足連線到 Unix 機器上的面向文字的外圍裝置的需求,那麼修復發生在系統的邊緣。中間的任何東西都不需要改變。)進入或離開文字流的字元字串可能需要修改以符合特定約定。這會導致輸入資料與輸出資料之間存在可能的差異。例如,在某些實現中,當空格字元在輸入中位於換行符之前時,空格字元會從輸出中刪除。一般來說,當資料僅包含可列印字元和控制字元(如水平製表符和換行符)時,文字流的輸入和輸出是相等的。
與文字流相比,二進位制流非常簡單。二進位制流是字元的有序序列,可以透明地記錄內部資料。寫入二進位制流的資料應始終等於在相同實現下讀取的資料。但是,二進位制流可能在流的末尾附加了實現定義數量的空字元。沒有其他需要考慮的約定。
Unix 中沒有任何內容阻止程式將任意 8 位二進位制程式碼寫入任何開啟的檔案,或從足夠的儲存庫中讀取它們而不進行更改。因此,Unix 消除了長期以來文字流和二進位制流之間的區別。
當 C 程式開始執行時,程式會自動開啟三個名為stdin、stdout和stderr的標準流。這些是為每個 C 程式附加的。
第一個標準流用於輸入緩衝,另外兩個用於輸出。這些流是位元組序列。
考慮以下程式
/* An example program. */
int main()
{
int var;
scanf ("%d", &var); /* use stdin for scanning an integer from keyboard. */
printf ("%d", var); /* use stdout for printing the integer that was just scanned in. */
return 0;
}
/* end program. */
預設情況下,stdin指向鍵盤,stdout和stderr指向螢幕。在 Unix 下是可能的,在其他作業系統下也可能是可能的,可以將輸入從檔案重定向或將輸出重定向到檔案或兩者兼而有之。
FILE而不是stream。<stdio.h>標頭檔案包含型別FILE的定義(通常透過typedef),它能夠處理控制流所需的所有資訊,包括其檔案位置指示器、指向關聯緩衝區(如果有)的指標、記錄是否發生讀/寫錯誤的錯誤指示器以及記錄檔案結尾是否已到達的檔案結尾指示器。
除非程式設計師正在編寫<stdio.h>及其內容的實現,否則直接訪問FILE的內容被認為是不好的做法。透過<stdio.h>中的函式提供了對FILE內容的更好訪問。可以說,FILE型別是面向物件程式設計的早期示例。
為了開啟和關閉檔案,<stdio.h> 庫提供了三個函式:fopen、freopen 和 fclose。
#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);
FILE *freopen(const char *filename, const char *mode, FILE *stream);
fopen 和 freopen 開啟由 filename 指向的字串中指定的檔案,並將其與一個流關聯。兩者都返回一個指向控制該流的物件的指標,或者如果開啟操作失敗,則返回一個空指標。錯誤和檔案結束指示器被清除,如果開啟操作失敗,則設定錯誤。freopen 與 fopen 的區別在於,如果 stream 指向的檔案已經開啟,則首先將其關閉,並且忽略任何關閉錯誤。
對於這兩個函式,mode 指向一個以以下序列之一開頭的字串(序列之後可以跟其他字元)
r open a text file for reading w truncate to zero length or create a text file for writing a append; open or create text file for writing at end-of-file rb open binary file for reading wb truncate to zero length or create a binary file for writing ab append; open or create binary file for writing at end-of-file r+ open text file for update (reading and writing) w+ truncate to zero length or create a text file for update a+ append; open or create text file for update r+b or rb+ open binary file for update (reading and writing) w+b or wb+ truncate to zero length or create a binary file for update a+b or ab+ append; open or create binary file for update
如果檔案不存在或無法讀取,則以讀取模式(mode 引數的第一個字元為 'r')開啟檔案將失敗。
以追加模式(mode 引數的第一個字元為 'a')開啟檔案會導致所有後續寫入檔案操作都被強制到當時的 檔案末尾,而不管中間是否呼叫了 fseek 函式。在某些實現中,以追加模式(上述 mode 引數列表中的第二個或第三個字元為 'b')開啟二進位制檔案可能會將流的檔案位置指示器初始定位到最後一個寫入資料之後,因為存在空字元填充。
當以更新模式(上述 mode 引數值的第二個或第三個字元為 '+')開啟檔案時,可以在關聯的流上執行輸入和輸出。但是,輸出不能直接後跟輸入,除非在中間呼叫 fflush 函式或檔案定位函式(fseek、fsetpos 或 rewind),並且輸入不能直接後跟輸出,除非在中間呼叫檔案定位函式,除非輸入操作遇到檔案結束。在某些實現中,以更新模式開啟(或建立)文字檔案可能會改為開啟(或建立)二進位制流。
當開啟時,當且僅當可以確定流不引用互動式裝置時,它才會被完全緩衝。
#include <stdio.h>
int fclose(FILE *stream);
fclose 函式會導致 stream 指向的流被重新整理,並且關聯的檔案被關閉。流的任何未寫入的緩衝資料都將傳遞到主機環境以寫入檔案;任何未讀取的緩衝資料都將被丟棄。流與檔案分離。如果關聯的緩衝區是自動分配的,則將其釋放。如果流成功關閉,則函式返回零;如果檢測到任何錯誤,則返回 EOF。
#include <stdio.h>
int fflush(FILE *stream);
如果 stream 指向輸出流或更新流,並且最近的操作不是輸入,則 fflush 函式會導致該流的任何未寫入資料被延遲到主機環境以寫入檔案。對於輸入流,fflush 的行為未定義。
如果 stream 是一個空指標,則 fflush 函式對所有行為如上所述定義的流執行此重新整理操作。
如果發生寫入錯誤,fflush 函式返回 EOF,否則返回零。
使用 fflush 函式的原因是,C 中的流可以具有緩衝輸入/輸出;也就是說,寫入檔案的函式實際上寫入 FILE 結構內部的緩衝區。如果緩衝區已滿,則寫入函式將呼叫 fflush 以實際“寫入”緩衝區中的資料到檔案。由於 fflush 只是偶爾被呼叫,因此可以最大限度地減少對作業系統的原始寫入呼叫。
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
除了不返回值之外,setbuf 函式等效於使用 _IOFBF 作為 mode 和 BUFSIZ 作為 size 呼叫 setvbuf 函式,或者(如果 buf 是一個空指標)使用 _IONBF 作為 mode 呼叫 setvbuf 函式。
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
setvbuf 函式只能在 stream 指向的流與開啟的檔案關聯之後以及在對流執行任何其他操作之前使用。引數 mode 確定流將如何被緩衝,如下所示:_IOFBF 使輸入/輸出被完全緩衝;_IOLBF 使輸入/輸出被行緩衝;_IONBF 使輸入/輸出不被緩衝。如果 buf 不是空指標,則它指向的陣列可以用來代替 setvbuf 函式關聯的緩衝區。(緩衝區的生存期必須至少與開啟的流一樣長,因此在塊退出時釋放具有自動儲存持續時間的緩衝區之前,應先關閉流。)引數 size 指定陣列的大小。陣列內容在任何時候都是不確定的。
如果成功,setvbuf 函式返回零;如果為 mode 提供了無效值或無法滿足請求,則返回非零值。
除了執行讀寫操作的函式外,stdio.h 庫還有五個影響檔案位置指示器的函式:fgetpos、fseek、fsetpos、ftell 和 rewind。
fseek 和 ftell 函式比 fgetpos 和 fsetpos 函式更早出現。
#include <stdio.h>
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, const fpos_t *pos);
fgetpos 函式將 stream 指向的流的檔案位置指示器的當前值儲存在 pos 指向的物件中。儲存的值包含 fsetpos 函式可用於將流重新定位到呼叫 fgetpos 函式時的位置的未指定資訊。
如果成功,fgetpos 函式返回零;如果失敗,則 fgetpos 函式返回非零值,並在 errno 中儲存一個實現定義的正值。
fsetpos 函式根據 pos 指向的物件的值設定 stream 指向的流的檔案位置指示器,該值應是從對同一流的先前 fgetpos 函式呼叫中獲得的值。
成功呼叫 fsetpos 函式會清除流的檔案結束指示器,並撤消 ungetc 函式對同一流的任何影響。在 fsetpos 呼叫之後,更新流上的下一個操作可以是輸入或輸出。
如果成功,fsetpos 函式返回零;如果失敗,則 fsetpos 函式返回非零值,並在 errno 中儲存一個實現定義的正值。
#include <stdio.h>
int fseek(FILE *stream, long int offset, int whence);
long int ftell(FILE *stream);
fseek 函式設定 stream 指向的流的檔案位置指示器。
對於二進位制流,新的位置(以檔案開頭字元為單位測量)是透過將offset新增到由whence指定的位置得到的。stdio.h中名為SEEK_SET、SEEK_CUR和SEEK_END的三個宏擴充套件為唯一值。如果由whence指定的位置是SEEK_SET,則指定的位置是檔案開頭;如果whence是SEEK_END,則指定的位置是檔案結尾;如果whence是SEEK_CUR,則指定的位置是當前檔案位置。二進位制流不必有意義地支援whence值為SEEK_END的fseek呼叫。
對於文字流,offset應為零,或者offset應為之前對同一流呼叫ftell函式返回的值,並且whence應為SEEK_SET。
只有在無法滿足請求時,fseek函式才會返回非零值。
ftell函式獲取由stream指向的流的檔案位置指示器的當前值。對於二進位制流,該值是從檔案開頭算起的字元數;對於文字流,其檔案位置指示器包含fseek函式可用於將流的檔案位置指示器返回到ftell呼叫時其位置的未指定資訊;這兩個返回值之間的差值不一定是寫入或讀取的字元數的有意義的度量。
如果成功,ftell函式將返回流的檔案位置指示器的當前值。如果失敗,ftell函式將返回-1L,並在errno中儲存一個實現定義的正值。
#include <stdio.h>
void rewind(FILE *stream);
rewind函式將由stream指向的流的檔案位置指示器設定為檔案開頭。它等效於
(void)fseek(stream, 0L, SEEK_SET)
但它還會清除流的錯誤指示器。
#include <stdio.h>
void clearerr(FILE *stream);
clearerr函式清除由stream指向的流的檔案結束指示器和錯誤指示器。
#include <stdio.h>
int feof(FILE *stream);
feof函式測試由stream指向的流的檔案結束指示器,當且僅當stream的檔案結束指示器已設定時返回非零值,否則返回零。
#include <stdio.h>
int ferror(FILE *stream);
ferror函式測試由stream指向的流的錯誤指示器,當且僅當stream的錯誤指示器已設定時返回非零值,否則返回零。
#include <stdio.h>
void perror(const char *s);
perror函式將整數表示式errno中的錯誤號對映到錯誤訊息。它向標準錯誤流寫入一系列字元,如下所示:首先,如果s不是空指標,並且s指向的字元不是空字元,則寫入s指向的字串,後跟冒號(:) 和一個空格;然後是一個適當的錯誤訊息字串,後跟一個換行符。錯誤訊息的內容與使用引數errno呼叫strerror函式返回的內容相同,這些內容是實現定義的。
stdio.h庫包含各種函式,除了讀取和寫入之外,還可以對檔案執行某些操作。
#include <stdio.h>
int remove(const char *filename);
remove函式會導致不再可以透過filename指向的字串指定的檔名訪問該檔案。隨後嘗試使用該檔名開啟該檔案將失敗,除非重新建立該檔案。如果檔案已開啟,則remove函式的行為是實現定義的。
如果操作成功,remove函式返回零,如果失敗則返回非零值。
#include <stdio.h>
int rename(const char *old_filename, const char *new_filename);
rename函式會導致從此以後透過new_filename指向的字串給出的名稱來識別old_filename指向的字串指定的檔名。不再可以透過該名稱訪問名為old_filename的檔案。如果在呼叫rename函式之前存在名為new_filename指向的字串的檔案,則行為是實現定義的。
如果操作成功,rename函式返回零,如果失敗則返回非零值,在這種情況下,如果檔案先前存在,則它仍然以其原始名稱識別。
#include <stdio.h>
FILE *tmpfile(void);
tmpfile函式建立一個臨時二進位制檔案,該檔案將在關閉時或程式終止時自動刪除。如果程式異常終止,則是否刪除開啟的臨時檔案是實現定義的。該檔案以"wb+"模式開啟以進行更新。
tmpfile函式返回指向其建立的檔案的流的指標。如果無法建立檔案,則tmpfile函式將返回空指標。
#include <stdio.h>
char *tmpnam(char *s);
tmpnam函式生成一個有效的檔名字串,該字串不是現有檔案的名稱。
tmpnam函式每次被呼叫時都會生成一個不同的字串,最多TMP_MAX次。(TMP_MAX是stdio.h中定義的宏。)如果它被呼叫超過TMP_MAX次,則行為是實現定義的。
實現應表現得好像沒有庫函式呼叫tmpnam函式一樣。
如果引數為空指標,則tmpnam函式將其結果保留在內部靜態物件中,並返回指向該物件的指標。隨後對tmpnam函式的呼叫可能會修改同一物件。如果引數不是空指標,則假定它指向至少L_tmpnam個字元的陣列(L_tmpnam是stdio.h中的另一個宏);tmpnam函式將其結果寫入該陣列,並返回引數作為其值。
宏TMP_MAX的值必須至少為25。
#include <stdio.h>
int fgetc(FILE *stream);
fgetc函式從由stream指向的流中獲取下一個字元(如果存在)作為轉換為int的unsigned char,並(如果已定義)推進流的相關檔案位置指示器。
fgetc函式返回由stream指向的流中的下一個字元。如果流處於檔案結尾或發生讀取錯誤,則fgetc將返回EOF(EOF是<stdio.h>中定義的負值,通常為(-1))。必須使用feof和ferror例程來區分檔案結尾和錯誤。如果發生錯誤,則全域性變數errno將設定為指示錯誤。
#include <stdio.h>
char *fgets(char *s, int n, FILE *stream);
fgets函式最多從由stream指向的流中讀取比n指定字元數少一個的字元,並將其寫入由s指向的陣列中。在換行符(保留)或檔案結尾之後,不會讀取其他字元。一個空字元將立即寫入讀取到陣列中的最後一個字元之後。
如果 fgets 函式執行成功,則返回 s。如果遇到檔案結束並且沒有字元讀入陣列,則陣列內容保持不變並返回空指標。如果操作過程中發生讀取錯誤,則陣列內容不確定並返回空指標。
警告:不同的作業系統可能使用不同的字元序列來表示行結束序列。例如,某些檔案系統在文字檔案中使用終止符 \r\n;fgets 可能會讀取這些行,刪除 \n 但保留 \r 作為 s 的最後一個字元。在使用該字串進行任何操作之前,應從字串 s 中刪除此多餘的字元(除非程式設計師不關心它)。Unix 通常使用 \n 作為其行結束序列,MS-DOS 和 Windows 使用 \r\n,Mac OS 在 OS X 之前使用 \r。許多非 Unix 或 Linux 作業系統上的編譯器在文字檔案的輸入中將換行序列對映到 \n;請檢查編譯器的文件以瞭解它在這種情況下執行的操作。
/* An example program that reads from stdin and writes to stdout */
#include <stdio.h>
#define BUFFER_SIZE 100
int main(void)
{
char buffer[BUFFER_SIZE]; /* a read buffer */
while( fgets (buffer, BUFFER_SIZE, stdin) != NULL)
{
printf("%s",buffer);
}
return 0;
}
/* end program. */
#include <stdio.h>
int getc(FILE *stream);
getc 函式等效於 fgetc,但它可能被實現為宏。如果它被實現為宏,則 stream 引數可能會被評估多次,因此該引數永遠不應該是一個有副作用的表示式(即具有賦值、增量或減量運算子,或為函式呼叫)。
getc 函式返回由 stream 指向的輸入流中的下一個字元。如果流處於檔案結束位置,則設定流的檔案結束指示符,並且 getc 返回 EOF(EOF 是在 <stdio.h> 中定義的負值,通常為 (-1))。如果發生讀取錯誤,則設定流的錯誤指示符,並且 getc 返回 EOF。
#include <stdio.h>
int getchar(void);
getchar 函式等效於引數為 stdin 的 getc。
getchar 函式返回由 stdin 指向的輸入流中的下一個字元。如果 stdin 處於檔案結束位置,則設定 stdin 的檔案結束指示符,並且 getchar 返回 EOF(EOF 是在 <stdio.h> 中定義的負值,通常為 (-1))。如果發生讀取錯誤,則設定 stdin 的錯誤指示符,並且 getchar 返回 EOF。
#include <stdio.h>
char *gets(char *s);
gets 函式從由 stdin 指向的輸入流中讀取字元到由 s 指向的陣列中,直到遇到檔案結束或讀取到換行符。任何換行符都將被丟棄,並且一個空字元將寫入讀取到陣列中的最後一個字元之後。
如果 gets 函式執行成功,則返回 s。如果遇到檔案結束並且沒有字元讀入陣列,則陣列內容保持不變並返回空指標。如果操作過程中發生讀取錯誤,則陣列內容不確定並返回空指標。
此函式和描述僅出於完整性考慮而在此處包含。如今,大多數 C 程式設計師都避免使用 gets,因為該函式無法知道程式設計師想要讀取到的緩衝區大小。
Henry Spencer 的《C 程式設計師十誡(註釋版)》中的第 5 條誡命寫道
你必須檢查所有字串(實際上是所有陣列)的陣列邊界,因為你鍵入 foo 的地方,總有一天會有人鍵入 supercalifragilisticexpialidocious。
它在註釋中提到了 gets
正如偉大的蠕蟲的事蹟所證明的那樣,這條誡命的結果是,健壯的生產軟體永遠不應該使用gets(),因為它確實是魔鬼的工具。你的介面應該始終告知你的僕人你的陣列的邊界,而那些輕視此類建議或悄悄地未能遵循此類建議的僕人,應立即被送往 Rm 之地,在那裡他們無法再對你造成任何傷害。
在 2018 年版本的 C 標準之前,gets 函式已被棄用。希望程式設計師改用 fgets 函式。
#include <stdio.h>
int ungetc(int c, FILE *stream);
ungetc 函式將由 c 指定的字元(轉換為 unsigned char)壓回由 stream 指向的輸入流中。壓回的字元將在該流上後續讀取時以其壓入的反序返回。對檔案定位函式(fseek、fsetpos 或 rewind)的成功干預呼叫(使用由 stream 指向的流)將丟棄該流的任何壓回字元。與流對應的外部儲存保持不變。
保證一個字元的壓回。如果在對同一流進行干預讀取或檔案定位操作之前,對同一流呼叫 ungetc 函式的次數過多,則操作可能會失敗。
如果 c 的值等於宏 EOF 的值,則操作失敗並且輸入流保持不變。
成功呼叫 ungetc 函式將清除流的檔案結束指示符。在讀取或丟棄所有壓回字元後,流的檔案位置指示符的值應與壓回字元之前相同。對於文字流,在成功呼叫 ungetc 函式後,其檔案位置指示符的值是不確定的,直到所有壓回字元都被讀取或丟棄。對於二進位制流,其檔案位置指示符將透過每次成功呼叫 ungetc 函式而遞減;如果其值在呼叫之前為零,則在呼叫之後是不確定的。
ungetc 函式返回轉換後壓回的字元,如果操作失敗則返回 EOF。
使用 fgetc、getc 或 getchar 時的一個錯誤是在將其與 EOF 進行比較之前將結果賦值給 char 型別的變數。以下程式碼片段展示了此錯誤,然後顯示了正確的方法(使用 int 型別)
| 錯誤 | 更正 |
|---|---|
char c;
while ((c = getchar()) != EOF)
putchar(c);
|
int c;
while ((c = getchar()) != EOF)
putchar(c);
|
考慮一個 char 型別為 8 位寬的系統,表示 256 個不同的值。getchar 可以返回 256 個可能的字元中的任何一個,它還可以返回 EOF 來指示檔案結束,總共有 257 個不同的可能返回值。
當 getchar 的結果被賦值給 char 時,char 只能表示 256 個不同的值,因此必然會丟失一些資訊——當將 257 個專案打包到 256 個槽中時,必然會發生衝突。EOF 值在轉換為 char 時,會變得與與其數值相同的 256 個字元中的任何一個無法區分。如果在檔案中找到該字元,則以上示例可能會將其誤認為是檔案結束指示符;或者,同樣糟糕的是,如果 char 型別是無符號的,那麼由於 EOF 是負數,它永遠不可能等於任何無符號 char,因此以上示例不會在檔案結束時終止。它將永遠迴圈,反覆列印將 EOF 轉換為 char 所產生的字元。
但是,如果 char 定義是有符號的(C 使預設 char 型別是有符號還是無符號的實現相關),[1]假設常用的EOF 值為 -1,則不會發生這種迴圈失敗模式。但是,根本問題仍然存在,如果 EOF 值定義在 char 型別的範圍之外,則當將其賦值給 char 時,該值會被截斷,並且將不再匹配退出迴圈所需的完整 EOF 值。另一方面,如果 EOF 在 char 的範圍內,則這保證了 EOF 和 char 值之間存在衝突。因此,無論系統型別如何定義,在測試 EOF 時都不要使用 char 型別。
在 int 和 char 大小相同的系統上(即與至少 POSIX 和 C99 標準不相容的系統),即使“良好”示例也會受到 EOF 和某些字元值無法區分的影響。處理這種情況的正確方法是在 getchar 返回 EOF 後檢查 feof 和 ferror。如果 feof 指示尚未到達檔案結束,並且 ferror 指示沒有發生錯誤,則可以假定 getchar 返回的 EOF 表示一個實際的字元。這些額外的檢查很少執行,因為大多數程式設計師假設他們的程式碼永遠不需要在這些“大 char”系統上執行。另一種方法是使用編譯時斷言以確保 UINT_MAX > UCHAR_MAX,這至少可以防止具有此類假設的程式在這樣的系統上編譯。
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread 函式將來自由 stream 指向的流中的最多 nmemb 個元素讀取到由 ptr 指向的陣列中,每個元素的大小由 size 指定。流的檔案位置指示器(如果已定義)將根據成功讀取的字元數前進。如果發生錯誤,流的檔案位置指示器的結果值是不確定的。如果讀取了部分元素,則其值是不確定的。
fread 函式返回成功讀取的元素數量,如果遇到讀取錯誤或檔案結尾,則該數量可能小於 nmemb。如果 size 或 nmemb 為零,則 fread 返回零,並且陣列的內容和流的狀態保持不變。
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
int scanf(const char *format, ...);
int sscanf(const char *s, const char *format, ...);
fscanf 函式從由 stream 指向的流中讀取輸入,在由 format 指向的字串的控制下,該字串指定了可接受的序列以及如何將它們轉換為賦值,使用後續引數作為指向接收轉換輸入的物件的指標。如果格式的引數不足,則行為未定義。如果在引數仍然存在的情況下格式已用盡,則會評估多餘的引數(一如既往),但其他情況下會被忽略。
格式應為多位元組字元序列,以其初始移位狀態開始和結束。格式由零個或多個指令組成:一個或多個空白字元;一個普通的多位元組字元(既不是%也不是空白字元);或轉換說明。每個轉換說明都以字元%開頭。在%之後,以下內容按順序出現
- 一個可選的賦值抑制字元*.
- 一個可選的非零十進位制整數,指定最大欄位寬度。
- 一個可選的h, l(ell)或L指示接收物件的尺寸。轉換說明符d, i和n應以h為字首,如果相應的引數是指向
short int的指標而不是指向int的指標,或者以l為字首,如果它是指向long int的指標。類似地,轉換說明符o, u和x應以h應以l為字首,如果相應的引數是指向unsigned short int的指標而不是unsigned int的指標,或者以e, f和g應以l應以L為字首,如果相應的引數是指向double的指標而不是指向float的指標,或者以h, l與任何其他格式說明符一起使用,則行為未定義。L或 - 指定要應用的轉換型別的字元。有效的轉換說明符如下所述。
fscanf 函式依次執行格式的每個指令。如果指令失敗,如下所述,fscanf 函式將返回。失敗被描述為輸入失敗(由於輸入字元不可用)或匹配失敗(由於輸入不合適)。
由空白字元組成的指令透過讀取輸入直到第一個非空白字元(該字元保持未讀)或直到沒有更多字元保持未讀來執行。
作為普通多位元組字元的指令透過讀取流的下一個字元來執行。如果其中一個字元與構成指令的字元不同,則指令失敗,並且不同的字元和後續字元保持未讀。
作為轉換說明的指令定義了一組匹配的輸入序列,如下所述,每個說明符都有相應的描述。轉換說明按以下步驟執行
跳過輸入空白字元(由 isspace 函式指定),除非說明包含[, c與任何其他格式說明符一起使用,則行為未定義。n說明符。(空白字元不計入指定的欄位寬度。)
從流中讀取一個輸入項,除非說明包含n說明符。輸入項定義為最長的匹配輸入字元序列,除非它超過指定的欄位寬度,在這種情況下,它是該序列中初始的長度相同的子序列。第一個字元(如果有)在輸入項之後保持未讀。如果輸入項的長度為零,則指令的執行失敗;此條件是匹配失敗,除非錯誤阻止了來自流的輸入,在這種情況下,它是輸入失敗。
除了%說明符的情況外,輸入項(或者,在%n指令的情況下,輸入字元的數量)被轉換為適合轉換說明符的型別。如果輸入項不是匹配的序列,則指令的執行失敗;此條件是匹配失敗。除非賦值抑制由*指示,否則轉換的結果將放置在 format 引數之後第一個尚未接收轉換結果的引數指向的物件中。如果此物件沒有合適的型別,或者轉換的結果無法在提供的空間中表示,則行為未定義。
以下轉換說明符有效
- d
- 匹配一個可選帶符號的十進位制整數,其格式與
strtol函式的主題序列(base引數的值為 10)的預期格式相同。相應的引數應是指向整數的指標。
- i
- 匹配一個可選帶符號的整數,其格式與
strtol函式的主題序列(base引數的值為 0)的預期格式相同。相應的引數應是指向整數的指標。
- o
- 匹配一個可選帶符號的八進位制整數,其格式與
strtoul函式的主題序列(base引數的值為 8)的預期格式相同。相應的引數應是指向無符號整數的指標。
- u
- 匹配一個可選帶符號的十進位制整數,其格式與
strtoul函式的主題序列(base引數的值為 10)的預期格式相同。相應的引數應是指向無符號整數的指標。
- x
- 匹配一個可選帶符號的十六進位制整數,其格式與
strtoul函式的主題序列(base引數的值為 16)的預期格式相同。相應的引數應是指向無符號整數的指標。
- e, f, g
- 匹配一個可選帶符號的浮點數,其格式與
strtod函式的主題字串的預期格式相同。相應的引數將是指向浮點數的指標。
- s
- 匹配一系列非空白字元。(沒有為多位元組字元做出特殊規定。)相應的引數應是指向足夠大的陣列的初始字元的指標,以接受該序列和一個終止空字元,該字元將自動新增。
- [
- 匹配一系列非空字元(沒有為多位元組字元做出特殊規定),這些字元來自一組預期字元(掃描集)。相應的引數應是指向足夠大的陣列的初始字元的指標,以接受該序列和一個終止空字元,該字元將自動新增。轉換說明符包含
format字串中的所有後續字元,直到包含匹配的右括號(])。括號之間的字元(掃描列表)構成掃描集,除非左括號後的字元是插入符號(^),在這種情況下,掃描集包含掃描列表中插入符號和右括號之間未出現的字元。如果轉換說明符以[]或[^]開頭,則右括號字元位於掃描列表中,下一個右括號字元是結束說明的匹配右括號;否則,第一個右括號字元是結束說明的字元。如果-字元位於掃描列表中,並且不是第一個字元,也不是第二個字元(其中第一個字元是^),也不是最後一個字元,則行為由實現定義。
- c
- 匹配由欄位寬度指定的字元序列(如果指令中沒有欄位寬度,則為 1)。相應的引數應是指向足夠大的陣列的初始字元的指標,以接受該序列。不會新增空字元。
- p
- 匹配一組由實現定義的序列,這組序列應與
fprintf函式的%p轉換可能產生的序列集相同。相應的引數應是指向void的指標。然後,輸入的解釋由實現定義。如果輸入項是在同一程式執行期間早些時候轉換的值,則結果指標應與該值相等;否則,%p轉換的行為未定義。
- n
- 不使用任何輸入。相應的引數應是指向整數的指標,該整數用於寫入到目前為止此
fscanf函式呼叫從輸入流中讀取的字元數。執行%n指令不會增加在fscanf函式執行完成時返回的賦值計數。
- %
- 匹配單個%;不發生轉換或賦值。完整的轉換說明應為%%.
如果轉換說明無效,則行為未定義。
轉換說明符E, G和X也有效,其行為分別與e, g和x.
相同。如果在輸入期間遇到檔案結尾,則轉換將終止。如果在讀取任何與當前指令匹配的字元(除了允許的開頭空白字元)之前發生檔案結尾,則當前指令的執行將以輸入失敗終止;否則,除非當前指令的執行以匹配失敗終止,否則後續指令(如果有)的執行將以輸入失敗終止。
如果轉換在遇到衝突的輸入字元時終止,則有問題的輸入字元將保留在輸入流中未讀取。尾隨空白(包括換行符)除非由指令匹配,否則將保留未讀取。除了透過%n指令之外,無法直接確定文字匹配和抑制賦值的成功與否。
如果在任何轉換之前發生輸入錯誤,則fscanf函式將返回宏EOF的值。否則,fscanf函式將返回已分配的輸入項數,在早期匹配失敗的情況下,該數可能少於提供的項數,甚至為零。
scanf函式等效於fscanf,只不過在scanf的引數之前插入了引數stdin。它的返回值類似於fscanf。
sscanf函式等效於fscanf,除了引數s指定要從中獲取輸入的字串,而不是從流中獲取。到達字串末尾等效於遇到fscanf函式的 檔案結束符。如果在重疊的物件之間進行復制,則行為未定義。
#include <stdio.h> int fputc(int c, FILE *stream);
fputc函式將由c指定的字元(轉換為unsigned char)寫入stream指向的流中,寫入位置由關聯的檔案位置指示器(如果已定義)指示,並適當地推進指示器。如果檔案不支援定位請求,或者如果流以追加模式開啟,則字元將追加到輸出流中。該函式返回寫入的字元,除非發生寫入錯誤,在這種情況下,流的錯誤指示器將被設定,並且fputc返回EOF。
#include <stdio.h> int fputs(const char *s, FILE *stream);
fputs函式將s指向的字串寫入stream指向的流中。不會寫入終止的空字元。如果發生寫入錯誤,則函式返回EOF,否則返回非負值。
#include <stdio.h> int putc(int c, FILE *stream);
putc函式等效於fputc,除了如果它作為宏實現,它可能會多次評估stream,因此引數永遠不應該是具有副作用的表示式。該函式返回寫入的字元,除非發生寫入錯誤,在這種情況下,流的錯誤指示器將被設定,並且函式返回EOF。
#include <stdio.h> int putchar(int c);
putchar函式等效於第二個引數為stdout的putc。它返回寫入的字元,除非發生寫入錯誤,在這種情況下,stdout的錯誤指示器將被設定,並且函式返回EOF。
#include <stdio.h> int puts(const char *s);
puts函式將s指向的字串寫入stdout指向的流中,並在輸出中追加一個換行符。不會寫入終止的空字元。如果發生寫入錯誤,則函式返回EOF;否則,返回非負值。
#include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite函式從ptr指向的陣列中,最多寫入nmemb個元素,每個元素的大小由size指定,寫入stream指向的流中。流的檔案位置指示器(如果已定義)將根據成功寫入的字元數前進。如果發生錯誤,則流的檔案位置指示器的結果值是不確定的。該函式返回成功寫入的元素數,只有在遇到寫入錯誤時,該數才會小於nmemb。
#include <stdarg.h> #include <stdio.h> int fprintf(FILE *stream, const char *format, ...); int printf(const char *format, ...); int sprintf(char *s, const char *format, ...); int vfprintf(FILE *stream, const char *format, va_list arg); int vprintf(const char *format, va_list arg); int vsprintf(char *s, const char *format, va_list arg);
注意:某些長度說明符和格式說明符是 C99 中的新增內容。在較舊的編譯器和 stdio 庫版本(遵循 C89/C90 標準)中可能無法使用這些內容。在可能的情況下,新的內容將用 (C99) 標記。
fprintf函式根據format指向的字串控制,將輸出寫入stream指向的流中,該字串指定後續引數如何轉換為輸出。如果格式的引數不足,則行為未定義。如果格式用盡而引數仍然存在,則會評估多餘的引數(一如既往),但除此之外將被忽略。當遇到格式字串的末尾時,fprintf函式返回。
格式應為多位元組字元序列,在初始移位狀態下開始和結束。格式由零個或多個指令組成:普通多位元組字元(不是%),它們會原樣複製到輸出流中;以及轉換說明,每個轉換說明都會導致獲取零個或多個後續引數,如果適用,則根據相應的轉換說明符轉換它們,然後將結果寫入輸出流。
每個轉換說明都以字元%開頭。在%之後,以下內容按順序出現
- 零個或多個標誌(按任何順序)修改轉換說明的含義。
- 可選的最小欄位寬度。如果轉換後的值的字元少於欄位寬度,則在左側(或右側,如果已給出稍後描述的左調整標誌)用空格(預設情況下)填充到欄位寬度。欄位寬度採用星號*(稍後描述)或十進位制整數的形式。(請注意,0 被視為標誌,而不是欄位寬度的開頭。)
- 可選的精度,它給出轉換出現的最小數字位數。d, i, o, u, x和X轉換,在小數點字元之後出現的數字位數。一個, 一個, e, E, f和F轉換,轉換的最大有效數字位數。g和G轉換,或從字串中寫入的最大字元數。s轉換。精度採用句點(.)後跟星號*(稍後描述)或可選的十進位制整數的形式;如果只指定句點,則精度取為零。如果與任何其他轉換說明符一起出現精度,則行為未定義。浮點數將四捨五入以適應精度;即printf("%1.1f\n", 1.19);產生1.2.
- 一個可選的長度修飾符,指定引數的大小。
- 一個轉換說明符字元,指定要應用的轉換型別。
如上所述,欄位寬度或精度或兩者都可以由星號表示。在這種情況下,int引數提供欄位寬度或精度。指定欄位寬度或精度或兩者的引數應(按該順序)出現在要轉換的引數(如果有)之前。負欄位寬度引數被視為-標誌後跟正欄位寬度。負精度引數被視為省略了精度。
標誌字元及其含義為
- -
- 轉換的結果在欄位內左對齊。(如果未指定此標誌,則右對齊。)
- +
- 帶符號轉換的結果始終以加號或減號開頭。(如果未指定此標誌,則僅在轉換負值時以符號開頭。所有負零的浮點轉換以及舍入為零的負值的轉換結果都包含減號。)
- 空格
- 如果帶符號轉換的第一個字元不是符號,或者帶符號轉換沒有產生任何字元,則在結果前面加上空格。如果空格和+標誌都出現,則忽略空格標誌。
- #
- 結果轉換為“備用形式”。對於o轉換,如果且僅當需要時,它會增加精度,以強制結果的第一個數字為零(如果值和精度均為 0,則列印單個 0)。對於x(或X)轉換,非零結果具有0x(或0X)作為字首。一個, 一個, e, E, f, F, g和G對於轉換,結果始終包含一個小數點字元,即使後面沒有數字。(通常,只有在小數點字元後面有數字時,這些轉換的結果中才會出現小數點字元。)對於g和G轉換,結果不會刪除尾隨零。對於其他轉換,行為未定義。
- 0
- 對於d, i, o, u, x, X, 一個, 一個, e, E, f, F, g和G轉換,前導零(在任何符號或基數指示之後)用於填充到欄位寬度;不執行空格填充。如果0和-標誌都出現,則0標誌將被忽略。對於d, i, o, u, x和X轉換,如果指定了精度,則0標誌將被忽略。對於其他轉換,行為未定義。
長度修飾符及其含義為
- hh
- (C99)指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
signed char或unsigned char引數(引數將根據整數提升進行提升,但其值應在列印之前轉換為signed char或unsigned char);或者後續n轉換說明符應用於指向signed char引數的指標。
- h
- 指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
short int或unsigned short int引數(引數將根據整數提升進行提升,但其值應在列印之前轉換為short int或unsigned short int);或者後續n轉換說明符應用於指向short int引數的指標。
- l(ell)
- 指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
long int或unsigned long int引數;後續n轉換說明符應用於指向long int引數的指標;(C99)後續c轉換說明符應用於wint_t引數;(C99)後續s轉換說明符應用於指向wchar_t引數的指標;或者對後續一個, 一個, e, E, f, F, g與任何其他格式說明符一起使用,則行為未定義。G轉換說明符沒有影響。
- ll(ell-ell)
- (C99)指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
long long int或unsigned long long int引數;或者後續n轉換說明符應用於指向long long int引數的指標。
- j
- (C99)指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
intmax_t或uintmax_t引數;或者後續n轉換說明符應用於指向intmax_t引數的指標。
- z
- (C99)指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
size_t或相應的帶符號整數型別引數;或者後續n轉換說明符應用於指向與size_t引數相對應的帶符號整數型別的指標。
- t
- (C99)指定後續d, i, o, u, x與任何其他格式說明符一起使用,則行為未定義。X轉換說明符應用於
ptrdiff_t或相應的無符號整數型別引數;或者後續n轉換說明符應用於指向ptrdiff_t引數的指標。
- L
- 指定後續一個, 一個, e, E, f, F, g與任何其他格式說明符一起使用,則行為未定義。G轉換說明符應用於
long double引數。
如果長度修飾符與除上述指定之外的任何轉換說明符一起出現,則行為未定義。
轉換說明符及其含義為
- d, i
int引數將轉換為帶符號十進位制,樣式為[−]dddd。精度指定要顯示的最小位數;如果要轉換的值可以用更少的位數表示,則用前導零擴充套件。預設精度為 1。將零值轉換為精度為零的結果是沒有任何字元。
- o, u, x, X
unsigned int引數將轉換為無符號八進位制(o)、無符號十進位制(u)或無符號十六進位制表示法(x或X)樣式為dddd;字母abcdef用於x轉換,字母ABCDEF用於X轉換。精度指定要顯示的最小位數;如果要轉換的值可以用更少的位數表示,則用前導零擴充套件。預設精度為 1。將零值轉換為精度為零的結果是沒有任何字元。
- f, F
- 表示(有限)浮點數的
double引數將轉換為十進位制表示法,樣式為[−]ddd.ddd,其中小數點字元後面的數字個數等於精度規範。如果缺少精度,則取為 6;如果精度為零且未指定#標誌,則不顯示小數點字元。如果出現小數點字元,則其前面至少出現一個數字。該值將四捨五入到適當的數字位數。
(C99)表示無窮大的double引數將轉換為以下樣式之一:[-]inf或[-]infinity——哪種樣式由實現定義。表示 NaN 的 double 引數將轉換為以下樣式之一:[-]nan或[-]nan(n-char-sequence)——哪種樣式以及任何n-char-sequence的含義由實現定義。該F轉換說明符產生INF, INFINITY與任何其他格式說明符一起使用,則行為未定義。NAN而不是inf, infinity與任何其他格式說明符一起使用,則行為未定義。nan,分別。(當應用於無窮大和 NaN 值時,-, +和space標誌具有其通常的含義;#和0標誌無效。)
- e, E
- 表示(有限)浮點數的
double引數將轉換為樣式[−]d.ddde±dd,其中小數點字元前有一個數字(如果引數非零,則該數字非零),並且其後的數字個數等於精度;如果缺少精度,則取為 6;如果精度為零且未指定#標誌,則不顯示小數點字元。該值將四捨五入到適當的數字位數。該E轉換說明符產生一個帶有E而不是e引入指數的數字。指數始終包含至少兩位數字,並且僅包含表示指數所需的更多位數。如果值為零,則指數為零。
(C99)表示無窮大或 NaN 的double引數將轉換為f或F轉換說明符沒有影響。
- g, G
- 表示(有限)浮點數的
double引數將轉換為樣式f或e(或在樣式F或E的情況下,在G轉換說明符的情況下),精度指定有效數字的個數。如果精度為零,則取為 1。使用的樣式取決於轉換的值;樣式e(或E)僅在由此類轉換產生的指數小於 –4 或大於或等於精度時使用。除非指定了#標誌,否則結果的小數部分將刪除尾隨零;僅當小數點字元後面有數字時,才會顯示小數點字元。
(C99)表示無窮大或 NaN 的double引數將轉換為f或F轉換說明符沒有影響。
- 一個, 一個
- (C99)表示(有限)浮點數的 double 引數將轉換為樣式[−]0xh.hhhhp±d,其中小數點字元前有一個十六進位制數字(如果引數是標準化的浮點數,則該數字非零,否則未指定)(二進位制實現可以選擇小數點字元左側的十六進位制數字,以便後續數字與 nibble [4 位] 邊界對齊。)並且其後的十六進位制數字個數等於精度;如果缺少精度且
FLT_RADIX是 2 的冪,則精度足以精確表示該值;如果缺少精度且FLT_RADIX不是 2 的冪,則精度足以區分(精度p足以區分源型別的值,如果 16p–1 > bn 其中b是FLT_RADIX,而n是源型別有效數中以b為底的數字個數。根據實現確定小數點字元左側數字的方案,較小的p可能就足夠了。)double型別的值,但可以省略尾隨零;如果精度為零且未指定#標誌,則不顯示小數點字元。字母abcdef用於一個轉換,字母ABCDEF用於一個轉換。該一個轉換說明符產生一個帶有X和P而不是x和p。指數始終包含至少一位數字,並且僅包含表示 2 的十進位制指數所需的更多位數。如果值為零,則指數為零。
表示無窮大或 NaN 的double引數將轉換為f或F轉換說明符沒有影響。
- c
- 如果沒有l長度修飾符,則
int引數將轉換為unsigned char,並寫入生成的字元。
(C99)如果l長度修飾符存在,則wint_t引數將轉換為好像由ls轉換規範(沒有精度)和一個指向包含wint_t引數的兩個元素wchar_t陣列的初始元素的指標一樣,第一個元素包含wint_t引數傳遞到lc轉換規範,第二個元素為 null 寬字元。
- s
- 如果沒有l長度修飾符存在,則引數應是指向字元型別陣列的初始元素的指標。(未對多位元組字元進行特殊規定。)寫入陣列中的字元,直至(但不包括)終止 null 字元。如果指定了精度,則最多寫入這麼多字元。如果未指定精度或大於陣列的大小,則陣列應包含一個 null 字元。
(C99)如果l長度修飾符存在,則引數應是指向wchar_t型別陣列的初始元素的指標。陣列中的寬字元將轉換為多位元組字元(每個都好像透過呼叫wcrtomb函式一樣,在第一個寬字元轉換之前,由mbstate_t物件描述的轉換狀態初始化為零)直至幷包括終止 null 寬字元。生成的多個位元組字元被寫入,直到(但不包括)終止 null 字元(位元組)。如果沒有指定精度,則陣列應包含一個 null 寬字元。如果指定了精度,則最多寫入這麼多字元(位元組)(包括任何移位序列),如果要等於精度給出的多位元組字元序列長度,函式需要訪問陣列末尾之外的一個寬字元,則陣列應包含一個 null 寬字元。在任何情況下都不會寫入部分多位元組字元。(如果多位元組字元具有狀態相關編碼,則可能會導致冗餘的移位序列。)
- p
- 引數應是指向
void的指標。指標的值將以實現定義的方式轉換為可列印字元序列。
- n
- 引數應是指向帶符號整數的指標,其中寫入此呼叫到
fprintf時迄今為止寫入輸出流的字元數。沒有轉換引數,但消耗了一個引數。如果轉換規範包含任何標誌、欄位寬度或精度,則行為未定義。
- %
- 一個%字元被寫入。沒有轉換引數。完整的轉換規範應為%%.
如果轉換規範無效,則行為未定義。如果任何引數不符合相應的轉換規範的正確型別,則行為未定義。
在任何情況下,不存在或小的欄位寬度都不會導致欄位截斷;如果轉換結果比欄位寬度寬,則欄位將擴充套件以包含轉換結果。
對於一個和一個如果FLT_RADIX是2的冪,則轉換結果會正確舍入為具有給定精度的十六進位制浮點數。
建議的做法是,如果FLT_RADIX不是2的冪,則結果應為具有給定精度的十六進位制浮點樣式中的兩個相鄰數字之一,並額外規定誤差應具有當前舍入方向的正確符號。
建議的做法是,對於e, E, f, F, g和G轉換,如果有效小數位數最多為DECIMAL_DIG,則結果應正確舍入。(對於二進位制到十進位制的轉換,結果格式的值是可以用給定格式說明符表示的數字。有效數字的數量由格式說明符確定,在定點轉換的情況下也由源值確定。)如果有效小數位數超過DECIMAL_DIG,但源值可以用DECIMAL_DIG位精確表示,則結果應為帶有尾隨零的精確表示。否則,源值由兩個相鄰的十進位制字串L < U界定,兩者都具有DECIMAL_DIG位有效數字;結果十進位制字串D的值應滿足L ≤ D ≤ U,並額外規定誤差應具有當前舍入方向的正確符號。
fprintf函式返回傳輸的字元數,如果發生輸出或編碼錯誤,則返回負值。
printf函式等效於fprintf,只不過在printf的引數之前插入了引數stdout。它返回傳輸的字元數,如果發生輸出錯誤,則返回負值。
sprintf函式等效於fprintf,除了引數s指定一個數組,生成的輸入將寫入該陣列,而不是寫入流。在寫入的字元的末尾寫入一個空字元;它不計入返回的總和。如果在重疊的物件之間進行復制,則行為未定義。該函式返回寫入陣列中的字元數,不包括終止的空字元。
vfprintf函式等效於fprintf,可變引數列表被arg替換,arg應由va_start宏(以及可能隨後的va_arg呼叫)初始化。vfprintf函式不呼叫va_end宏。該函式返回傳輸的字元數,如果發生輸出錯誤,則返回負值。
vprintf函式等效於printf,可變引數列表被arg替換,arg應由va_start宏(以及可能隨後的va_arg呼叫)初始化。vprintf函式不呼叫va_end宏。該函式返回傳輸的字元數,如果發生輸出錯誤,則返回負值。
vsprintf函式等效於sprintf,可變引數列表被arg替換,arg應由va_start宏(以及可能隨後的va_arg呼叫)初始化。vsprintf函式不呼叫va_end宏。如果在重疊的物件之間進行復制,則行為未定義。該函式返回寫入陣列中的字元數,不包括終止的空字元。
- ↑ C99 §6.2.5/15