2012年6月8日 星期五

命令列引數

程式在執行時,可以附加一些引數以指定執行不同的功能,例如:
copy -r ./temp ./tmp

其中copy是程式名稱,而-r、./temp、./tmp都是在程式執行時附加給程式的一些引數,這種程式執行方式在以文字畫面為主的程式中相當常見, 稱之為「命令列引數」(Command-line argument)。

範例:
int main(int argc, char *argv[])
{
    ....
} 


注意:
argc是指引數的數量,一定大於或等於1,因為包含了程式名稱。
*argv[]存入的是引數的字串,一樣,*argv[0]是程式名稱。

舉個例子:
如果是 Sample a b c ,則:
argc=4
*argv[0]="Sample"
*argv[1]="a"
……以此類推。

值得注意的是,如果變成 Sample "a b c"
argc=2
*argv[0]="Sample"
*argv[1]="a b c"
像這樣,用雙引號把字串夾起來,就能傳遞包含空白的引數

2012年6月4日 星期一

檔案輸出、輸入的基礎


  • 檔案的機制

使用標準程式庫函數,就可以從螢幕或是鍵盤進行輸入、輸出。如果要更進一步將資料長久保存下來,或是需要載入大量資料時,使用檔案來管理資料是不可或缺的一環哦!
在程式中要操作檔案,需要依照以下的順序來進行:

(1)開啟檔案   →   (2)讀取/寫入檔案   →   (3)關閉檔案

所謂檔案的「開啟」、「關閉」,就是在操作開始之前用於輸入、輸出的資料流概念,以及檔案連結與檔案分開等處理。開始使用檔案的時候需要將檔案開啟,最後則需要將檔案關閉。


範例:


#include <stdio.h>

int main(void)
{
    FILE *fp;
    int a, sum=0;
    fp=fopen("test1.txt", "r");  //將檔案開啟
    if(fp==NULL)
    {
        printf("無法開啟檔案。\n");
        return 1;
    }
    while(fscanf(fp, "%d", &a)!=EOF)
    {
        sum+=a;
    }
    fclose(fp);  //將檔案關閉
    fp=fopen("test1.txt", "a");  //將檔案開啟
    fprintf(fp, "%d\n", sum);
    fclose(fp);  //將檔案關閉
    return 0;
}

為了配合這個範例,你可以在本程式所在的資料夾下建立一個檔案,名為"test1.txt",內容如下:
1
2
3
4
5
6
7
8
9
10

  記得在最後面空一行。


注意:

檔案的開啟用fopen()函數來進行。這個函數會傳遞以下兩項引數:檔案名稱、開啟模式(如下表)。傳回值是該檔案的指標(FILE*型態),這個指標稱之為檔案指標或者是資料流指標
此外,基於某種理由而無法開啟檔案時,fopen()函數就會傳回「NULL」這個由巨集所定義的指標。

開啟模式:
"w":用於寫入純文字檔案(檔案內的內容被完全覆蓋)
"r":用於讀取純文字檔案
"a":用於追加寫入純文字檔案(從檔案的末尾開始寫入)
"w+"、"r+"、"a+":與以上相同,但是同時具有寫入跟讀取的權限。
"wb"、"rb"、"ab":與首三者相同,但是是以二進位方式進行。

在程式的最後,使用fclose()函數進行將檔案關閉的處理。如果沒有關閉檔案的話,檔案有時候會因此發生問題,請別忘記這個動作哦!

寫入檔案的函數與輸出用的printf()很相像,是fprintf(),用法與printf()幾乎一樣,只是多了一個引數指定檔案指標。

讀取檔案也是哦,用的是與scanf()相似的fscanf(),一樣多了一個引數用來指定檔案指標。


補充--printf()、scanf()的轉換規格

   printf()以及scanf()是兩個我們非常常用的函數,基本的用法大家都已經大致了解,不外乎是:一個一個對應、長整數用%d、浮點數用%f……等等。
像是%d、%f、%s等等,這些要求它以特定的格式輸出輸入的東西,就叫做轉換規格

printf()轉換規格:
%c 以字元輸出
%d 以10進位輸出
%f 以浮點小數輸出
%e 以科學記號表示,指數部份加上e之後輸出
%E 以科學記號表示,指數部份加上E之後輸出
%s 以字串輸出
%p 以指標輸出
%o 以8進位輸出
%x 以16進位小寫輸出
%X 以16進位大寫輸出
%u 以不帶記號的10進位輸出



scanf()轉換規格:
%c 以字元輸入
%d 以10進位輸入
%f 以浮點小數float輸入
%lf 以浮點小數double輸入
%s 以字串輸入
%p 以指標輸入
%o 以8進位輸入
%x 以16進位輸入
%u 以不帶記號的10進位輸入



  • 指定輸出寬度

這是用於對齊的,常常輸出時想要在螢幕上或是檔案中排列得整齊,就要指定輸出的寬度。
舉例:%5d或%+5d 以5個字元的寬度來輸出,並靠右對齊
%-5d 以5個字元的寬度來輸出,並靠左對齊

  • 指定精度

輸出時也可以指定所輸出的小數點要幾位數,也就是所謂的精度。
舉例:%.3f 輸出至小數點以下3位

以上兩種也可以組合起來哦!例如:%-6.2f,就是將浮點小數向左對齊、總字原寬度為6個字元、輸出至小數點以下2位數。圓周率的話就變成「3.14(空格)(空格)」。

2012年5月25日 星期五

傳入陣列給函數的方法

int SampleFunction(int var1, int var2, ...)
{
    ...
    return result;
}

這是一個簡單的函數架構,函數(函式,Function)就是可以傳入引數(即參數),讓它傳回結果的一段程式碼。
如果沒有回傳值的話,也可以稱作副程式。

如果引數只有二到三個,當然沒有什麼問題;
但是如果你要一次傳入很多引數,甚至是陣列時,要怎麼做呢?

範例一:

#include <stdio.h>
#include <stdlib.h>
int sum(int var[], int b);

int main(void)
{
    int a[]={1, 2, 3, ...};
    printf("%d\n", sum(a, 10));
}

int sum(int var[], int b)  //計算首b個元素的總和
{
    int x=0, i;
    for(i=0; i<b; i++)
    {
        x+=var[i];
    }
    return x;
}

注意:

  1. 函數的引數使用「陣列」時,函數內就使用陣列編寫。
  2. 調用函數時,引數只要寫「陣列名稱」即可。(補充:陣列名稱代表「陣列首個元素的位址」。)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

範例二:


#include <stdio.h>
#include <stdlib.h>
int sum(int* var, int b);

int main(void)
{
    int a[]={1, 2, 3, ...};
    printf("%d\n", sum(a, 10));
}

int sum(int* var, int b)  //計算首b個元素的總和
{
    int x=0, i;
    for(i=0; i<b; i++)
    {
        x+=*(var+i);
    }
    return x;
}

注意:

  1. 函數的引數使用「指標」時,函數內就以指標編寫。
  2. 調用函數時,一樣傳入「陣列首個元素的位址」。

scanf() 的小秘密

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char a[100];
    scanf("%s", a);
    printf("%s\n", a);
    system("pause");
    return 0;
}


以上是一個簡單的程式,也就是讓使用者輸入一段文字後,再將它輸出。
那,這有什麼特別的呢?

試試看輸入"This is a book."吧!
輸出結果竟是"This(換行)"!發生什麼事了呢?

實際上,scanf()這個函數不會接收「空格」、「Tab」以及「換行」這些符號。
那怎麼辦呢?你可以使用gets()函數,它可以接收包含空格的整行文字。

範例:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char a[100];
    gets(a);  //讀取包含空白的整行文字
    printf("%s\n", a);
    system("pause");
    return 0;
}

補充:
如果硬是要使用scanf()的話,就這樣寫吧。

char a[100];
scanf("%[^\n]", a);  //讀取除了換行以外的所有字元

排序 (Sorting)

常用的排序法有「選擇排序法」以及「氣泡排序法」,兩種都很適合初學者學習和研究,儘管速度不快,卻是非常經典的例子。

  • 選擇排序
將要排序的對象分作兩部份,一個是已排序的,一個是未排序的,從後端未排序部份選擇一個最小值,並放入前端已排序部份的最後一個,例如:

排序前:70 80 31 37 10 1 48 60 33 80
  1. [1] 80 31 37 10 70 48 60 33 80 選出最小值1
  2. [1 10] 31 37 80 70 48 60 33 80 選出最小值10
  3. [1 10 31] 37 80 70 48 60 33 80 選出最小值31
  4. [1 10 31 33] 80 70 48 60 37 80 ......
  5. [1 10 31 33 37] 70 48 60 80 80 ......
  6. [1 10 31 33 37 48] 70 60 80 80 ......
  7. [1 10 31 33 37 48 60] 70 80 80 ......
  8. [1 10 31 33 37 48 60 70] 80 80 ......
  9. [1 10 31 33 37 48 60 70 80] 80 ......

  • 氣泡排序法
顧名思義,就是排序時,最大的元素會如同氣泡一樣移至右端,其利用比較相鄰元素的方法,將大的元素交換至右端,所以大的元素會不斷的往右移動,直到適當的位置為止。
基本的氣泡排序法可以利用旗標的方式稍微減少一些比較的時間,當尋訪完陣列後都沒有發生任何的交換動作,表示排序已經完成,而無需再進行之後的迴圈比較與交換動作,例如:
排序前:95 27 90 49 80 58 6 9 18 50
  1. 27 90 49 80 58 6 9 18 50 [95] 95浮出
  2. 27 49 80 58 6 9 18 50 [90 95] 90浮出
  3. 27 49 58 6 9 18 50 [80 90 95] 80浮出
  4. 27 49 6 9 18 50 [58 80 90 95] ......
  5. 27 6 9 18 49 [50 58 80 90 95] ......
  6. 6 9 18 27 [49 50 58 80 90 95] ......
  7. 6 9 18 [27 49 50 58 80 90 95] 由於接下來不會再發生交換動作,排序提早結束
在上面的例子當中,還加入了一個觀念,就是當進行至i與i+1時沒有交換的動作,表示接下來的i+2至n已經排序完畢,這也增進了氣泡排序的效率。



概念大致了解了之後,實作看看吧:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SWAP(x,y) {int t; t = x; x = y; y = t;} //交換
void selectionSort(int number[]);  // 選擇排序
void bubbleSort(int number[]);  // 氣泡排序

int main(void)
{
    int number[10], i, a;
    srand((unsigned)time(NULL));
    printf("排序前:");
    for(i=0; i<10; i++)
    {
        number[i]=rand()%100;
        printf("%d ", number[i]);
    }
    printf("\n選擇排序方式:\n");
    printf("(1)選擇排序  (2)氣泡排序\n");
    scanf("%d", &a);
    switch(a)
    {
        case 1: selectionSort(number); break;
        case 2: bubbleSort(number); break;
        default: return 0; break;
    }
    for(i=0; i<10; i++)
        printf("%d ", number[i]);
    printf("\n");
    system("pause");
    return 0;
}

void selectionSort(int number[])
{
    int i, j, m;
    for(i=0; i<9; i++)
    {
        m=i;
        for(j=i+1; j<10; j++)
            if(number[j]<number[m])
                m=j;
        if(i!=m)
            SWAP(number[i], number[m]);
    }
}

void bubbleSort(int number[])
{
    int flag=1, i, j;
    for(i=0; i<9 && flag==1; i++)
    {
        flag=0;
        for(j=0; j<9-i; j++)
            if(number[j]>number[j+1])
            {
                SWAP(number[j+1], number[j]);
                flag=1;
            }
    }
}

2012年5月18日 星期五

寫程式需要什麼?

寫程式需要有什麼東西呢?

(1) 開發環境:
指的是寫程式用的軟體。
較為人所知的有--
Dev-C++: http://www.bloodshed.net/devcpp.html
wxDev-C++: http://wxdsgn.sourceforge.net/
Code::Blocks: http://www.codeblocks.org/
Microsoft Visual C++ Express: http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express

補充:
整合開發環境就是俗稱的IDE
IDE中通常會有:程式語言編輯器、除錯器(Debugger)等等,有些IDE會內建編譯器(Compiler),沒有內建的話,就會調用第三方的編譯器來進行編譯的動作。
現在的IDE也有可以設計「圖形化使用者介面」的工具,或者是「類別瀏覽器」、「物件檢視器」、「物件結構圖」等等與物件導向程式相關的工具。

(2) 一本書:
儘管你已經很熟練了,但人總會忘。
所以需要一本書來查,當然也可以拿來學。

(3) 你的腦袋:
不用多說了吧!