標準的輸入與輸出 2-1 字元的輸出與輸入 2-2 格式化的輸出與輸入 2-3 摘要 2-4 關鍵字 2-5 問題演練 2-6 程式實作
i 上 C 語言 這一章我們將談論標準的輸入與輸出 (standard input/output) 所謂標準的輸入與輸出, 其作用端分別為鍵盤和螢幕 除了標準的輸入與輸出外,C 語言還提供檔案的輸入與輸出, 其作用端皆為檔案, 此部份留在第十一章 : 檔案, 再加以解說 標準的輸入與輸出可分為三種 : 一為字元的輸入與輸出, 如 getchar 和 putchar 函數 ; 二為字串的輸入與輸出, 如 gets 和 puts 函數 ; 三為格式化的輸入與輸出, 如 scanf 和 printf 函數 這些函數的原型都在 stdio.h 標頭檔案中, 所以使用這些函數, 記得將它載入進來, 以便在編譯時期加以核對 本章將探討字元與格式化的輸入與輸出, 而字串的輸入與輸出留在第六章 陣列, 再加以討論 2-1 字元的輸出與輸入 getchar 函數表示從鍵盤輸入資料, 但只讀取一字元, 而 putchar 函數則顯示一字元在螢幕上 如範例 2-1a 所示, 將 getchar 函數所得到的字元, 指定給字元變數 ch, 然後利用 putchar(ch), 將 ch 變數的內容顯示在螢幕上 範例 2-1a /* ex2-1a.c */ char ch; ch = getchar(); /* 從鍵盤得到的字元, 並指定給 ch */ putchar(ch); /* 將 ch 顯示於螢幕 */ system( PAUSE ); /* 讓的畫面暫停 */ 2-2
第 2 章標準的輸入與輸出 mis m 從中, 可以知道不管輸入多少字元, 只有一個字元被指定給 ch 變數 如輸入 mis, 只有 m 被指定給 ch 讀取一個字元, 除了 getchar 函數外, 還有 getch 和 getche 這兩個函數 不同的是,getch 和 getche 這二函數是屬於非緩衝區的輸入 (unbuffered input); 而 getchar 為緩衝區的輸入 (buffered input) 緩衝區的輸入, 顧名思義乃將輸入的資料先放在緩衝區, 等待使用者按下 Enter 鍵, 才會處理輸入的動作 而非緩衝區的輸入, 則輸入的資料不放在緩衝區 ( 因為沒有給它緩衝區 ), 因此, 無需等待使用者按 Enter 鍵, 它會馬上處理輸入的動作 請參閱範例 2-1b 範例 2-1b /* ex2-1b.c */ #include <conio.h> char ch; ch = getche(); putchar(ch); yy 2-3
i 上 C 語言 為 第一個 y 是使用者鍵入的 yy 第二個 y 是由 putchar 函數輸出的 若將 ch = getche(); 改為 ch = getch(); 則為 y 此字元是由 putchar 函數產生, 而使用者輸入的字元沒有顯示出來 注意! 由於 getche 與 getch 函數的語法是宣告在 conio.h, 記得將此標頭檔載入進來 假使不想讓別人知道您鍵入的字元, 則可利用 getch 函數, 因為它不會將輸 入的字元顯示於螢幕上 千萬要小心, 在 C 語言中, 大小寫的英文字母是有所區別的, 不相信的話, 讀者可將程式中的小寫字母改為大寫, 看看其結果為何 1. 請將範例 2-1b 的 getche 函數改為 getch 函數, 並觀察有何不同 2-4
第 2 章標準的輸入與輸出 2-2 格式化的輸出與輸入 接下來, 我們來介紹格式化的輸出與輸入函數, 分別是 printf 與 scanf 函數 2-2-1 printf 函數 首先來談 printf 函數 printf 可譯為二個字 print( 印出 ) 與 format( 格式 ), 亦即為格式化的輸出 printf 函數將雙引號內的內容 ( 即字串 ) 輸出至螢幕, 如 雙引號是必備的 printf( "Hello, world."); 表示將 Hello, world. 輸出至螢幕 請參閱範例 2-2-1a 範例 2-2-1a /* ex2-2-1a.c */ printf("hello, world."); Hello, world. printf 函數將雙引號括起來的字串, 即將 Hello, world. 加以輸出 2-5
i 上 C 語言 當然也可以在字串內加一些控制字元 (control character), 這些控制字元是由反斜線 (\) 所引導出來的, 以下是幾個常用的控制字元及其功能 控制字元 功能 \n 跳行 \f 跳頁 \t 跳一個 tab( 一般為 8 格 ) \b 倒退一格 \r 跳回行首 \' 印出 ' \" 印出 " \\ 印出 \ \ooo \xhh 印出八進位 ASCII 碼 印出十六進位 ASCII 碼 其中最常用的是 \n 若要印出 Hello, everybody. Go learning C language. 則片段程式如下 : printf("hello, everybody.\n"); 跳行 printf("go learning C language."); 因為控制字元 \n 表示跳行 若將第一個 printf 函數中的 \n 刪除, 則第二個 printf 函數所輸出的字串就會緊跟著第一個字串的後面 如下所示 : Hello, everybody.go learning C language. 2-6
第 2 章標準的輸入與輸出 再來看其它的控制字元, 試問若要印出下一個字串 : learn 'C' now 由於此字串中有單引號, 所以必需使用 \' 控制字元完成之, 如下所示 : printf("learn \'C\' now"); 利用 \' 印出 ' 再次利用 \' 印出 ' 底下將以範例 2-2-1b 2-2-1c 2-2-1d 及 2-2-1e 等四個, 展示常用控制字元的功能 範例 2-2-1b /* ex2-2-1b.c */ printf("hello,"); printf("everybody.\n"); /* 使用 \n 作為跳行之用 */ printf("go learning C language."); Hello,everybody. Go learning C language. 此範例以 '\n' 控制字元達到跳行的目的 2-7
i 上 C 語言 範例 2-2-1c /* ex2-2-1c.c */ printf("perfect\t"); /* 使用 \t 跳 8 格 */ printf("pc\b"); /* 使用 \b 倒退一格 */ printf("erformance\n"); Perfect Performance 此範例以 '\t' 控制字元跳一 tab, 並利用 '\b' 倒退一個字元, 故 "PC" 的 C 字元不見了, 因為被其後輸出的 erformance 覆蓋了 範例 2-2-1d /* ex2-2-1d.c */ printf("learn \'C\' now\n"); printf("learn \"C language\" now\n"); printf("learn \\C language programming\\ now\n"); learn 'C' now learn "C language" now learn \C language programming\ now 此範例以 \' 印出單引號 ('),\" 印出雙引號 ("), 而以 \\ 印出反斜線 (\) 2-8
第 2 章標準的輸入與輸出 範例 2-2-1e /* ex2-2-1e.c */ printf("\061\062\063\n"); printf("\x61\x62\x63\n"); 123 abc 除了以 \x 為首的數字是十六進位外, 其餘的方式即是以八進位視之 \061 就是十進位的 49, 對應的 ASCII 為 1,\x61 就是十進位的 97, 對應的 ASCII 為 a 完整的 ASCII, 請參閱附錄 C 格式特定字元接下來, 若要輸出某一變數的值, 則需要一對應的格式特定字元 首先, 要看變數所屬的資料型態是什麼, 再利用此資料型態所對應的格式特定字元 (format specified character) 格式特定字元是以 % 開頭, 之後接英文字母 資料型態 對應的格式特定字元 int %d float %f double %f char %c 2-9
i 上 C 語言 假設 x 為整數型態 (int), 表示此數沒有小數點, 其初始值為 100, 則 printf("x = %d", x); 對應 %d 則印出 x = 100 如範例 2-2-1f 所示 範例 2-2-1f /* ex2-2-1f.c */ int x = 100; printf("x = %d", x); x = 100 以 %d 格式特定字元, 表示以十進位 (decimal) 的方式, 印出整數變數 x 的值 因為在雙引號內的格式特定字元, 與變數之間的關係是一對一的關係 格式特定字元的個數與變數的個數需相同, 否則會有錯誤的訊息產生 printf("x = %d, y = %f", x, y); 如變數 x 與 %d,y 與 %f 相互對應 變數之間以逗號隔開 請參閱範例 2-2-1g 2-10
第 2 章標準的輸入與輸出 範例 2-2-1g /* ex2-2-1g.c */ int x = 100; float y = 123.123; printf("x = %d, y = %f", x, y); x = 100, y = 123.123001 以格式特定字元 %d, 印出整數變數 x 的值 100; 而以格式特定字元 %f, 印出浮點數型態變數 y 的值為 123.123001, 而不是 123.123000, 這是因為在處理 float 浮點數時, 會失去一些精確度 (precision), 因為浮點數常數內定是 double 的資料型態 若將上一範例的 float 改為 double, 則 y 的為 123.123000 若想列印 "100% 橘子汁 " 的字串, 在此面臨的問題是,% 為格式特定字元的開頭字元, 為了去除此原來 % 的用意, 可用 %% 解除原意, 並印出 % 請參閱範例 2-2-1h 範例 2-2-1h /* ex2-2-1h.c */ printf("100%% 橘子汁 "); 2-11
i 上 C 語言 100% 橘子汁 因為 %% 印出 % 字元, 故 100%% 輸出為 100%, 程式的為 100% 橘子汁 除了上述的格式特定字元外, 還有下列幾個常用的格式特定字元 資料型態 int( 八進位 ) int( 十六進位 ) unsigned int 對應的格式特定字元 % o % x % u float double % e, %g %o %x 是針對整數資料型態的數值, 其中 %o 將以八進位印出, 而 %x 則以十六進位印出 %u 對應於 unsigned 資料型態, 它也適用於 int 資料型態, 而 %e 和 %g, 則適用於 float 與 double 的資料型態 請參閱範例 2-2-1i 範例 2-2-1i /* ex2-2-1i.c */ int x = 100; printf(" 以十進位的方式印出 x = %d\n", x); printf(" 以無負號的型式印出 : x = %u\n", x); printf(" 以八進位的方式印出 : x = %o\n", x); printf(" 以十六進位的方式印出 : x = %x", x); 2-12
第 2 章標準的輸入與輸出 以十進位的方式印出 x = 100 以無負號的型式印出 : x = 100 以八進位的方式印出 : x = 144 以十六進位的方式印出 : x = 64 上例以 %d %u %o %x, 分別印出變數 x 十進位的整數值 無正負號的整數值 八進位數值與十六進位數值 %e 與 %g 適用於 float 與 double 資料型態 %e 印出以科學記號表示的浮點數, 其格式為 m.dddddde±xxx 或 -m.dddddde±xxx, 如 123.4567 以科學記號表示, 即為 1.234567e+002;%g 則指當指數小於 -4 或大於等於 6 時, 以 %e 格式輸出, 否則, 以 %f 格式輸出, 並將尾端的 0 刪除 請參閱範例 2-2-1j 範例 2-2-1j /* ex2-2-1j.c */ double nf = 123.456; printf(" 以 (%%f) 格式印出 : nf = %f\n", nf); printf(" 以 (%%e) 格式印出 : nf = %e\n", nf); printf(" 以 (%%g) 格式印出 : nf = %g\n", nf); 以 (%%f) 格式印出 : nf = 123.456000 以 (%%e) 格式印出 : nf = 1.234560e+002 以 (%%g) 格式印出 : nf = 123.456 浮點數變數 nf 的值以 %f 印出, 是使用小數點表示法的 123.456000; 而以 %e 則印出科學記號表示法的 1.234560e+002; 若以 %g 輸出, 由於指數小於 6, 因此以 %f 方式輸出, 並將尾端的 0 刪除 2-13
i 上 C 語言 讓更美觀 為了使更加美觀, 我們可以指定輸出的欄位寬 (width), 即給變數多少空間, 如 %5d 表示 5 個欄位寬, 可想像我們給它 5 個空間, 如 int x = 100; printf("x = %5d\n", x); printf("x = %2d", x); 則輸出 x = ΔΔ100(Δ 表示空白 ) x = 100 由於 x = 100 只有三位數, 而現有的欄位寬為 5 位數字 (%5d), 故左邊有 2 位空白 而 %2d 欄位寬為 2, 但還是 100, 為什麼呢? 因為此時 x 數值的總位數大於欄位寬, 因此, 編譯程式認為你寫錯了, 並將欄位寬的設定設為無效, 因此, 此時 %2d 就如同以 %d 印出 一般印出的結果, 皆向右對齊, 不過我們可以利用負號加在欄位寬的前面, 使得是向左靠齊 x = 100; printf("x = %-5d", x); 則輸出 x = 100ΔΔ (Δ 表示空白 ) 不是印出 -100 使用者可利用欄位寬加上向左或向右靠齊, 對資料加以編排成一份令人賞心悅目的報表 讓我們從範例 2-2-1k 看起 2-14
第 2 章標準的輸入與輸出 範例 2-2-1k /* ex2-2-1k.c */ int x = 100; printf("x = %5d \n", x); printf("x = %2d \n", x); printf("x = %-5d \n", x); x = 100 x = 100 x = 100 變數 x 的值 100 只佔 3 個欄位寬, 若使用 %5d, 表示欄位寬為 5, 而且是向右對齊, 故輸出 100 時, 左邊會空出 2 個空白字元 ; 若以 %2d 輸出, 因欄位寬不足, 使得它以 %d 的格式輸出 ; 而 %-5d, 表示為向左對齊, 故右邊會多出 2 個空白字元 和整數一樣, 浮點數也可以使用欄位寬 % m.nf 小數點後面的位數總共的欄位寬, 包括小數點在內 如下所示 : double nf = 123.1234; printf("nf = %f \n", nf); 2-15
i 上 C 語言 為 nf = 123.123400, 印出小數點後面六位數 ( 內定 ) printf("nf = %10.2f \n", nf); 為 nf = ΔΔΔΔ123.12, 總共有 10 位寬, 且小數點後有 2 位 printf("nf = %-10.2f \n", nf); 為 nf = 123.12ΔΔΔΔ, 向左靠齊 printf("nf = %3.2f ", nf); 為 nf = 123.12, 正確的總欄位寬必須 4 位或 4 位以上, 故此時欄位寬 3 自動失效, 而以 %.2f 的格式印出, 表示對總欄位寬不加以限定 請參閱範例 2-2-1L 範例 2-2-1L /* ex2-2-1l.c */ double nf = 123.1234; printf("nf = %f \n", nf); printf("nf = %10.2f \n", nf); printf("nf = %-10.2f \n", nf); printf("nf = %3.2f \n", nf); printf("nf = %.2f \n", nf); nf = 123.123400 nf = 123.12 nf = 123.12 2-16
第 2 章標準的輸入與輸出 nf = 123.12 nf = 123.12 程式中最後一個 printf 敘述, 是以 %.2f 格式印出 nf, 此表示我們只關心要印出小數點後 2 位 此範例需要多少位欄位寬呢?nf 變數小數點前有 3 位數, 加上小數點及小數點後 2 位數, 總欄位寬至少要給予 7 位, 否則, 視為無效 以下的範例, 將展示欄位寬的好處 範例 2-2-1m /* ex2-2-1m.c */ int a=100, b=1000, c=100000, d=10; int i=20000, j=2, k=2000, x=2000000; printf("%d %d %d %d\n", a, b, c, d); printf("%d %d %d %d\n", i, j, k, x); printf("\n 以另一種方式輸出 :\n"); printf("%9d %9d %9d %9d\n", a, b, c, d); printf("%9d %9d %9d %9d\n", i, j, k, x); 2-17
i 上 C 語言 從此範例可看出, 若有指定欄位寬, 則是很美觀的 不要忘記, 是給對方的第一個印象 範例 2-2-1n /* ex2-2-1n.c */ double a=111111.1, b=111.11, c=1111.1, d=1111111.1; double i=2.2, j=222222.2, k=222.2, x=22222.2; printf("%f %f %f %f\n", a, b, c, d); printf("%f %f %f %f\n", i, j, k, x); printf("\n 以另一種方式輸出 :\n"); printf("%9.1f %9.1f %9.1f %9.1f\n", a, b, c, d); printf("%9.1f %9.1f %9.1f %9.1f\n", i, j, k, x); 若以 %9.1f 取代 %f, 則將是很整齊的 從上述兩範例程式的可看出, 欄位寬的確很重要, 我們應多多加以練習, 讓可以漂漂亮亮的 2-18
第 2 章標準的輸入與輸出 1. 試問下列程式的為何? (a) printf("hello, world\r"); printf("hi, world"); printf("\b\b\b\b\babcde"); (b) printf(" /\\\n"); printf(" / \\\n"); printf(" / \\\n"); printf(" / \\\n"); printf("+ +\n"); 2. 試問下列程式的為何? (a) double d_num = 1000000.0; printf(" 以 %%e 的格式輸出 : d_num=%e\n", d_num); printf(" 以 %%g 的格式輸出 : d_num=%g\n", d_num); 2-19
i 上 C 語言 (b) int i = 12345; printf("i = 12345\n"); printf("%%d = %d \n", i); printf("%%10d = %10d \n", i); printf("%%2d = %2d \n", i); printf("%%-10d = %-10d \n", i); (c) double f = 123.1234; printf("f = 13.1234f\n"); printf("%%7.2f = %7.2f \n", f); printf("%%10.2f = %10.2f \n", f); printf("%%-10.2f = %-10.2f \n", f); printf("%%*.2f = %*.2f \n", 10, f); printf("%%4.2f = %4.2f \n", f); 2-2-2 scanf 函數 看完 printf 輸出函數後, 接下來討論 scanf 輸入函數 scanf 函數的語法和 printf 函數相同 輸入資料時, 必需告訴編譯程式, 從鍵盤輸入的資料要存放於哪一個變數, 則需根據程式所指定的記憶體位址 (address), 就好比郵差要根據收件人的地址來送信, 若沒有收件人的地址, 這封信件將無法送達 2-20
第 2 章標準的輸入與輸出 scanf("%d", &x); 表示位址的意思 此敘述表示從鍵盤輸入一整數資料, 然後根據記憶體位址, 將它存放於 x 變數內, 因為此處是指定 x 變數的記憶體位址 請參閱範例 2-2-2a scanf 函數的語法和 printf 函數相同, 但變數要使用位址運算子 & 來取得此 變數的位址 初學者最常犯的錯誤是, 忘記在變數前面加上 & 範例 2-2-2a /* ex2-2-2a.c */ int x; printf(" 請輸入一整數 : "); scanf("%d", &x); printf(" 變數 x 的值為 %d\n", x); 請輸入一整數 : 100 變數 x 的值為 100 輸入的 100 儲存在 x 變數 當程式執行到 scanf 函數時, 將等待使用者輸入資料, 為了增加程式的友善性 (friendly), 通常在輸入資料時, 會使用 printf 函數印出提示訊息 2-21
i 上 C 語言 scanf 函數也允許同時輸入多個變數的資料 與 printf 函數一樣, 變數與格式特定字元是一對一的對應關係, 如下所示 : scanf("%d %f", &x, &y); 空白, 表示輸入的資料之間須以空白隔開 完整的程式, 請參閱範例 2-2-2a 範例 2-2-2b /* ex2-2-2b.c */ int hour, min, sec; printf(" 請輸入時分秒 : "); scanf("%d %d %d", &hour, &min, &sec); printf(" 您輸入的時間為 %d 時 %d 分 %d 秒 \n", hour, min, sec); 請輸入時分秒 : 11 22 33 您輸入的時間為 11 時 22 分 33 秒 輸入資料若為 11 22 33, 此時會將它們分別指定給 hour min sec 三個變數 當輸入 double 浮點數時要加以小心 如範例 2-2-2c 所示 範例 2-2-2c /* ex2-2-2c.c */ 2-22
第 2 章標準的輸入與輸出 int x; double y; printf(" 請輸入一整數與 double 數 : "); scanf("%d %lf", &x, &y); printf("x = %d\t y = %f", x, y); 請輸入一整數與 double 數 : 100 123.123 x = 100 y = 123.123000 程式剖析 注意!y 是 double 的浮點數, 必需以 %lf 做為輸入的格式, 否則會產生錯誤的訊息 若是 float 的浮點數, 則以 %f 做為其輸入的格式 C 語言的好處是可讓我們知道變數在何處 若要印出記憶體的位址, 只要在變數前加上 & 即可 請參閱範例 2-2-2d 範例 2-2-2d /* ex2-2-2d.c */ int y = 100; printf(" 變數 y 的值為 %d\n", y); printf(" 變數 y 所在記憶體的位址為 %x\n", &y); 變數 y 的值為 100 變數 y 所在記憶體的位址為 22ff74 2-23
i 上 C 語言 程式剖析 y 為整數變數, 並且設定其初值為 100, 從得知,y 與 &y 是不一樣的, 前者表示 y 變數的值, 後者則表示 y 變數的記憶體位址 通常印出記憶體的位址, 會以 %x 格式特定字元對應之, 其中 %x 的 x 表示十六進位 (hexadecimal) 的意思 此範例以 %d 印出 y 變數值為 100, 並以 %x 印出 y 變數在記憶體的位址為 2ff74 1. 有一程式如下 : int i; scanf("%d", i); printf("%d\n", i); 假設您輸入 100, 試問此程式的為 100 嗎? 若不是, 試問在哪一個地方有誤, 請修正並說明之 2. 以下是 Nancy 同學撰寫的程式, 由於她是第一次撰寫 C 語言的程式, 所以難免會有一些錯誤 聰明的你, 可否幫她除錯一下? #include <stdioh> int Main ( DOUBLE us, nt; scanf("%f", us); nt = ns * 32.09; printf('nt=%f\n', nt); ) 2-24
第 2 章標準的輸入與輸出 2-3 摘要 程式中最常用的敘述, 莫過於輸出與輸入 有了輸出才知道此程式是否正確, 再利用欄位寬, 使更加美觀 在輸入方面要注意的是, 要告知輸入的資料是放在那一變數的位址內, 同時也要注意,double 變數的輸入格式是 %lf, 而 float 變數的輸入格式是 %f, 但在輸出格式上, 這兩者都是 %f 若已熟練上述範例之用法, 則要恭喜你, 你已成功了一半, 繼續往前邁進吧, 加油! 2-4 關鍵字 getchar putchar getch getche 非緩衝區的輸入 (unbuffered input) 緩衝區的輸入 (buffered input) printf 控制字元 (control character) 欄位寬 (width) scanf 標準的輸入與輸出 (standard input/output) 格式特定字元 (format specified character) 2-5 問題演練 1. 假設從鍵盤輸入 computer, 試問下一程式之? 2-25
i 上 C 語言 char ch; ch = getchar(); putchar(ch); 2. 試問下一程式之? int y = 30; printf("%%d...y = %d\n", y); printf("%%i...y = %i\n", y); printf("%%o...y = %o\n", y); printf("%%x...y = %x\n", y); 3. 試問下一程式之? char c = '$'; printf("the original c is %c\n", c); printf("%%c.../%c/\n", c); printf("%%5c.../%5c/\n", c); printf("%%-5c.../%-5c/\n", c); 4. 試問下一程式之? double f = 678.90; printf("%%3.2f.../%3.2f/\n", f); 2-26
第 2 章標準的輸入與輸出 printf("%%7.2f.../%7.2f/\n", f); printf("%%7.0f.../%7.0f/\n", f); printf("%%.2f.../%.2f/\n", f); 5. 試問下一程式之? double f = 1234.56; printf("%%e.../%e/\n", f); printf("%%.0e.../%.0e/\n", f); printf("%%.1e.../%.1e/\n", f); printf("%%.3e.../%.3e/\n", f); 6. 試問下一程式之? double f1 = 0.0007; double f2 = 0.00007; double f3 = 77777777.77; printf("f1 = %g\n", f1); printf("f2 = %g\n", f2); printf("f3 = %g\n", f3); 7. 試問下一程式之? 2-27
i 上 C 語言 printf("hello, how are you?"); printf("\r"); printf("how do you do?\n"); printf("\thello, how are you?\n"); printf("\bhello, how are you?\n"); 8. 請問 \t 和 \f 這兩個控制字元各代表什麼意義? 9. 請問 \x41 \x24 及 \133 這三個控制字元其所對應 ASCII 碼的值為何? 請參考附錄 C 2-6 程式實作 1. 請撰寫一程式, 利用 scanf 函數輸入六個整數值 ( 如 1000,10,1,10000, 100), 分別儲存於 a,b,c,i,j,k 最後利用 printf 函數, 將這些變數值每三個印成一列 請比較有無加上欄位寬的 2. 請撰寫一程式, 利用 scanf 函數輸入五個 double 浮點數值 ( 如 111.111, 22.2222,3333.333,44.44444,5555.55555), 分別儲存於 a,b,c, i,j,k 最後利用 printf 函數, 將這些變數值每三個印成一列, 並取小數點二位 請比較有無加上欄位寬的 2-28