SOGO論壇
  登入   註冊   找回密碼
查看: 512|回覆: 0
列印 上一主題 下一主題

[技術文章] 型別與運算 [複製連結]

Rank: 11Rank: 11Rank: 11Rank: 11

熱心參予論壇活動及用心回覆主題勳章 數位硬體勳章

狀態︰ 離線
跳轉到指定樓層
1
發表於 2012-7-16 09:23:28 |只看該作者 |倒序瀏覽
現在的數位電腦幾乎都以二進位形式儲存資料。如果事先不知道二進位的編碼意義,則無法解釋這些二進位資料的意義。因此我們必須透過程式語言定義的資料型別, 告訴Compiler變數內所存放的資料有多大,如何去解釋它,如何翻譯含有該變數的運算指令等問題。只要是資料,不論是存放在主記憶體內的變數,或是存放於暫存器的運算結果, 都有型別。而型別和運算符號間的關係如下:

    運算符號只能作用於規定的型別上,並非所有的型別和運算符號組合都合法。
    若運算符號需要兩筆資料(如乘法), 則這兩筆資料必須是同一個型別,運算的結果也是同一個型別。
    我們可在資料前面加上(type),也就是以圓括弧將型別名稱括起來的形式,將該資料強迫轉型。
    由於我們常常會把不同型態的數值做算數運算, 如把整數加上浮點數, 或短整數加上長整數, 因此C語言為了方便起見, 除了=(assigement)會將右側的型別轉成左側變數的型別外, 在做算數運算時會自動將"型別比較小"的數值轉成"型別比較大"的數值。其中浮點數型別>整數型別, 位元長度長的>位元長度短的。值得注意的是, 這類的型別自動轉換, 不同程式語言的規則不同, 且會破壞型別檢查機制, 容易讓程式撰寫者發生不容易被發現的錯誤,因此能免則免。

變數,常數,以及運算符號可以組合成運算式。運算式執行完畢後會在暫存器上留下該運算式的執行結果,此結果也具有型別。

資料型別(Data Type)

C語言所定義的資料型別如下
型別         符號位元         位元長度         表示方法         數值範圍
整數         有         16或32         int         -2147483648 ~ 2147483647
8         char         -128 ~ 127
16         short         -32768 ~ 32767
32         long         -2147483648 ~ 2147483647
64         long long          
無         16或32         unsigned int         0 ~ 4294967295
8         unsigned char         0 ~ 256
16         unsigned short         0 ~ 65535
32         unsigned long         0 ~ 4294967295
64         unsigned long long          
浮點數         有         32         float         10^-38~10^38
64         double         10^-308~10^308
字元         有         8         char         -128 ~ 127

以上的整數使用二補數,浮點數則採用IEEE的標準,相關資料請見電腦系統概論。至於char,對C語言來說,採用ASCII code的編碼方式。

變數的宣告是以

type varname;
type varname = constant; // 給定初始值initial value

的形式來宣告varname的型態。常數的部分,其表示法為

123表示十進位的int

0123表示八進位的int

0x123表示十六進位的int

123L表示十進位的long

123.0表示十進位的float

123.0L表示十進位的double

123.0e-8表示123*10-8的float

'a'表示char

'\n'表示char的換行符號(new line)

'\t'表示char的Tab

'\b'表示char的Backsapce

'\r'表示Carriage Return

'\f'表示Line Feed

'\\'表示\

'\''表示'

變數宣告的前面也可以加上const這個保留字,以表示此變數只能在宣告時給定初始值,以後就不能再改了

const double PI = 3.14159;

運算符號(Operator)

算術(Arithmetic)運算符號
運算符號         功能敘述         運算符號         功能敘述
+         加         *         乘
-         減         /         除
%         餘數                    
++         加一         --         減一

以上除了%只能用於整數外,其餘符號不論整數或浮點數均可使用。++和--是所謂的unary operator,只能用於變數,可放在變數的前或後面,意義如下

x++ 此運算式的結果為x的數值,此外x變數的內容會加1

++x 此運算式的結果為(x的數值+1),此外x變數的內容會加1

x-- 此運算式的結果為x的數值,此外x變數的內容會減1

--x 此運算式的結果為(x的數值-1),此外x變數的內容會減1

邏輯(logic)運算符號
運算符號         功能敘述         運算符號         功能敘述
>         大於         <         小於
>=         大於等於         <=         小於等於
==         等於         !=         不等於
&&         logic AND         ||         logic OR
!         logic NOT                

C語言裡並沒有boolean資料型態(只有true和false兩種值),條件是否成立完全是看運算式的結果,若為0表示不成立,不為0表示成立。這一點設計得不好,很容易讓人犯錯,誤把邏輯運算寫成數值運算而不自覺。像java語言就提供了boolean資料型態,且邏輯運算和數值運算不能混用,以免程式撰寫者犯錯。

AND 兩者為真才為真,其餘皆為假

OR 兩者為假才為假,其餘皆為真

位元(Bit)運算符號
運算符號         功能敘述         運算符號         功能敘述
&         bit AND         <<         left bit shift
|         bit OR         >>         right bit shift
^         bit XOR         ~         1的補數

所謂位元運算符號,其作用的大小是bit,而不是整個數值。因此要了解這類符號的運算結果,必須先把數值轉成二進位(2補數)。以下範例內的==>表示計算的過程, 且為說明起見, 假設這些數字為8bits

2 & 3 ==> 00000010 & 00000011 ==> 00000010 ==> 相當於2

2 & 1 ==> 00000010 & 00000001 ==> 00000000 ==> 相當於0

2 | 1 ==> 00000010 |000000 01 ==> 00000011 ==> 相當於3

XOR表示兩者相同時為0,兩者不同時為1

2 ^ 3 ==> 00000010 ^ 00000011 ==> 00000001 ==> 相當於1

~2 ==> ~00000010 ==> 11111101 ==> 相當於-3

left bit shift是把每一個bit都向左移,右邊補0

2 << 1 ==> 00000010 << 1 ==> 00000100 ==> 相當於4

right bit shift是把每一個bit都向右移,左邊補上最左邊的bit

2 >> 1 ==> 00000010 >> 1 ==> 00000001 ==> 相當於1

-3 >> 1 ==> 11111101 >> 1 ==> 11111110 ==> 相當於-2

指標(Pointer)陣列(Array)與函數(Function)運算符號
運算元         功能敘述         運算元         功能敘述
&         取變數的地址         ->         透過結構指標取結構成員數值
*         透過指標取數值         []         取陣列元素數值
FunctionName()         函數呼叫         .         結構變數.結構成員

以上運算符號的用法和意義,請見後面章節詳述

其他運算符號
運算元         功能敘述         運算元         功能敘述
=         將右邊的值複製到左邊的變數         (type)         將右邊的數值轉換成type型別
+=         將右邊的數值加上左邊的數值然後指定給左邊的變數         ?:         若?左邊成立則做:左邊否則做:右邊
,         合併兩個運算視為一個敘述         sizeof(type)         傳回type所需要的byte數
(運算式)         表示()內優先運算                

= 將右邊的數值指定(assigement,也就是複製)給左邊的變數

+= 將將右邊的數值加上左邊的數值然後指定給左邊的變數,其餘如-= *= /= %= &= ^= |= <<= >>=的意義也都相同,只是第一個運算符號不同而已

, 將兩個運算式結合成一個,以後面運算式的數值作為結果。比較常見的用途是在for(;;)迴圈,由於以分號分開的部分只能有一個敘述,若想放進兩個運算式,就可以使用","如下

for (i=0, j=2; i<n && j<m; i++, j++);

另一個,的用途是在變數宣告時,若要一個敘述內宣告好幾個變數可以用

int x, y, z;

c? p1: p2 相當於if(c) p1 else p2這個條件敘述的縮寫,範例如下

(x==0) ? 0 : 1/x;

 

運算優先權與結合順序

運算式可以由任意的運算符號,變數,以及常數所組成。而電腦執行程式時只能依序執行指令,因此運算式中運算符號的執行順序,必須定義得非常明確,編譯程式才能幫我們做正確的翻譯。程式語言中有關定義運算符號的執行順序,是由優先權(Priority)和結合順序(Association)兩者構成的。優先權越高的越早執行,相同優先權的符號,則看其結合順序。若為左結合(Left Association)表示左邊的符號先做,若為右結合(Right Association)表示右邊的符號先做。下表是C語言定義的優先權和結合順序,上方的列的優先權高於下方的列,同一列的運算順序則由表最右側的結合順序決定。例如第三列乘法*的優先權高於第四列+法,而同為第三列的*/%則視其在運算式中的出現順序決定。
運算元         結合順序
(   )         [   ]         ->         .                                                                               左到右
!         ~         ++         --         +         -         *         &         (type)          sizeof                    右到左
*         /         %                                                                                         左到右
+         -                                                                                                   左到右
<<         >>                                                                                                   左到右
<         <=         >         >=                                                                               左到右
==         !=                                                                                                   左到右
&                                                                                                             左到右
^                                                                                                             左到右
|                                                                                                             左到右
&&                                                                                                             左到右
||                                                                                                             左到右
?:                                                                                                             右到左
=         +=         -=         *=         /=         %=         ^=         |=         <<=         >>=                   右到左
,                                                                                                             左到右

第二優先權裡的+-*&是所謂unary operator,其作用的目標只有一個。此處+表示正號,-表示負號,*表示由指標取值,&表示取變數的地址。以下是幾個運算式計算的過程:

2+3+4*5 其中*為第三優先權左結合,+為第四優先權左結合,因此*先做,再來是左邊的+,最後中間的+,整個的執行過程

2+3+4*5==>2+3+20==>5+20==>25

所以最後的結果是"int 25"

2+3+4*5.0 此處要注意的是5變成了5.0,也就是說這些常數並不是同一個型別,因此過程變成

2+3+4*5.0 ==> 2+3+4.0*5.0 ==> 2+3+20.0 ==> 5+20.0 ==> 5.0+20.0 ==> 25.0

最後結果為"float 25"

假設int x; float y;

x = y = 0;因為=是右結合,因此右邊的=號先做,而y的型別是float,常數0的型別是int,因此過程如下

x = y = 0 ==> x = y = (float)0 ==> x = 0.0 ==> x = (int)0.0 ==> 0

最後結果為"int 0", 且x的內容為0,y變數的內容為0.0

最後強調上述範例的精神是,運算式最後的結果必然有型別,而且此結果是存放在ALU的暫存器內。雖然=運算符號會改變左側變數的內容,但整個運算過程的重點在於暫存器上的型態和大小。

假設int x= 1;float y;

y = x++ ==> y = 1 ==> y = (float)1 ==>1.0

最後結果為"float 1.0", 且x變數的內容為int 2,y變數的內容為float 1.0
範例

攝氏溫度轉華氏溫度

#include <stdio.h>
int main() {
    float degree = 100.0;
    printf("100C=%fF\n", degree*9.0/5.0+32.0);
}

華氏溫度轉攝氏溫度

#include <stdio.h>
int main() {
    float degree = 100.0;
    printf("100F=%fC\n", (degree-32.0)*5.0/9.0);
}

1+2+...+n的總合

#include <stdio.h>
int main() {
    int n = 100;
    printf("1+2+...+%d = %d\n", n, n*(n+1)/2);
}

特別注意上述的運算式裡/2要放到最後面,如果寫成n/2*(n+1),從數學式子的角度看好像沒問題,但別忘了,binary operator的兩邊必須是同樣型別的資料,而且計算的結果也是同樣的型別。因此n/2*(n+1)會先計算n/2,如果n不能被2整除的話,那麼為了符合計算結果必須是整數的限制,則小數點的部份就會無條件捨去,使得計算的結果錯誤。下面的範例一樣要注意相同的問題。

12+22+...+n2的總合

#include <stdio.h>
int main() {
    int n = 100;
    printf("1*1+2*2+...+%d*%d = %d\n", n, n, n*(n+1)*(2*n+1)/6);
}

把浮點數四捨五入為整數

C語言規定浮點數轉整數時,小數點部分無條件捨去。如果要達到浮點數四捨五入為整數的效果,可以使用下面的小技巧

#include <stdio.h>
int round(float y) {
    return (int)(y + 0.5);
}
int main() {
    float x = 20.6;
    printf("%f 四捨五入成為 %d\n",  x, (int)(x+0.5));
    printf("%f 四捨五入成為 %d\n",  x, round(x));
}
喜歡嗎?分享這篇文章給親朋好友︰
               感謝作者     

請注意︰利用多帳號發表自問自答的業配文置入性行銷廣告者,將直接禁訪或刪除帳號及全部文章!
您需要登錄後才可以回覆 登入 | 註冊


本論壇為非營利自由討論平台,所有個人言論不代表本站立場。文章內容如有涉及侵權,請通知管理人員,將立即刪除相關文章資料。侵權申訴或移除要求:abuse@oursogo.com

GMT+8, 2024-5-4 16:32

© 2004-2024 SOGO論壇 OURSOGO.COM
回頂部