由於初學 C 程式設計時都在撰寫終端機程式,學習格式化字串輸出入有助於我們和程式進行互動。
用 assert
取代 printf
很多 C 程式設計的教材都會在早期就介紹格式化字串輸出,其中一個目的是將資料印在終端機上,讓學習者可以有回饋;筆者先前也是這樣做,但筆者後來很少用格式化字串輸出來檢查程式碼了。因為我們可以用 assert
敘述來取代 printf
,像是以下例子:
#include <stdio.h>
int main(void)
{
int n = 3 + 2;
printf("%d\n", n);
return 0;
}
我們要把程式執行後,再由人工觀看終端機的輸出,才能確認 n
是否符合我們的預期。但我們將程式改寫如下:
#include <assert.h>
int main(void)
{
int n = 3 + 2;
assert(n == 5);
return 0;
}
在我們這裡例子中,只要程式可以順利執行,即代表程式符合我們的預期,不需要人工逐一確認。此外,我們在閱讀程式碼時,可以清楚地知道我們檢查的標的。
不過,我們有時候還是會用到格式化字串,因此,我們接下來會介紹相關內容。
格式化輸出入的格式 (format)
我們以 printf
為例來說明 format 如何撰寫。printf
函式的宣告如下 (可參考這裡):
int printf(const char *format, ... );
在 printf
中,第一個參數是 format ,第二個以後參數的數量和型別會和 format 相關;使用格式化輸出的重點是學習撰寫 format 。
以下是一個簡例:
printf("%s %s\n", "Hello", "Michelle");
// Out: Hello Michelle
在這個例子中,format 是 "%s %s\n"
,該 format 接收兩個字串 (%s
),並在字串尾部加上一個換行符號。
以下是另一個簡例:
printf("3 + 2 = %d\n", (3 + 2));
// Out: 3 + 2 = 5
在這個例子中,format 是 "3 + 2 = %d\n"
,該 format 接收一個整數 (%d
),並在字串尾部加上一個換行符號。
基本上,format 除了塞入值的地方,其他的地方都是固定的字串,我們只要學習一些特殊的表示法即可。常見的特殊字串如下:
%d
或%i
:整數%f
:浮點數%c
:字元%s
:字串 (即字元陣列)%p
:指標位址
其他更詳細的用法可參考前述的參考資料。
標準輸出和標準錯誤
fprintf
可指定輸出的目標,其宣告如下 (可參考這裡):
int fprintf ( FILE * stream, const char * format, ... );
format 的寫法和先前的介紹相同。但 fprintf
可指定輸出標的。一個例子是將錯誤訊息輸出到標準錯誤 (standard error):
fprintf(stderr, "Something wrong\n");
命令列程式印出訊息其實有兩個標的,一個是標準輸出 (stdout
),一個是標準錯誤 (stderr
),表面上看起來兩種標的都是印在終端機上,但其實兩者是相異的標的。如果對命令列程式的輸出進行重導 (redirecting) 就會發現其差異。
以下是範例程式碼:
#include <stdio.h>
int main(void)
{
// Go to stdout.
printf("Some information\n");
// Go to stderr.
fprintf(stderr, "Something wrong\n");
return 0;
}
執行該程式:
# First run.
$ ./program
Some information
Something wrong
# Second run.
$ ./program 1>/dev/null
Something wrong
第一次執行程式時,標準輸出和標準錯誤皆有文字訊息。在第二次執行程式時,我們將標準輸出重導走,就只留下標準錯誤。對於一般訊息和錯誤訊息的輸出,應用兩種不同的標的,程式使用者才能將其分開來。
格式化輸入
除了格式化輸出,我們也會使用格式化輸入,這是為了製作在終端機互動的程式。C 語言常用 scanf
進行格式化輸入,scanf
的宣告如下 (參考這裡):
int scanf ( const char * format, ... );
由於輸入時有可能會得到錯誤的值,需檢查是否輸入成功。一個例子如下:
#include <stdio.h>
int main(void)
{
printf("Input a number: ");
int n;
if (scanf("%d", &n) == 1)
printf("You input %d\n", n);
else
fprintf(stderr, "Invalid input\n");
return 0;
}
&n
這個部分的寫法新手常會犯錯,這代表取得變數 n
的記憶體位址 (memory address)。這牽涉到指標操作,現在就當成固定的寫法即可。scanf("%d", &n) == 1
檢查 scanf
的回傳值是否為 1,在本例中,我們僅輸入一個變數,回傳 1 即代表輸入成功。
如果輸入正確,可顯示輸入的值:
$ ./program
Input a number: 36
You input 36
如果輸入錯誤,會吐出錯誤訊息:
$ ./program
Input a number: something
Invalid input