C/簡單的輸入輸出

維基教科書,自由的教學讀本
< C

機器處理東西。我們將東西餵給機器,機器給我們吐出不同的東西。鋸子將樹木變為木板。內燃機將汽油轉化為轉動的能量。計算機也一樣。不過,計算機為我們處理的不是物質材料,而是信息。

我們把信息餵給計算機,告訴計算機如何處理這些信息,然後取得結果。那一部分我們輸入計算機的信息稱為輸入,而我們從計算機收到的信息則稱為輸出。任何地方都可以是輸入的來源。鍵盤擊打、網絡數據,抑或轉換到電信號的聲波都是輸入的例子。輸出也有眾多的形式,如顯示器上播放的視頻、終端上顯示的一段字符串,或存儲在硬盤上的數據。輸入的集合與輸出的產生統稱為輸入/輸出,或縮寫為I/O,是計算機的一項核心功能。

有趣的是,C語言並沒有內置的I/O功能,取而代之的是包含I/O函數的外部函數庫。我們能把這些函數編譯、連結到程序里。在章節開始時的示例「Hello, World!」中,我們已經用到一個輸出庫函數:printf()。你可能會想起這個函數屬於stdio.h函數庫文件。正如文件名所示,stdio.h包含標準化的I/O函數,可用於給我們的程序增加加輸入/輸出能力。本章將帶你探索其中的一些函數。


使用printf()輸出[編輯]

現在回憶之前的演示程序:

#include <stdio.h>

int main(void)
{
     printf("Hello, World!");
     return 0;
}

如果你把它編譯運行,你就能看到下方的句子顯示在屏幕上:


  Hello, World!

這項令人驚喜的成就是由 printf()函數實現的。一個函數就像一個「黑盒子」,它為你完成特定工作,而不顯示自身的內部結構。我們也可以藉助C語言寫自己的函數,但那就是後話了。

你已經看到用printf()可以輸出括號中雙引號里的文本。我們稱包含在引號中的文本為字面字符串(簡稱字符串),那個字符串則是printf的一個參數

在此澄清一下:有時為了方便起見,我們在一個函數名稱後寫一對圓括號,提醒我們這確實是一個函數。然而,在大家都知道討論的對象是函數時,這並不是必需的。

正如上文的例子所言,用printf()就像打一段文字、兩端加雙引號一樣簡單(注意這是雙引號,而不是兩個單引號)。也就是說,你可以把任何字符串作為printf()的參數打印出來:


  printf("This sentence will print out exactly as you see it...");

而一旦它被包含在合適的main()函數中,它將會顯示:


  This sentence will print out exactly as you see it...


打印數值和轉義字符[編輯]

佔位符[編輯]

printf()是一個強大的函數,很可能也是C程序中最常用的函數。

接下來我們用一個問題做例子。假如我們要計算:19 + 31。讓我們用C語言求出答案。

現在我們就開始寫

#include "stdio.h" // this is important, since printf
                   // can't be used without this header

int main(void)
{
    printf("19+31 is");

但是卡在這裏了!printf()只能打印字符串!幸好printf有輸出數值的方法。我們要做的就是在字符串中放一個佔位格式控制符。這樣寫:

    printf("19+31 is %d", 19+31);

顧名思義,佔位符%d給19與31相加得到的具體數值「佔了位置」。

這些佔位符稱為格式說明符。還有很多格式說明符可以與printf()使用。如果我們有一個單精度浮點數,我們可以用%f完整輸出。其他的格式說明符有:

  • %d - 整型 (同%i)
  • %ld - 長整型 (同%li)
  • %f - 單精度浮點型
  • %lf , %g - 雙精度浮點型[1]
  • %c - 字符型
  • %s - 字符串型
  • %x - 十六進制型

維基百科上printf()全部格式說明符的列表。

Tabs和換行[編輯]

假使我們想輸出這樣的文本:

   1905 
  312 +
  -----

printf()不會在每個語句結尾輸出換行符:我們必須手動操作。可是怎樣做呢?

我們能做的是使用換行轉義字符。轉義字符是一種特殊字符,我們能寫出來,但它會在屏幕上做某些特殊的事情,如發出蜂鳴聲、寫一個tab字符等。要想寫一個換行符,可以使用\n。所有轉義字符以反斜線開始。

因此,想要實現上述的輸出,應該這樣寫:

    printf(" 1905\n312 +\n-----\n");

為了使代碼更清晰,我們將這個很長的printf語句分割成幾行。然後我們的程序會變成這樣:

#include <stdio.h>

int main(void)
{
    printf(" 1905\n");
    printf("312 +\n");
    printf("-----\n");
    printf("%d", 1905+312);
    return 0;
}

此外還有很多其他轉義字符可用。另一個常見的做法是用\t寫一個tab。你還可以用\a讓電腦的蜂鳴器發出響聲,不過你不應該在自己的程序里過多地使用它,因為聲音的過度使用對用戶的感官不很友好。


其他輸出方式[編輯]

puts()[編輯]

在不考慮任何佔位符或變量時,puts()函數是一種在屏幕上輸出字符串的非常簡單的方法。它和我們在"Hello, World!"這個例子裏看到的printf()函數很是相像:

    puts("Print this string.");

輸出到屏幕的是:

  Print this string.

緊接着是一個換行符(前文已經說到過)。(puts函數會在輸出的結尾加一個換行符。)

使用scanf()輸入[編輯]

scanf()函數是與printf()輸出函數等價的輸入方法——它簡單但強大。在最簡單的調用中,scanf的格式字符串包含一個佔位符,表示用戶將要輸入值的類型。這些佔位符大抵與printf()函數相同——%d表示整數,%f表示單精度浮點數,%lf表示雙精度浮點數。

然而,與printf()相比,scanf()有一點不同。scanf()函數要求你將要存儲輸入值的變量內存地址。 雖然這裏可以使用指針(存儲內存地址的變量),這畢竟是一個之後才會涉及的概念。相反,較為簡單的技巧是使用取地址運算符,即&。在我們討論指針之前,現在我們最好先把它看作"魔法"。

下面是一個典型的用例:

#include "stdio.h"

int main(void)
{
    int a;

    printf("Please input an integer value: ");
    scanf("%d", &a);
    printf("You entered: %d\n", a);

    return 0;
}

如果你要描述上面scanf()函數產生的作用,它可以讀作:「讀取用戶輸入的整數並存儲到變量a所在的地址」。

假如你要用scanf輸入一個字符串,你應該包含&運算符。下面的代碼會產生運行時錯誤,很可能導致程序崩潰。

    scanf("%s", &a);

正確的用法應該是:

    scanf("%s", a);

這是因為你在使用字符串(%s)格式說明符時,你用來存儲值的變量是一個數組,數組名稱(在這個例子中是a)本身指向它的基地址,因此不需要取地址運算符。

注意,使用scanf()從用戶鍵盤上獲取數據可能讓你的代碼易受緩衝區溢出攻擊,如果你不很謹慎,則可能導致其他不可預測的行為。考慮使用 fgets()而不是scanf()

參考資料[編輯]

  1. 實際上%f也能輸出double型數據,但輸入時%f的使用則不同。更多細節請見維基百科上對C語言數據類型的介紹