程式設計
第2章 型別、運算子、運算式
Types, Operators, and Expressions
蘇維宗 (Wei-Tsung Su)
suwt@au.edu.tw
564D
https://reurl.cc/DyWZQd
目標
2
2-1
變數與型別
專業的程式設計師會依據需求選擇適當的型別
3
變數、型別、與物件
在程式語言中,變數(variable)是用來指向不同型別(type)且可以改變的資料物件(object)。依據型別的特性可以將程式語言分類
4
變數(Variables)
C語言屬於靜態型別語言,所以定義變數時必須指定型別(type),例如:
char 字元(或8位元的整數) character (例如,'C'、'S'、'I'、'E') �int 整數 integer (例如,125、–50)�short 短整數 (例如,25、–32)�long 長整數 (例如,36、–100)�float 單精準度實數 (例如,12.0、–365.25、0.625)�double 雙精準度實數 (例如,–12.0、365.25、0.625)
5
定義變數
定義變數時要給定變數的(1)型別與(2)變數名稱,例如
6
提醒:有意義的變數命名有助於程式的可讀性。
格式化輸出/輸入型別指定符號(Specifiers)
使用格式化輸出(printf)與輸入(scanf)時須注意對應的型別指定符號
char 字元(或8位元的整數) character %c�int 整數 integer %d�short 短整數 %d�long 長整數 %d�float 單精準度實數 %f�double 雙精準度實數 %f
注意!沒有使用對應的型別指定符號就無法正確輸出/輸入變數!
7
Practice
8
格式化輸出/輸入變數
9
提醒:有意義的變數命名有助於程式的可讀性。
如何得知型別(或變數)佔用多少記憶體空間?
所有在程式中定義的變數對會佔用記憶體。不同型別的變數會佔用的記憶體空間不同,使用sizeof()運算子可以取得不同型別(或變數)所佔用位元組(byte)的記憶體空間。
10
11
定義正整數
透過unsigned修飾字可定義正整數(>=0),例如:
unsigned char num1 = 255; // 10進位數字� unsigned int num2 = 0xFF; // 16進位數字� unsigned short num3 = 0377; // 8進位數字� unsigned int num4 = 2.55e2; // 科學記號表示� unsigned long num5 = 4294967295;
12
*如何更精確的使用整數型別?
因為int,long等變數佔用的記憶體會隨著機器而不同,所以用起來有一些不確定性。這時候可以考慮使用標準整數型態函式庫(stdint)。
13
溢位(Overflow)
電腦運算後的結果無法以所配置的記憶體空間儲存時會發生溢位。
14
Review Questions?
15
實數(遵循IEEE 754浮點數運算標準)
單精準度(32位元)�float num1 = 15.7;
雙精準度(64位元)�double num2 = 0.001;
注意! 實數無法使用unsigned修飾字
16
字元與字串
字元(character)�char class = 'B';
字串(string)�char name[] = "Bob";
請撰寫程式取得class與name變數分別所占用的記憶體空間大小?
17
name[0] | name[1] | name[2] | name[3] |
B | o | b | \0 |
'\0'為字串結束字元
printf("%s", name); //列印到'\0'為止
*字串的輸入與輸出
18
問題:
怎麼用目前學會的程式技巧將輸出
Hello, <name>
改成
Hi, <name>
型別轉換(Type Conversion)
C語言屬於弱型別語言,所以變數的型別是可以被改變的
19
型別轉換(透過函式庫)
例如,如何將字串"123"轉成整數123?�在標準函式庫(stdlib)中有提供atoi函式可以將字串轉成整數。
int atoi(const char *str);
20
強制轉型
C語言屬於也支援強制轉型的語法
(型別)運算式
例如,下面的程式碼可以將int變數強制轉型成float整數
21
變數的可視範圍(Scope)
22
變數的可視範圍
變數的可視範圍(scope)是能夠存取該變數的程式區間。
依據可視範圍的不同,變數主要可分為三大類
23
全域變數
定義在所有函式(包含main)之外的變數,其可視範圍是
從 變數定義之後
到 整個檔案結束
24
主函式區域變數
定義在單一函式(如主函式main)之內的變數,其可視範圍是
從 變數定義之後
到 整個函式結束
25
副函式區域變數
定義在單一函式(如副函式power)之內的變數,其可視範圍是
從 變數定義之後
到 整個函式結束
26
區塊變數
定義在單一程式區塊之內的變數,其可視範圍是
從 變數定義之後
到 整個程式區塊結束
27
相同名稱的變數
同一個可視範圍中不可定義相同名稱的變數。
在不同可視範園中允許定義相同名稱的變數,但其優先順序為
區塊變數 > 區域變數 > 全域變數
28
Practice
29
變數的修飾字
30
變數修飾字:const
在變數定義前加上const修飾字代表此變數無法再被改變。
例如:
請問編譯器出現的錯誤是什麼意思?
31
變數修飾字:static
在變數定義前加上static修飾字代表此變數會一直存在記憶體中直到程式結束為止。
32
第一次呼叫static_count()後會在整個程式結束之前將num變數(初始值為0)保存在記憶體中。
因此,當再次呼叫static_count()時,會繼續使用已經存在記憶體中的num變數。
2-2
運算子(Operator)
33
運算子的種類
34
算術運算子
回傳對運算元進行算術運算後的結果,例如� int num1 = 5, num2 = 2;
+ 加 printf("%d\n", num1 + num2); // ?�- 減 printf("%d\n", num1 - num2); // ?�* 乘 printf("%d\n", num1 * num2); // ?�/ 除 printf("%f\n", (float)num1 / num2); // ?�% 模除 printf("%d\n", num1 % num2); // ?
35
練習:計算王
隨機出一題加法運算,給定被加數與答案,請使用者輸入加數。如果使用者答對輸出YES,否則輸出NO. ANS=[答案]
如何隨機產生數字?
#include<math.h>
#include<time.h>
...
//以時間為種子產生亂數
srand(time(NULL));
//產生0 ~ 99的亂數
int num = rand() % 100;
輸入 輸出
50+B=100� B=50 YES
55+B=30� B=-20 NO. ANS=-25
藍字部分為使用者輸入
36
指派運算子
將運算子右邊運算式的結果指派給運算子左邊的運算元,例如� int32_t num1 = 5, num2 = 2;
= 指派 num1 = num2; // num1 = ? �+= 加指派 num1 += num2; // ≡ num1 = num1 + num2 ?�-= 減指派 num1 -= num2; // ≡ num1 = num1 - num2 ?�*= 乘指派 num1 *= num2; // ≡ num1 = num1 * num2 ?�/= 除指派 num1 /= num2; // ≡ num1 = num1 / num2 ?�%= 模除指派 num1 %= num2; // ≡ num1 = num1 % num2 ?
37
遞增(減)運算子
直接對運算元加1(減1),例如� int32_t num = 5;
++ 遞增 num++; // ≡ num = num + 1 = ?� ++num; // ≡ num = num + 1 = ?�-- 遞減 num--; // ≡ num = num - 1 = ?� --num; // ≡ num = num - 1 = ?
問題:num++與++num有什麼不同?
38
練習:計算兩整數間所有整數的總和
試寫一個程式,輸入兩個整數,並計算兩整數間所有整數的總和。
輸入 輸出
10 100 5005
10 1 55
39
位元運算子
回傳對運算元進行位元運算後的結果,例如� char pat1 = 0x11, pat2 = 0x21;
<< 左移運算 char pat = pat1 << 1; // pat = ? �>> 右移運算 char pat = pat1 >> 2; // pat = ?�& AND運算 char pat = pat1 & pat2; // pat = ?�| OR運算 char pat = pat1 | pat2; // pat = ?�^ XOR運算 char pat = pat1 ^ pat2; // pat = ?�~ NOT運算 char pat = ~pat1; // pat = ?
40
練習:二補數
輸入一個8位元的整數(-128 ~ 127),輸出對應與經過二補數運算後的16進制數值
何謂二補數?
Bit pattern 00000001
1補數 11111110
2補數 11111111
輸入 輸出
1 0x01 0xff
127 0x7f 0x81
41
關係運算子
回傳比較運算元之關係後的結果(0為假,1為真),例如� int32_t num1 = 5, num2 = 2;
< 小於 uint8_t isT = (num1 < num2); // isT = ?�<= 小於等於 uint8_t isT = (num1 <= num2); // isT = ? �> 大於 uint8_t isT = (num1 > num2); // isT = ?�>= 大於等於 uint8_t isT = (num1 >= num2); // isT = ?�== 等於 uint8_t isT = (num1 == num2); // isT = ?�!= 不等於 uint8_t isT = (num1 != num2); // isT = ?
42
邏輯運算子
回傳運算元(0為假,非0為真)經過邏輯運算後的結果(0為假,1為真),例如� int num1=5, num2=0;
&& AND unsigned char isT = (num1 && num2); // isT = ?
|| OR unsigned char isT = (num1 || num2); // isT = ?
! NOT unsigned char isT = !num1; // isT = ?� unsigned char isT = !(num1 && num2); // isT = ?
43
複雜的邏輯判斷
透過結合關係運算子與邏輯運算子就可以組合出複雜的邏輯判斷。例如,
"如果成績介於55~ 60分之間,則自動將成績改為60"
44
複雜的邏輯判斷(續)
透過結合關係運算子與邏輯運算子就可以組合出複雜的邏輯判斷。例如,
"如果(成績介於55~ 60分之間)或(成績介於50~ 54分之間且作業繳交數量超過10個),則自動將成績改為60"
45
練習:計算1~N內能被2跟3整除,但不能被12整除的整數總和
撰寫一個程式,輸入一正整數 N , 找出 1 ~ N 的整數裡,可以被 2 與 3 整除,但不能被 12 整除的整數,並將這些數字做加總。
輸入 輸出
20 24
80 294
60 150
46
練習:找大小
輸入一個M或m的字元與兩個整數。如果輸出字元為M,輸出兩數的最大值,如果輸入字元為m,輸出兩數的最小值。
輸入 輸出
M 10 5 10
M 5 10 10
m 10 5 5
m 5 10 5
47
條件運算子
條件運算子的結構如下
(判斷條件) ? 條件成立的回傳值 : 條件不成立的回傳值
例如,
int num1 = 5, num2 = 0;�int max = num1 >= num2 ? num1 : num2; // max = ?
48
練習:找大小
輸入一個M或m的字元與兩個整數。如果輸出字元為M,輸出兩數的最大值,如果輸入字元為m,輸出兩數的最小值。
限制:使用條件運算子
輸入 輸出
M 10 5 10
M 5 10 10
m 10 5 5
m 5 10 5
49
2-3
運算子的優先順序
50
運算子的優先順序
當運算式中出現多個運算子時,
"寫一個結果會取決於計算次序的程式是個不好的習慣"�- B. Kernighan and D. Ritchie
51
2-4
常數
52
定義常數
利用#define可定義常數,例如
為何不直接寫?
差異在哪?
53
列舉(enumerate)
利用enum定義常數集合,例如
54
Q & A
55
Computer History Museum, Mt. View, CA