You are on page 1of 66

第三章

資料的輸出與輸入
資料的輸出
資料的輸入
可能的輸入問題與解決方法
字串的表達與使用

尊重智慧財產權,請勿任意複製或是轉載!
本章簡介
 程式的首要工作
 確保輸入的資料沒有錯誤
 對輸入的資料進行加工與處理
 轉變成有用的資訊
依照既定格式輸出這些資訊
 輸入的媒介
 程式中直接給、從檔案讀取
 從鍵盤、滑鼠或是繪圖版讀取
 資料的輸出
 螢幕、印表機
 或其他的裝置
 螢幕與鍵盤為標準輸出入裝置
 stdout (螢幕)
 stdin (鍵盤)

尊重智慧財產權,請勿任意複製或是轉載!
資料的輸出
pinrtf 格式化輸出函式
puts/putchar 字串/字元輸出函式

尊重智慧財產權,請勿任意複製或是轉載!
資料的輸出
 printf ()
 將指定的內容以指定的格式(控制格式)輸出在螢幕上。
 內容可以是 int、float、double、char(字元或是字串)
 putchar ()
 函數名稱取 put 以及 character 兩個字組成
 函式的用途是輸出字元,一次只能輸出一個字元
 不具備像 printf () 的格式化輸出功能
 puts()
 函數名稱取 put 以及 string 兩個字組成
 函式的用途是輸出字串,一次只能輸出一個字串
 不具備像 printf () 的格式化輸出功能
 這些預設都是從螢幕 stdout 輸出
3

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
printf(格式化字串[[,引數1][,引數2][,引數3]…]);
 格式化字串(format string)
 必須是字串,一定要寫在兩個雙引號 " " 之間

 可包含文字或是控制格式,如:
 %d(輸出整數)、%f(浮點數)、%c(字元),以 % 為開頭的就是控制格式
 範例
 單純的文字,如:"台北101"
 包含一個控制字元輸出,如:"你購買 %d 杯紅茶"
 包含兩個控制字元輸出,如:"購買 %d 杯可以打 %d 折"
 [[,引數1][,引數2][引數3]…]
 引數可以是變數、常數或是運算式,如:x*2-15

 中括弧 [ ] 所包含的部分代表該選項是可有可無的

 不同的引數之間一定要有逗號「,」來區隔

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
 [[,引數1][,引數2][引數3]…]
 範例
 沒有引數:printf("台北101");
 一個引數:printf("你購買 %d 杯紅茶" ,Cups);
 兩個引數:printf("購買 %d 杯可以打 %d 折",Cups, 85);
 記住以下的兩個規則:
 個數相同
 引數的個數與格式化字串中所出現的控制格式的數量要相同
 引數與引數之間一定要用逗號隔開(初學很容易忘記打逗號)
 型別與控制格式相符
 引數與控制格式的出現順序是一對一對應的,而且型別必須相
符,否則輸出的結果將無法預期

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式

 範例 77 的一杯35元
購買 10 杯,共 131267028 元
購買 1073741824 杯,可以打 0.00 折
#include <stdio.h>
#include <stdlib.h> Prog3-1.c
int main(void)
{
int iCups = 10;
char cSize = 'M'; //中杯對應的英文字母,字元以整數輸出
float fDiscount = 0.85;
printf("%d 的一杯35元\n",cSize);// 給錯輸出格式
printf("購買 %d 杯,共 %d 元\n",iCups); //漏打了一個引數
printf("購買 %d 杯,可以打 %1.2f 折\n",fDiscount,iCups); //引數不小心放錯順序
system("pause"); return(0);
}

記得是一對一的對應關係

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
%[flags] [width] [.precision] [l | h | L]type
%[輸出調整旗標] [最少輸出字元數] [.輸出位數] [資料型別修飾]輸出格式
 type(輸出格式)
 控制格式唯一必要的項目
 指定輸出的資料型態,可使用的控制字元如下

 x 與 e 有大小寫的選擇,差別在於輸出的英文字母顯示為大寫或小寫
 g (或 G) 則是根據輸入浮點數的精確度,自動選擇 e (或自動選擇 E ) 或 f
來顯示浮點數 7

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式

 範例 60 的 8 進位: 74. 16 進位: 3c 或 3C


0.003 的%f: 0.003000
0.003 的%e: 3.000000e-003, %E: 3.000000E-003
#include <stdio.h> 0.003 的%g: 0.003
#include <stdlib.h> Prog3-2.c 0.00003850168 的%f: 0.000039, %e: 3.850168e-005
0.00003850168 的%g: 3.85017e-005, %G: 3.85017E-005
int main(void)
{ e 與 f 在輸出時,預設都是顯示到小數點下第 6 位
int iN = 60;
double dV6 = 0.003; //f,e與E的6位數顯示
double dV5 = 0.00003850168; // 小數點下第 5 位才不是 0
printf("%d 的 8 進位: %o. 16 進位: %x 或 %X\n",iN,iN,iN,iN);
printf("0.003 的%%f: %f\n",dV6); //預設顯示到第6位
printf("0.003 的%%e: %e, %%E: %E\n",dV6,dV6);
printf("0.003 的%%g: %g\n",dV6); //這裡 g 選擇 f
printf("0.00003850168 的%%f: %f, %%e: %e\n",dV5,dV5);
printf("0.00003850168 的%%g: %g, %%G: %G\n",dV5,dV5);
system("pause"); return(0);
}
要輸出 % 必須使用 %% 才行,不是\%,別用錯了

 輸出結果顯示 g 選擇 f 或是 e 是以小數下第 5 位為分界點


 小數點以下前 4 位都是 0,就會選擇 e,否則會選擇 f
 在顯示位數部分,小數點下最多只顯示到第 5 位
8
 而 %% 的規則就是為顯示 % 字元。 尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
%[flags] [width] [.precision] [l | h | L]type
%[輸出調整旗標] [最少輸出字元數] [.輸出位數] [資料型別修飾]輸出格式

 flags(輸出調整旗標)
 在輸出的數值之前,顯示正(+)、負(-)符號、空白、數字0、小數點(.)、八進
位(0)或十六進位(0x、0X)符號等
 可使用的旗標與功能如下表所示:

flag 功能說明
- 輸出的數值靠左對齊。如:%-14.6f
+ 在輸出的數值前顯示該數的正(+)、負(-)符號
0 當輸出內容少於 width 欄位所指定的字元數時,不足的部分會在輸出的前面補零
空白 當輸出為正數時,前面補上一個空白(此方便對齊於負數前的負號)。
空白與 + 同時出現時,空白旗標的功能被忽略
# 與 o, x 或 X 控制字元一起使用時,數值的前面將會分別補上 0, 0x 與 0X,
其中 0 表示 8 進位,0x(0X) 表 16 進位數值
與 e, E 或 f 控制字元一起使用時,輸出的數值一定會包含小數點
與 g 或 G 控制字元一起使用時,會保留小數點後的 0 9

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
 控制輸出欄位的寬度
%[flags] [width] [.precision] [l | h | L]type
%[輸出調整旗標] [最少輸出字元數] [.輸出位數] [資料型別修飾]輸出格式

 Width(最少的輸出字元數)
 數值位數不到該字元數時,輸出時會向右對齊
 前面不足的位置會以空白填補
 輸出數值所伴隨顯示的符號都算在 width 內
 如:正負號、0x、小數點、e 或 E 等

10

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式

 控制輸出欄位的寬度
%[flags] [width] [.precision] [l | h | L]type
%[輸出調整旗標] [最少輸出字元數] [.輸出位數] [資料型別修飾]輸出格式
 .precision(指定輸出的位數)
 與不同的控制字元搭配效果如下:
 d, u, o, x, X:precision 設定一定要輸出的字元數,如果數值
位數不足該字元數時前面補 0。例如
 60 配合 %6.4d 的輸出結果為□□0060(□為空白)
 e, E, f:precision 設定在小數點之後一定要輸出的位數
 s:precision 設定字串一定要輸出的字元數。

11

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式

 控制輸出欄位的寬度
%[flags] [width] [.precision] [l | h | L]type
%[輸出調整旗標] [最少輸出字元數] [.輸出位數] [資料型別修飾]輸出格式

 l | h | L(指定的型別輸出資料)
 l(小寫 L)代表 long,h 代表 short、L 代表 long double。
 舉例來說
 變數 x 為 int 型別,當 x 以 %hd 輸出時,x 的內容會被轉
成 short 然後再輸出

12

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式
 整數的範例(開啟並執行 Prog3-3.c) Prog3-3.c

13

尊重智慧財產權,請勿任意複製或是轉載!
pinrtf 格式化輸出函式

 浮點數的範例
#include <stdio.h>
#include <stdlib.h> Prog3-4.c
int main(void)
{
double dVal = 9453.59;
printf("123456789012345\n"); //顯示定位用 123456789012345
printf("%f\n",dVal); //全部輸出,小數點下固定6位 9453.590000
printf("%e\n",dVal); //全部輸出,小數點下固定6位 9.453590e+003
printf("%4.2f\n",dVal); //4個字元,小數點下2位 9453.59
printf("%10.1f\n",dVal); //10個字元,小數點下1位 9453.6
printf("%010.3f\n",dVal); //10個字元,小數點下3位,前面補0 009453.590
printf("%15.4e\n",dVal); //15個字元,小數點下4位 9.4536e+003
printf("%+10.2f\n",dVal); //輸出正負號,10個字元,小數點下2位 +9453.59
system("pause"); return(0);
}

 硬是要去背誦這些控制方式會蠻花時間的,而且也不見得記得住
 建議先記住一些常用的
14

尊重智慧財產權,請勿任意複製或是轉載!
puts/putchar 字串/字元輸出函式
 puts 輸出字串,並在最後輸出一個換行字元('\n')
 putchar
 輸出一個字元
 若引數是數字,則根據 ASCII 編碼將該數字轉換成字元後輸出

#include <stdio.h>
Prog3-5.c
#include <stdlib.h> a
int main(void) 上一行以 putchar(cNL) 輸出換行
A
{
char cNL = '\n'; int n = 97;
putchar(n); /* 輸出變數 n 的值, a 的 ASCII 編碼是 97 */
putchar(cNL);
puts("上一行以 putchar(cNL) 輸出換行");
putchar(65); /* 輸出 65 所對應的字元*/
putchar('\n'); /* 換行*/
system("pause"); return(0);
}

15

尊重智慧財產權,請勿任意複製或是轉載!
資料的輸入
scanf 格式化輸入函式
getchar/getche/getch 字元輸入函式

16

尊重智慧財產權,請勿任意複製或是轉載!
輸入函式簡介
 scanf ()
 函式名稱取 scan 以及 format 兩個字組成
 使用者以鍵盤輸入資料後, 需按下 Enter 鍵,才會讀取資料
 getchar ()
 函式名稱取 get 以及 character 兩個字組成
 輸入資料後按下 Enter 鍵, 會讀取資料的第 1 個字元進入程式
 gets()
 函式名稱取 get 以及 string 兩個字組成
 在輸入資料後按下 Enter 鍵, 會讀取輸入的所有文字
 getch() 與 getche() 非 ANSI C 所提供的函式
 功能與 getchar 相同,無須按下 Enter 鍵,就會自動讀取第 1 個字元
 getche():輸入的字元會顯示在螢幕上
 getch() :輸入的字元不會顯示在螢幕上

17

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 scanf () 函式:格式化輸入函式
 最常用的鍵盤輸入函式
 可配合各種輸入格式控制字元, 讀取任何型別的資料

18

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 使用 scanf () 的格式如下:
scanf(格式化字串,引數1[[,引數2][,引數3]…]);
 格式化字串:scanf 只負責輸入,所以格式化字串中所存放的都是
控制字元的組合,最多加上空白或是一些符號的組合。定義如下:
%[width] [l | h | L]type
 type:與 printf 是相同
 c (字元)、d (整數)、o (8進位整數)、u (無正負號整數)、x (16進位整數)、
s (字串)、f(浮點數,float) 與 lf (浮點數, double)
 %f (浮點數,必須以 float 變數來接收)
 %lf 則是配合 double 型別變數來接收使用輸入的浮點數
 width最大可接受的字元數
 輸入的字元數如果超過 width 的設定,超過的部分會直接忽略
 l | h | L:轉換指定的型別
 l (小寫 L)代表 long,h 代表 short、L 代表 long double
 例如:%hd 必須以 short 型別變數來接收輸入的短整數
19

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 範例
 "%d" : 讀取一個整數
 "%x" : 讀取一個 16 進位數值
 "%f" : 讀取一個單倍精確度浮點數
 "%lf" : 讀取一個雙倍精確度浮點數 (double)
 “%4d” : 讀取輸入的前四個字元為輸入的整數,超過的將被忽略

20

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
scanf(格式化字串,引數1[[,引數2][,引數3]…]);

 [[,引數1][,引數2][引數3]…]:
 使用規則:引數必須是儲存變數所對應的位址(左值)
 變數前加上 & 符號,代表變數的位址(左值)
 初學者最容易在這裡忽略了 &
 使用注意事項
 引數與格式化字串中控制字元必須
 個數相同、型別與控制格式相符

21

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 範例
#include <stdio.h>
#include <stdlib.h> Prog3-6.c
int main(void)
{
int iAge, iCode; short iId1, iId2;
float fWeight; char cH;
printf("輸入性別代碼(M:男,F:女):"); scanf("%c",&cH);
printf("輸入的是: %c\n",cH);
printf("輸入年齡:"); scanf("%d",&iAge);
printf("輸入的是: %d\n",iAge); 輸入性別代碼(M:男,F:女):F
printf("輸入體重(小數點下1位):"); 輸入的是: F
scanf("%f",&fWeight); 輸入年齡:23
printf("輸入的是: %3.1f\n",fWeight); 輸入的是: 23
printf("輸入兩個整數代碼:"); 輸入體重(小數點下1位):56.5
scanf("%hd%hd",&iId1,&iId2); 輸入的是: 56.5
printf("輸入的兩個數字: %d %d\n",iId1,iId2); 輸入兩個整數代碼:1234 45678
printf("輸入身份證後四碼:"); 輸入的兩個數字: 1234 -19858
scanf("%4d",&iCode); 輸入身份證後四碼:12345
printf("輸入的四碼是: %d\n",iCode); 輸入的四碼是: 1234
system("pause"); return(0);
輸入非預設格式所能接受的
22
}
尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 再次執行 Prog3-6.c,製造以下的錯誤
 第7行
 printf("輸入性別代碼(M:男,F:女):"); scanf("%c",&cH);
 將 scanf 中的 &cH 改成 cH,產生引數格式錯誤
 第9行
 printf("輸入年齡:"); scanf("%d",&iAge);
 程式執行時,故意輸入英文字母或是浮點數來代表年齡
 製造輸入內容與所預期接受型別不符的錯誤(這種錯誤最常遇到)
 第 15 行
 scanf("%hd%hd",&iId1,&iId2);
 將 scanf 中的 &hd 改成 &d,產生格式不相符的錯誤
 上述的三種錯誤,是使用 scanf 時最容易發生的

23

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 範例 ─ 多筆資料的輸入
 格式化字串中放置多個控制格式即可
 「,」、「@」、「#」等符號也可以當成是輸入的間隔。
 應用時必須先在格式化字串中直接填入
 如:“%d#%d”,輸入的內容也同樣必須使用相對應的符號才行
 使用這樣的輸入方式,一定要有足夠的提示

#include <stdio.h> 輸入兩個整數:85 83


Prog3-7.c
#include <stdlib.h> 輸入的兩個數值是: 85 83
int main(void) 輸入兩個整數,請用逗號隔開數值:85,52
{ 輸入的兩個數值是: 85 52
int ia,ib;
printf("輸入兩個整數:"); scanf("%d%d",&ia,&ib); 逗號隔開
printf("輸入的兩個數值是: %d %d\n",ia,ib);
printf("輸入兩個整數,請用逗號隔開數值:"); scanf("%d,%d",&ia, &ib);
printf("輸入的兩個數值是: %3d %d\n",ia,ib);
system("pause"); return(0);
} 24

尊重智慧財產權,請勿任意複製或是轉載!
變數內容的交換
 範例:交換變數 x 與 y 的內容
 直接寫 x = y 是不對的
 那只是讓變數 x 的內容變成 y 而已
 交換是有技巧的,需要借用另外一個變數來達成

#include <stdio.h>
#include <stdlib.h> Prog3-8.c
int main(void)
{
int x, y, t; // t 用於交換過程的暫存
printf("輸入兩個整數 x 與 y : ");
scanf("%d%d",&x,&y);
printf("交換前: x = %d, y = %d\n",x,y);
t = x; x = y; y = t; // 交換兩個變數內容
printf("交換後: x = %d, y = %d\n",x,y); 輸入兩個整數 x 與 y : 56 23
system("pause"); return(0); 交換前: x = 56, y = 23
} 交換後: x = 23, y = 56

25

尊重智慧財產權,請勿任意複製或是轉載!
程式技術講座

 變數內容的交換
 程式設計中經常會用到的技巧。
 假設 x 與 y 是兩個變數,基本規則就是
 再宣告一個相同型別的變數 (假設變數名稱為 t)
 將 x 內容儲存到 t
 指定 y 的內容給 x (x 內容已經保留在 t 中,所以內容可以被取代)
 將 t 的內容給 y
 程式碼就是寫成:t = x; x = y; y = t;

26

尊重智慧財產權,請勿任意複製或是轉載!
scanf 格式化輸入函式
 範例 ─ 16 進位與 8 進位數值的輸入與輸出
 %x 與 %o 接收
 請輸入:16 進位數值 0x12、FF 或是 8 進位數值
0127 是否能如所預期的透過 scanf 正確的讀入

#include <stdio.h>
#include <stdlib.h> Prog3-9.c
輸入一個16進位數字:0xFa
int main(void)
fa(16進位) = 250(10進位) = 372(8進位)
{
輸入一個 8 進位數字:546
int iO,iH;
546(8進位) = 358(10進位) = 166(16進位)
printf("輸入一個16進位數字:");
scanf("%x",&iH);
printf("%x(16進位) = %d(10進位) = %o(8進位)\n",iH,iH,iH);
printf("輸入一個 8 進位數字:");
scanf("%o",&iO);
printf("%o(8進位) = %d(10進位) = %x(16進位)\n",iO,iO,iO);
system("pause"); return(0);
} 27

尊重智慧財產權,請勿任意複製或是轉載!
getchar/getche/getch 字元輸入函式

 getchar、getch 與 getche 都是字元輸入函式


 利用一個字元變數來接收所傳回的使用者輸入
 cx 為字元變數
 cx = getchar()、cx = getch() 、 cx = getche() 都讓 cx 取得
輸入的字元

28

尊重智慧財產權,請勿任意複製或是轉載!
getchar/getche/getch 字元輸入函式

 範例
#include <stdio.h>
#include <stdlib.h> Prog3-10.c
#include <conio.h>
int main(void)
{
char cx;
printf("輸入一個字元: "); cx = getchar(); 輸入一個字元: X
printf("輸入的字元是: %c\n",cx); 輸入的字元是: X
printf("輸入一個字元: "); cx = getche(); 輸入一個字元: y輸入的字元是: y
printf("輸入的字元是: %c\n",cx); 輸入一個字元: 輸入的字元是: Z
printf("輸入一個字元: "); cx = getch();
printf("輸入的字元是: %c\n",cx); 沒有顯示輸入的字元喔!執行結果
system("pause"); return(0);
}
getch 與 getche 只要一按下按鍵就會立刻處理

29

尊重智慧財產權,請勿任意複製或是轉載!
可能的輸入問題與解決方法
如何正確讀取/跳過空白字元
解決輸入緩衝區的問題--記得清除
輸入緩衝區

30

尊重智慧財產權,請勿任意複製或是轉載!
如何正確讀取/跳過空白字元
 練習一
 輸入:三個空白後再跟著200NTD
 結果:讀取到 200
 前面的空白並沒有被接受,後面的NTD也沒有被處理

#include <stdio.h>
#include <stdlib.h> Prog3-11.c
int main(void)
{
char ch = '\0'; int ix = 0;
printf("輸入一個整數:"); scanf("%d",&ix);
// printf("輸入一個字元:"); scanf("%c",&ch);
printf("ch = %d, ix = %d\n",ch,ix);
system("pause"); return(0);
}

31

尊重智慧財產權,請勿任意複製或是轉載!
如何正確讀取/跳過空白字元
 練習二:
 第 6 行前面輸入 // ,設定這一行是註解,然後刪除第 7 行前面的 //
 輸入:一個空白鍵然後再輸入一個字元 A
 結果:讀取到第一個空白字元
 練習三:
 scanf 中的 “%c” 改成 “ %c” ,也就是 % 與前面的 “ 之間多一個空白
 輸入:三個空白字元後面跟者 A
 結果:跳過前面的空白,讀取到字元 A

32

尊重智慧財產權,請勿任意複製或是轉載!
如何正確讀取/跳過空白字元
 scanf 處理空白字元的規則
 %d 格式接受整數
 自動跳過遇到數字前的空白字元

 遇到數字時就會開始讀取,遇到非數字字元時就會停止讀取

 浮點數或其他整數資料的輸入時,採用相同的處理方式

 %c 格式接受字元
 因為空白也是字元,所以 scanf 並不會跳過會直接讀取

 在控制格式前多一個空白字元
 跳過所有遇到的空白字元,就算是使用 %c 格式也是

33

尊重智慧財產權,請勿任意複製或是轉載!
解決輸入緩衝區的問題--記得清除輸入緩衝區
 認識輸入緩衝區
 暫存使用者從鍵盤 (stdin) 輸入資料的地方
 開啟並執行 Prog3-13.c
 輸入:三個空白鍵後面跟著 310NTD

34
或許你會認為起因是輸入的資料不符合規範所導致的問題,與緩衝區的功能無關
尊重智慧財產權,請勿任意複製或是轉載!
解決輸入緩衝區的問題--記得清除輸入緩衝區

 正常的輸入
#include <stdio.h>
Prog3-14.c
#include <stdlib.h>
int main(void)
{
char ch; int ix = 0;
printf("輸入一個整數:"); scanf("%d",&ix); 輸入一個整數:351
printf("輸入一個字元:"); ch = getchar(); 輸入一個字元:
printf("\nch = %d, ix = %d\n",ch, ix); ch = 10, ix = 351
system("pause"); return(0);
} LF 的 ASCII 編碼

35

尊重智慧財產權,請勿任意複製或是轉載!
解決輸入緩衝區的問題--記得清除輸入緩衝區

 問題都是發生在輸入緩衝區中還有資料殘存
 fflush 函式:清除指定區域的資料
 fflush(stdin) :清除輸入緩衝區中的資料

#include <stdio.h>
#include <stdlib.h> Prog3-15.c
void main(void)
{
char ch; int ix = 0;
printf("輸入一個整數:"); scanf("%d",&ix);
輸入一個整數:351
fflush(stdin); // 清除輸入緩衝區的內容
輸入一個字元:Z
printf("輸入一個字元:"); ch = getchar();
ch = 90, ix = 351
printf("ch = %d, ix = %d\n",ch, ix);
system("pause");
}

36

尊重智慧財產權,請勿任意複製或是轉載!
字串的表達與使用
字串的宣告
字串的輸入與輸出函式
正確讀取輸入內容的技巧
為字串設定初始值--確保輸出/入結果正確
限制最大可讀取字元數
字串的長度
37

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告

 字串是一個或是多個字元的組合
 EX: "Zing went the strings of my heart!"
 雙引號並非字串的一部份,只是告訴編譯器這是字串
 單引號所包含住的字元。EX: 'A'
 C 語言並沒有為字串的專屬型別名稱
 必須利用陣列(Array)將一群字元組合在一起成為而成為字串

38

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告
 從字元到字串
 假設要儲存 “Einstein” 這個字串,那就需要 8 個
(c1~c8) 變數來儲存
 如果能組合這些變數,而且只用一個名稱(如:cName)
就能代表這個字串,不就變得很方便
 要達成這個目標,就必須使用陣列(Array)

39

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告
 陣列
 一群具有相同資料型別的儲存空間

 宣告方式跟變數是相同的,只是必須指明需要的空間

 陣列的宣告語法

資料型別 變數名稱[大小];

 資料型別
 這裡僅考慮字串,所以資料型別就是 char
 以 char 型別所宣告的陣列又稱為字元陣列。
 變數名稱[大小]
 變數名稱就是陣列的名稱,命名的規則同變數命名
 中括弧 [ ] 是必要的,這是陣列宣告的規定
 陣列的大小一定要寫在中括弧 [ ] 內

40

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告

 範例
 char cName[9]; 這就是字元陣列,可以儲存 9 個字元
 char strX[40]; 可以儲存 40 個字元
 陣列可以看成是一個連續的記憶體單位 (memory
cell) 所組成的資料儲存方式
 陣列中的每一個記憶體單位都是一樣的大小
 以 char cName[9] 為例
 每一個記憶體單位都是 1 byte
 共佔用 9 個記憶體單位 共9個
cName

1 byte 41

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告
 回到儲存 “Einstein” 這個字串的目標
 將設定變數初始值的方法也融入陣列的宣告中
char cName[9] = "Einstein";

42

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告

 字串的宣告與使用規則
 ‘\0’ 是字串的結尾字元或稱為 null 字元(character)
 儲存 Einstein 這 8 個字母時,需要宣告 9 個位置,多要的
一個空間就是留給 null 字元
 字串本身即包含了結尾字元
 最後一個就是 null 字元,只是從程式的敘述中看不到而已
 比較字串 "A" 與字元 ‘A’
 字串 “A”包含了兩個字元 A 與 \0 ,字元 ‘A’ 就只有 A 而已
 所以字串 "Einstein" 其實包含了 9 個字元,而不是 8 個

43

尊重智慧財產權,請勿任意複製或是轉載!
字串的宣告
 字串的宣告與使用規則
 「= "字串內容";」指定字串的初始值
 例如:char strX[20] = "C與 C++";

 陣列名稱就是左值
 所以 cName 代表的意義就是左值(l-value)
 這個觀念一定要記下來,稍後在字串的輸入與輸出函式就會用到
 陣列儲存空間是從 0 開始編號
 陣列所取的連續空間(抽屜),編號是從 0 開始

 cName[0] 代表連續空間的第一個抽屜

 cName[1] 是第二個

 ...依此類推

44

尊重智慧財產權,請勿任意複製或是轉載!
字串的輸入與輸出函式
 printf 與 scanf 輸出入字串的範例
#include <stdio.h>
#include <stdlib.h> Prog3-16.c
int main(void)
{
char cName[16];
printf("輸入一段文字: ");
scanf("%s",cName); // 輸入姓名
輸入一段文字: Butterfly
printf("你輸入的是: %s\n",cName);
你輸入的是: Butterfly
system("pause");
return(0);
}

45

尊重智慧財產權,請勿任意複製或是轉載!
字串的輸入與輸出函式
 printf 與 scanf 輸出入字串的規則
 輸出字串時,使用字元陣列的名稱即可
 字元陣列的名稱就是儲存位址的起點

 printf 會從該起點往後依序的讀取字元並輸出,直到遇到 null


字元 (字串結束字元) 才會停止。
 輸入字串時,同樣使用字元陣列的名稱即可
 scanf 讀取輸入的字串時,讀入的字元同樣從該陣列的儲存
起點,依序的存入字元陣列中

46

尊重智慧財產權,請勿任意複製或是轉載!
程式技術講座

 因為指寫陣列名稱就是陣列的起始位置(左值)
 一定要記熟使用 scanf 時兩者的差異
 一般變數要加上 &
 陣列變數給名稱就行了

47

尊重智慧財產權,請勿任意複製或是轉載!
字串的輸入與輸出函式
 gets 與 puts 函式
 gets 讀取字串、 puts 輸出字串
 使用時不需給 %s 控制格式,直接使用字元陣列的名稱即可,
語法的定義如下:
puts("輸出的字串") 或 puts(字串變數名稱);
gets(字串變數名稱);

48

尊重智慧財產權,請勿任意複製或是轉載!
字串的輸入與輸出函式
 範例
#include <stdio.h>
Prog3-17.c
#include <stdlib.h>
int main(void)
{
char cAsk1[20] = "輸入你的姓名:";
字元陣列宣告設定初始值
char cAsk2[20] = "輸入你的學號:";
char cName[20];
char cId[20];
printf("%s",cAsk1);
scanf("%s",cName); // 輸入姓名 輸入你的姓名:Butterfly
fflush(stdin); 輸入你的學號:
puts(cAsk2); 123456
gets(cId); // 輸入學號 你的姓名: Butterfly 學號: 123456
printf("你的姓名: %s 學號: %s\n",cName,cId); 輸入你的姓名:Fly
printf("%s",cAsk1);
scanf("%s",cName); //瞭解字串的儲存內容
system("pause"); return(0); puts 函式在輸出字串後會動的換行
}

49

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 練習一:執行 Prog3-17.c
 在姓名處輸入:Bumble Bee
 預期「Bumble Bee」應該出現在 cName 字串中
 但 Bee 這幾個字卻不見了,印出來的文字只有 Bumble
 練習二:再次執行 Prog3-17.c
 在學號處輸入:Bumble Bee
 如預期的「Bumble Bee」確實存入字元陣列 cId 中

50

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 scanf 與 gets 在處理輸入的字串上差異:
 gets
 以 ‘\n’ 換行字元作為字串的結束字元
 輸入「Bumble Bee」,整個字串都會儲存到 cId 字元陣列中
 scanf
 以空白(space)、跳格(tab) 或換行(\n)字元作為字串的結束字元
 輸入「Bumble Bee」
 空白字元讓 scanf 停止讀取,Bumble 就是目前的輸入結果並儲存到變
數 cName 中

 字串輸入的第一個問題:輸入的內容並不如預期的都被
儲存到字串變數中

51

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 以 scanf 來輸入,但要達到 gets 的輸入效果
 必須使用 %[可接受字元] 的方式來取代 %s
 規則
 只要輸入字元沒有出現在中括弧 [ ] 中所指定的可接受字元
時,就會停止讀取
 並將之前讀到的所有字元 (當然都是可接受字元的一份子)
當成是目前所輸入的字串

52

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 範例
 scanf(“%[a-z]”,cName)
 代表的意義是小寫英文字母 a 到 z。

 輸入的字元非小寫的 a 到 z,就會停止字元的讀取
 如輸入:butterFly,則 cName 的內容將會是 butter。
 scanf(“%[0-9]”,cId)
 0-9 為可接受字元

 如輸入為:123A45,則 cId 的內容是 123


 這個方式可讓 cId 只接受數字
 scanf(“%[A-Za-z0-9 ]”,cName)
 可接受字包含大小寫英文字母、數字 0 到 9 以及空白字元
 輸入:Bumble Bee,cName 就能得到完整的輸入

53

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 修改 Prog3-17.c 的第 10 行
 scanf("%s",cName) 換成 scanf("%[A-Za-z0-9 ]",cName)
#include <stdio.h>
#include <stdlib.h> Prog3-18.c
int main(void)
{
char cAsk1[20] = "輸入你的姓名:";
char cAsk2[20] = "輸入你的學號:";
char cName[20];
char cId[20];
printf("%s",cAsk1);
scanf("%[A-Za-z0-9 ]",cName); // 輸入姓名
fflush(stdin);
puts(cAsk2);
gets(cId); // 輸入學號
printf("你的姓名: %s 學號: %s\n",cName,cId);
printf("%s",cAsk1);
scanf("%s",cName); //瞭解字串的儲存內容
system("pause");
return(0);
}
54

尊重智慧財產權,請勿任意複製或是轉載!
正確讀取輸入內容的技巧
 以 Prog3-17.c 進
行偵錯與監看

55

尊重智慧財產權,請勿任意複製或是轉載!
為字串設定初始值--確保輸出/入結果正確
 字串未設定初始值與輸入沒被接受所合併引發的問題
 執行 Prog3-18.c
 「輸入你的姓名」處,輸入「@Butterfly」

 沒有對 cName 進行字元的儲存,


也就不會有寫入 null 字元的機會
 第二個問題:字元陣列沒
有設定初始值

56

尊重智慧財產權,請勿任意複製或是轉載!
為字串設定初始值--確保輸出/入結果正確

 字元陣列設定初始值
 = ""; 就可以讓字元陣列都儲存 \0 null 字元
 例如: char cName[20] = ""; 與 char cId[20] = "";,
#include <stdio.h>
#include <stdlib.h> Prog3-19.c
int main(void)
{
char cAsk1[20] = "輸入你的姓名:";
char cAsk2[20] = "輸入你的學號:";
char cName[20] = ""; // 設定初始值為 \0 字元
char cId[20] = ""; // 設定初始值為 \0 字元
printf("%s",cAsk1); 輸入你的姓名:@Butter
scanf("%[A-Za-z0-9 ]",cName); // 輸入姓名 輸入你的學號:
12345
fflush(stdin);
你的姓名: 學號: 12345
puts(cAsk2);
gets(cId); // 輸入學號
printf("你的姓名: %s 學號: %s\n",cName,cId);
system("pause"); return(0);
} 57

尊重智慧財產權,請勿任意複製或是轉載!
限制最大可讀取字元數
 最後一個問題
 使用者輸入的字元數超過字元陣列的儲存大小
 執行 Prog3-19.c
 姓名處,請輸入超過 20 個字元
輸入你的姓名:1234567890123456789012345
輸入你的學號:
A123
你的姓名: 1234567890123456789012345 學號: A123

結果就是發生執行上的錯誤

58

尊重智慧財產權,請勿任意複製或是轉載!
限制最大可讀取字元數
 原因
 scanf 並不會幫你檢查要寫入字元陣列的字元個數
 當輸入超過所能儲存的容量時,就是造成執行錯誤
的原因
 這是程式開發非常危險也是最常見的程式語意錯誤,
程式碼違反了對記憶體的存取規則。

59

尊重智慧財產權,請勿任意複製或是轉載!
限制最大可讀取字元數
 解決的方法
 在 %s 設定所能讀取的最大字元數
 例如:%20s 就能限制最多只讀取 20 個字元
 不要忘記字串的結尾字元 \0 null 字元
 最大字元數設定成陣列大小減一,留一個空間給 null 字元

#include <stdio.h>
#include <stdlib.h> Prog3-20.c
int main(void)
{
char cAsk1[20] = "輸入你的姓名:";
char cAsk2[20] = "輸入你的學號:";
char cName[20] = ""; // 設定初始值為 \0 字元
char cId[20] = ""; // 設定初始值為 \0 字元
printf("%s",cAsk1); // 設定只能接受 19 個字元
scanf("%19[A-Za-z0-9 ]",cName); // 輸入姓名.設定只能接受 19 個字元
fflush(stdin);
puts(cAsk2);
gets(cId); // 輸入學號
printf("你的姓名: %s 學號: %s\n",cName,cId);
system("pause");
return(0); 60
}
尊重智慧財產權,請勿任意複製或是轉載!
字串的長度
 strlen() ─ 被定義在 string.h 中
 回傳所給予字串中字元的個數 (也稱為字串的長度)
 語法如下:

strlen(字串常數); 或 strlen(字元陣列名稱);

 比較 sizeof 與 strlen
 sizoef 傳回字元陣列的大小(也就是整個陣列的大小)
 strlen 傳回字串的長度(以 null 字元為計算的結束點,但不含 null 字元)

char cStr[15] = "Bumble Bee";

61

尊重智慧財產權,請勿任意複製或是轉載!
字串的長度

 如果是用 #define 定義的字串符號常數呢?


 #define MOVIE_NAME "Transformers 2"
 strlen 會傳回 14
 sizeof 傳回 15
 因為是字串,所以包含 null 字元,strlen 不會計算 null 字元
 但 sizeof 會,因為它也是成員之一

62

尊重智慧財產權,請勿任意複製或是轉載!
字串的長度

 範例
#include <stdio.h>
#include <stdlib.h> Prog3-21.c
#include <string.h>
strlen(MOVIE_NAME) = 14
#define MOVIE_NAME "Transformers 2"
sizeof(MOVIE_NAME) = 15
int main(void) strlen(cStr) = 10
{ sizeof(cStr) = 15
char cStr[15] = "Bumble Bee";
printf("strlen(MOVIE_NAME) = %d\n",strlen(MOVIE_NAME));
printf("sizeof(MOVIE_NAME) = %d\n",sizeof(MOVIE_NAME));
printf("strlen(cStr) = %d\n",strlen(cStr));
printf("sizeof(cStr) = %d\n",sizeof(cStr));
system("pause"); return(0);
}

63

尊重智慧財產權,請勿任意複製或是轉載!
程式技術講座

 strlen 與 sizeof 都是非常有用的函式


 這兩個函式在取得字串長度或是空間大小時所扮
演的重要角色
 記得使用 strlen 時,一定要載入 string.h
 程式的開頭要有 #include <string.h> 這一行

64

尊重智慧財產權,請勿任意複製或是轉載!
本章結束

65

尊重智慧財產權,請勿任意複製或是轉載!

You might also like