2011年7月22日 星期五

2.2 數值的運算

第 2 講 之 2    Arithmetic Operations
       Topic: 數值的運算
現在我們來看看如何作數值的運算,請看下面的範例程式:
/* arop.c */
/* Arithmetic Operations */

#include <stdio.h>

int main (void)
{
    int a, b, c;

    a = 4;
    b = -3;
    c = a / b;

    printf ("%d\n", a + b);
    printf ("%d %d %d %d\n", a - b, a * b, c, a % b);

    return 0;
}
它的執行結果如下:
1
7 -12 -1 1
我們現在來看看這個程式。首先又是 #include <stdio.h> 這個編譯指示,這是因為我們如果要使用 printf 這個函數的話,就必須含入 stdio.h 這個標頭檔,詳細的情形留待討論函數時再說明。

接下來我們宣告了三個變數 a, b, c,它們的型態是有號整數。C 語言提供了幾個基本的運算子 (operator) 供數值運算使用:
        *       乘 (multiply)
        /       除 (divide)
        %       求餘數 (modulo)
        +       加 (add)
                正號 (plus sign)
        -       減 (subtract)
                負號 (minus sign)
        ()      括號 (parenthesis)
比較值得注意的是 C 語言並沒有求次方的運算子,想求 x 的 y 次方必須叫用標準函式庫,而不能直接寫成 x^y。

我們仔細觀察上面的程式,首先看到
printf ("%d\n", a + b);
這一行,它的意思就是把 a + b 顯示在螢幕上,然後把游標移動到下一行的開頭。printf 的用法是:
printf (format string, expression, expression ....);
格式 (format string) 是一個字串 (string),現在你只要記得 "%d\n" 是顯示一個整數,"%d %d %d\n" 是顯示三個整數就可以了 (當然,相信聰明的你知道要怎麼顯示兩個整數 ^_^ ),每一個 %d 依次序對應到後面運算式 (expression) 的運算結果。

所以我們現在可以知道 7 -12 -1 1 分別對應到 a - b, a * b, a / b, a % b。你也許會覺得奇怪,a / b 應該是 -4/3 = -1.3333 才對呀,為什麼會顯示 -1 呢?這是因為我們用一個整數型態的變數 c 來存 a / b 的結果,當你用整數型態的變數來存浮點數時,小數點以後的數字會被無條件捨去。

[+/-] 繼續閱讀...

2011年7月13日 星期三

2.1 數值型態

第 2 講 之 1    Arithmetic Operations
       Topic: 數值型態
在 C 語言中,我們表示一個數的方法有兩種。第一種方法就是直接寫出這個數,我們叫它做立即值 (immediate value),例如 3、5.5這些都叫做立即值,也就是你可以直接看出值的量 (quantity)。

第二種方法就是設定一個變數 (variable),這個變數儲存了某個值,我們經由取用這個變數的程序來得到我們要的值。在 C 語言中,每個變數都具有它自己的資料型態 (data type),這是為什麼呢?

真實世界的數被分成許多的體 (field) 或環 (ring) 或群 (group),當然,這是像作者這種只學過一點點數學的人的笨分法,學過很多數學的人就不會這樣兒分了。但是沒有關係,因為電腦也很笨,所以電腦對於數它只有以下兩種二分法:

第一種分法:有號數和無號數
有號數就是帶正負號的數,無號數就是不帶正負號的數。
第二種分法:整數和浮點數
整數就是不帶小數點的數,浮點數就是帶小數點的數。
C 語言為了表示出電腦裡這兩種二分法,所以規定所有的變數都必須有型態,以便翻譯成機器語言給笨笨的電腦去處理。C 語言對於整數和浮點數的分法如下所示:
            ┌ 短整數          short  
        整數┼ 普通整數        int
            └ 長整數          long

            ┌ 單精度浮點數    float
      浮點數┼ 倍精度浮點數    double
            └ 加倍精度浮點數  long double
為什麼要把整數和浮點數分成這麼多類呢?這是因為要讓程式設計者能有更多的選擇。孟子曰:「魚與熊掌,不可得兼」,由於電腦硬體的限制,如果我們在處理數字時要求比較高的精確度的話,那麼我們必須花比較大的空間來存這些數字 (因為位數比較多),同時我們也必須花比較長的時間對這些數作算術運算。所以 C 語言提供了各三種精確度的整數和浮點數供程式設計師使用。

那麼 C 語言是如何表示有號數和無號數的呢?很簡單,它的做法就是以 unsigned 和 signed 兩個修飾子 (modifier) 來區別。

那麼在程式中,我要怎麼樣使用及設定數值變數呢?首先你必須記住:任何變數使用前必須先宣告它的資料型態。例如:
unsigned int a;
就表示我們宣告了一個變數,它的名字叫做 a,它的型態是無號整數。變數的名字不可以是純數字 (因為會和立即值混淆),不可以含有特殊符號 (通常除了底線字元 _ 之外都是不允許的),也不可以是保留字(reserved words)。保留字就是像 int、float ... 這些在 C 語言中有特殊意義的字,為了避免混淆當然不可以當作變數啦!

那麼 C 語言有那些保留字呢?請看:
auto            double          int             struct
break           else            long            switch
case            enum            register        typedef
char            extern          return          union
const           float           short           unsigned
continue        for             signed          void
default         goto            sizeof          volatile
do              if              static          while
某些編譯器會增加下面的保留字:
asm             fortran
再來請注意的就是 unsigned 它必須放在宣告的最前面,如果你寫
int unsigned a;
可能會產生錯誤 (當然不是絕對,和你的編譯器有關,不過沒人這樣寫就是了)。C 語言內定數值型態是有號數,也就是說,你寫
int a;

signed int a;
的意思是相同的。

[+/-] 繼續閱讀...

2011年7月11日 星期一

1.4 新手的背景知識

第 1 講 之 4    Newbies Look Here
Topic: 新手的背景知識
在寫作 C 語言程式的時候,我們通常使用 .c 作為副檔名,也就是程式檔的檔名通常取成像 abcd.c 這種形式。C 語言程式中有所謂的標頭檔,它們的副檔名通常是 .h。標頭檔的作用以後再詳述。

cc 或 gcc 會自動地把副檔名為 .c 的檔案當成是 C 語言程式檔,編譯出來的目的碼通常被命名為副檔名為 .o 的檔案,以後會很多例子展示這種情形。

當你的程式發生嚴重的錯誤時,在 UNIX 系統下會出現這個訊息:
Segmentation Fault, core dumped.
這個訊息就相當於你在 PC 上直接當掉的意思是一樣的,只是因為 UNIX 這個作業系統比較先進,所以它會把當時你程式的執行情形存成一個檔案,這個檔案的檔名就叫 core。無論如何,當你的程式會發生 core dump 的情形,就表示你的程式內還有嚴重的錯誤,必須加以修正。

我們在寫作程式的時候,應該考慮的基本因素有下列幾項:
  1. 正確性:
    敬愛的小平同志曾經說過:「管牠黑貓白貓,會拿耗子的就是好貓。」不管你的程式技巧多麼純熟,格調多麼優雅,不能正常工作的程式就不能算是好程式,甚至連稱為程式的資格都沒有。寫程式的第一要務就是求程式的正確性。
  2. 可維護性:
    程式除了能正確地工作之外,還要能讓人看的懂才叫好程式,否則只是耍耍花槍不入流的東西罷了。程式要如何寫才叫乾淨漂亮,這我們以後會慢慢地向各位介紹。
  3. 可擴充性及可重用性:
    有一個很有名的公式:程式=資料結構+演算法。資料結構就是儲存資料的方法,演算法就是處理資料的方法,而我們寫程式就是要設計這兩種方法並把它們用程式語言實現出來。當你在設計這兩種方法的時候,必須考慮你現在和未來可能面臨的需求和狀況,這樣才不會遇上改動一個功能就必須重寫整個程式的慘劇。
  4. 效率:
    程式的效率可以從兩個方面來看:執行的效率和開發的效率。執行的效率說的通俗一些就是你的程式跑的快或慢,開發的效率就是你的程式寫作時間是長或短。良好的程式設計師在時間狀況允許之下應該努力追求程式的執行效率。
  5. 容錯能力:
    程式就像一部機器,我們給定一定的輸入,得到我們想要的輸出。但是一個好的程式必須要有處理錯誤輸入的能力,以避免產生錯誤的輸出。
  6. 介面:
    介面就是程式和使用程式的人溝通的方式。程式最好盡量使使用者易於與程式溝通,這樣程式才能稱的上是好用。
以上是寫作程式時所應該注意的大原則,細部的情形我們以後會詳細地探討。

[+/-] 繼續閱讀...

1.3 C 語言程式的基本架構 (2)

第 1 講 之 3    A whole new world
Topic: C 語言程式的基本架構 (2)
再來我們看到:
int main (void)
{
    printf ("Hello, world!\n");
    return 0;
}
main 是一個函數,也是 C 程式的主體 (不然叫 main 做啥?),你的程式會由 main 函數開始執行。printf 是另一個函數,它負責顯示訊息到螢幕上。C 語言中函數的意義我們以後會講,你現在只要知道 main () 是一個函數, printf () 也是一個函數就可以了。那兩個大括號 { } 是幹什麼用的?用大括號括起來的東西,我們叫它做一個程式區塊 (program block)。緊跟在函數定義後的程式區塊叫函數本體 (function body),也就是說,
{
    printf ("Hello, world!\n");
    return 0;
}
這個程式區塊就是 main () 這個函數的函數本體。再來仔細看這一行:
printf ("Hello, world!\n");
看到行末的分號了沒? 程式區塊中的每一行都要以分號來表示這一行結束了。最後這行:
return 0;
代表 main () 回傳 0 ,至於這有什麼作用後面會講,目前只要知道 main () 通常回傳 0 代表程式正常結束

[+/-] 繼續閱讀...

2011年7月8日 星期五

1.2 C 語言程式的基本架構 (1)

第 1 講 之 2    A whole new world
Topic: C 語言程式的基本架構 (1)
現在回到我們剛剛輸入的程式上,我們來看看到底它是如何工作的,先看第一行:
/* hello.c */
這種用 /* 和 */ 括起來的東西叫註解 (remark),是用來註解程式用的。註解會被編譯器完全地忽略,這是 C 語言比較特殊的地方,請特別注意。好的程式應該有簡明恰當的註解。再來看到:
#include <stdio.h>
以 # 開頭的東西叫編譯指示 (compiler directives),它的作用是指示編譯器照著編譯指示去編譯你的程式。#include 的意思就是在編譯這個程式之前,請編譯器先引入標頭檔 (header file) 加以編譯,編譯完之後再編譯本程式下面的部份。所以說呢,#include <stdio.h> 的意思就是叫 compiler 先編譯 stdio.h 這個檔然後再編譯下面的程式。我們以一個簡單的流程圖表示如下:
                      gcc 編譯 hello.c

看到了 #include <stdio.h>

gcc 去找尋 stdio.h
↓ 找到了
gcc 先編譯 stdio.h
↓ 編譯完畢
gcc 再編譯 hello.c 剩下的部份
↓ 編譯完畢
gcc 呼叫 ld 來連結

hello 這個程式製作完畢
你也可以把 #include <stdio.h> 寫成 #include "/usr/include/stdio.h",可以得到同樣的效果。那麼,#include 後面接角括號 < > 和雙引號 " " 有何不同呢?角括號括起來的檔案通常表示那個檔案是個標準標頭檔 (standard header file)。如果是標準標頭檔,編譯器會到某個固定的地方去找這個檔案(至於是那個地方那就不一定了,看當初安裝系統的時候是怎麼裝的)。用雙引號括起來的檔案通常表示那是個使用者自定的標頭檔 (user-defined header file),所以你必須給編譯器完整的路徑 (filepath) 它才能找的到。

[+/-] 繼續閱讀...