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) 它才能找的到。

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

2011年7月7日 星期四

1.1 如何編譯及執行程式

第 1 講 之 1    Say Hello
       Topic: 如何編譯及執行程式
請你先用編輯器 (editor) 編輯 hello.c 這個檔,它的內容如下:
/* hello.c */
#include <stdio.h>

int main (void)
{
    printf ("Hello, world!\n");
    return 0;
}
接下來我們準備執行這個程式,請在 UNIX 提示號下鍵入以下高亮度字元:

[thccy14]/Oz/u/u80/ee/u801833/program> gcc -o hello hello.c
[thccy14]/Oz/u/u80/ee/u801833/program> ./hello
Hello, world!

當你輸入了 gcc -o hello hello.c 卻出現了這個訊息時
gcc: Command not found
那麼表示你的工作站上沒有 gcc 這個編譯器 (compiler),這時候請你把 gcc 換成 cc,也就是說,請你輸入
cc -o hello hello.c
代替我們例子中 gcc 那一行。請各位特別注意小寫英文字母 o 和數字 0 的差別。

gcc -o hello hello.c 這一行究竟代表什麼意思呢?其實 gcc 是一個 C 語言的編譯器 (compiler),編譯器的工作就是負責把程式重編之後翻譯成可執行檔用的。一個程式必須經過下面的流程才可以變成可執行檔:
                        原始碼 (source code)
                            ↓
                        編譯器編譯 (compiled by compiler)
                            ↓
                        目的碼 (object code)
                            ↓
                        連結器連結程式庫 (link library by linker)
                            ↓
                        可執行檔 (executable file)
而本行的參數和它們的意思如下:
                        gcc -o hello hello1.c
                            ^  ^     ^
                            |  |     這個是你所要編譯的程式名稱
                            |  這個是你編譯出來後可執行檔的名字
                            這個是指定可執行檔名字的參數
如果你不加 -o hello,也就是直接打 gcc hello1.c 的話,那編出來的可執行檔自動被命名為 a.out。gcc 其他的參數我們以後用到再講。

你或許會覺得奇怪,程式不是要經過編譯器和連結器才可以變成可執行檔嗎?可是 gcc 只是一個編譯器而已,它怎麼可以造出可執行檔呢?這是因為 gcc 編譯完程式之後,會偷偷地呼叫 ld 這個連結器,造出可執行檔來。

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

星星流 C 語言講座

星星流 C 語言講座的作者是徐振家,最原始的版本可以在作者網站看到
http://www.cchsu.com/arthur/prg_bg5/c/

很久以前的文章了,我高一剛學寫程式時曾看過,內容很精簡,重點都講到了,算是 C 語言很好入門的教學文章。

本來的文章是發表在 BBS 上,這裡整理成比較漂亮的版本,並稍微修改增補一些東西,希望不要是狗尾續貂。全部分七個章節,每章分類如下:

  • 第一講--緒論
  • 第二講--數值與運算
  • 第三講--基本輸出與輸入
  • 第四講--流程控制
  • 第五講--陣列與指標
  • 第六講--函數
  • 第七講--型態與運算子再論

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