2. C Language Q & A

嬉 C 兮雜記

1. 變數.指標.記憶體
2.1.1. 動態決定陣列大小
2.1.2. 動態二維陣列配置
2.1.3. 動態多維陣列處理
2.1.4. 陣列動態元素觀念問題
2.1.5. 由副函式回傳兩個以上的值
2.1.6. 陣列指標的使用
2.1.7. 雜湊表範例
2.1.8. 函式指標的用法
2.1.9. 常數指標的作用
2.1.10. 改變呼叫端指標的指向位址
2. 資料轉換
2.2.1. 十六進位字串轉數值
2.2.2. 十六進位字串轉二進位字串
2.2.3. 二進位字串轉一般數值
2.2.4. 內碼輸出成中文字串
2.2.5. 觀察一個 byte 裡的每個 bit 值
3. 演算法
2.3.1. 樂透隨機程式範例
2.3.2. 固定次數,隨機出現。
2.3.3. 檔案加密的簡單範例
2.3.4. 動態輸入資料排序
2.3.5. 簡易賽程表
2.3.6. 簡易撲克(showhand)
2.3.7. 選課程式範例
4. 字串處理
2.4.1. C 語言的字串處理特色
2.4.2. 消除連續空白字元
2.4.3. 標記字串範例
5. 輸出輸入
2.5.1. fwrite如何一次寫入兩個變數?
2.5.2. 一次讀入整個檔案的方法
2.5.3. 檔案 16 進位編碼顯示器範例
2.5.4. 動態不等長資料輸入的讀取
6. 系統呼叫
2.6.1. 如何顯示系統時間
7. C 語言的編撰工具
2.7.1. trace C source
聯絡人. 周安仁 老貢生
Revision History
Revision 1.22004/05/09
第一次對外發佈

文件由來

這是我把平日在網路上與網友切磋時,所收集到的一些寶貴經驗,特別是在 tw.bbs.comp.language 這個 news group 上。因為並非針對單一主題,目標也廣泛而零碎,不忍捨棄,就通通集中在一篇文章裡,以>備將來他日自己遺忘時的再度檢索參考。

1. 變數.指標.記憶體

2.1.1. 動態決定陣列大小

rd wrote:有一個陣列,大小需由使用者輸入,使用者輸入一數值 n ,程式內部要根據 n 值定義陣列大小,值全為0的陣列。請問欲達成此目的,該如何做?

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

int main(int argc, char *argv[])
{
  unsigned int n,i;
  int *ip;

  printf("How many array number is : \n");
  scanf("%d",&n);

  ip=(int *)malloc(sizeof(int)*n);
  bzero((void *)ip,sizeof(int)*n);

  for(i=0;i<n;i++)
    printf("The No%d is %d\n",i,ip[i]);

  free(ip);
  return 0;
}
        

2.1.2. 動態二維陣列配置

兔兔愛睡覺 wrote:宣告2維陣列時,陣列的大小要用變數要如何做呢?

二維動態陣列配置

#include <stdio.h>
int main(int argc, char *argv[])
{
   int x,y,i,j;
   int *ip;
   printf("請輸入第一維數目: ");
   scanf("%d",&x);
   printf("請輸入第二維數目: ");
   scanf("%d",&y);

   ip=(int *)malloc(sizeof(int)*x*y);

  /* 輸入動態二維陣列值 */
   for (i=0;i < x;i++) {
      for (j=0;j < y;j++) {
        printf("請輸入 %d 行,%d 列數值: ",i+1,j+1);
        scanf("%d",ip+(i*y)+j);
      }
   }

  /* 輸出動態二維陣列值 */
  for (i=0;i < x;i++)
  {
     for (j=0;j < y;j++)
        printf("%d 行,%d 列的數值是 -- %d.\n",i+1,j+1,*(ip+(i*y)+j));
  }

  free(ip);
}

2.1.3. 動態多維陣列處理

請問如何用 C 語言寫一個副程式,可以處理任意大小的多維陣列?

首先陣列變數除了第一維外,其他維都必須要事先宣告固定大小,這是 C 語言的標準規則,不可以違反的。變通的方式是,配置一個動態記憶體區塊,來模擬動態數目元素的陣列。範例如下:

#include <stdio.h>
#include <stdlib.h>
#define XNUM 3          /* 第一維元素數目常數宣告 */
#define YNUM 2          /* 第二維元素數目常數宣告 */
#define ZNUM 4          /* 第三維元素數目常數宣告 */

/* 將動態陣列元素值相加的副函式 */
int AddTest(int *d,int m,int n,int o)
{
  int retvalue=0;
  int i,j,k;

  for(i=0;i<m;i++)
    for(j=0;j<n;j++)
      for(k=0;k<o;k++)
        retvalue+=*(d+(i*n*o)+(j*o)+k);

  return retvalue;
}

int main(int argc, char *argv[])
{
  int *data;
  int value,i,j,k;

  data=(int *)malloc(sizeof(int)*XNUM*YNUM*ZNUM);        /* 配置動態記憶體 */

  /* 為每一個陣列變數元素填入數值 */
  for(i=0;i<XNUM;i++)
    for(j=0;j<YNUM;j++)
      for(k=0;k<ZNUM;k++)
        *(data+(i*YNUM*ZNUM)+(j*ZNUM)+k)=1;

  value=AddTest(data,XNUM,YNUM,ZNUM);        /* 執行加總副函式 */
  printf("%d\n",value);

  free(data);        /* 釋放動態記憶空間 */
}

上例可以看出,就算每一維元素數目都可動態調整,但維數卻是固定的,也就是如果要處理三維變數,迴圈就要套三層,既不能是四維,也不能是三維。

2.1.4. 陣列動態元素觀念問題

為何 C 語言的陣列變數元素取值時,只有第一維可以動態改變大小,其他維都要做固定元素大小?通通都接受動態調整不更靈活嗎?

因為陣列變數就是一群變數元素的集合,要對每個元素讀寫,就必須做適當的位移,也就是每個元素資料長度是多少,必須讓 C 編譯器知道,否則 C 就無法取出正確的值。

一維陣列變數因為宣告時同時決定了他的型態,所以可以判斷出一個元素的位移是多少,譬如 char array 位移長度是 1 byte ,int array 位移長度是 4 byte , float array 位移長度是 8 byte 。

但二維以上呢?譬如 int IA[a][b] ,一個 a 單位的位移,等於 b 總數 * 4 byte(int 型態資料的長度),那當 C 要從 IA[0][0] 移到 IA[1][0] 時,他該移動多長的距離才能正確讀到呢?你不先告訴他 b 總數是多少,他是無從判斷的。

2.1.5. 由副函式回傳兩個以上的值

fooa wrote:能否在一個function 結束之前回傳兩個值 ? 或者兩個東西 , 不一定是value ..?

送進去兩個盒子,請 function 把傳回值放盒裡。

數值範例:

void retsomething(int *a,int *b) {
  *a=123;
  *b=456; }

int a,b;
retsomething(&a,&b);
printf("a is %4d,b is %4d.\n",*a,*b);

字串範例:

void retsomething(char *a,char *b)
{
  strcpy(a,"老貢生");
  strcpy(b,"太雞婆");
}
char a[16],b[16];
retsomething(a,b);
printf("%s,%s。\n",a,b);

資料結構範例:

strucstruct node {
  int age;
  char name[12];
};
                                                                                                 
struct node retsomething(char *name, int age)
{
  struct node np;
  np.age=age;
  strcpy(np.name,name);
  return np;
}
                                                                                                 
  struct node nstock;
  char ns[12]="老貢生";
                                                                                                 
  nstock=retsomething(ns,44);
  printf("My name is %s,age is %d.\n",nstock.name,nstock.age);

位址回傳:

#include <stdio.h>
#include <string.h>
                                                                                                 
struct node {
  int age;
  char name[12];
};
                                                                                                 
struct node *retsomething(int num)
{
  int i;
  struct node *np;
  struct node *skip;
                                                                                                 
  np=(struct node *)malloc(sizeof(struct node)*num);
  skip=np;
  for (i=0;i<num;i++)
  {
    skip->age=i+20;
    sprintf(skip->name,"A%d 君",i);
    skip++;
  }
  return np;
}
                                                                                                 
int main(int argc, char *argv[])
{
  struct node *nstock;
  struct node *nacc;
  int i,num=3;
                                                                                                 
  nacc=nstock=retsomething(num);
  for (i=0;i<num;i++) {
    printf("My name is %s,age is %d.\n",nstock->name,nstock->age);
    nstock++; }
                                                                                                 
  free(nacc);
}

Important

位址回傳只適用在副函式中用動態記憶配置指令配置的記憶區塊位址,而不能用在副函式一般的變數陣列宣告位址。因為副函式的變數在副函式結束時,被分配的區塊會被系統收回,你無法保證其資料不被覆蓋,這很可能引起嚴重的 bug 。至於用像 malloc 分配的動態記憶體,不會隨副函式結束而被收回,因此,你要記得在使用完畢後,自己在呼叫端程式敘述中用 free 指令釋放之。

2.1.6. 陣列指標的使用

all pass! wrote,c語言內, 如果用:

int data[20][6];
int *p;
p=data;

是錯誤的...為何呢???編譯器好像是提示: 一個二維陣列,不能和一個指標合用..

請改成 int (*p)[6]; 這叫陣列指標,因為 int *p 的位移長度是 4 byte ,而 int data[20][6] 一個元素的位移是 4 byte * 6 。

在 C 語言中,指標必須具有兩項數值,一是記憶體或變數的開始位址,二是位移一個單位的資料需要移動多長的距離。所以 int *p 只能作為一維陣列的指標,二維以上,必須使用陣列指標,他的表示是

type *arrayname[第二維數目][第三維數目]...

除了第一維外,其他維度都必須註明元素個數,才能算出一個 array 位移要移動多少長度。

2.1.7. 雜湊表範例

/* C 語言雜湊表範例 */
#include <stdio.h>
#include <search.h>

int main(int argc, char *argv[])
{
  ENTRY ent,*ep;		/* 雜湊表的資料結構 */
  char k[10],d[256];
  int i;

  if(hcreate(100))
  {
    printf("請輸入關鍵字: ");
    scanf("%s",k);

    printf("請輸入字串值: ");
    scanf("%s",d);

    ent.key=k;			/* 索引字串 */
    ent.data=d;			/* 內容字串 */
    if(hsearch(ent,ENTER))	/* ENTER 參數表示加紀錄到雜湊表 */
    {
      printf("請輸入搜尋鍵: ");
      scanf("%s",k);
      ep=hsearch(ent,FIND);	/* FIND 參數表示搜尋雜湊表符合紀錄 */
      if (ep)
        printf("搜尋到資料[%s]\n",ep->data);
      else
        printf("你沒有鍵入該筆資料\n");
    }
    else
    {
      printf("資料新增失敗\n");
      exit(2);
    }
    hdestroy();
  }
  else
  {
    printf("雜湊表開啟失敗\n");
    exit(1);
  }
  exit(0);
}

2.1.8. 函式指標的用法

寫作業 wrote:假如我把一個 function pointer指向一個function,那可不可以取得這個 pointer所指的地址呢?要如何做呢?謝謝

/* 宣告 function: */

void funA(int d,char *s)
{
  int i;
  for (i=0;i<d;i++) {
    printf("%s in funA",s);
  }
}

void funB(int d,char *s)
{
  int i;
  for (i=0;i<d;i++) {
    printf("%s in funB",s);
  }
}


main()
{
  /* 宣告函數指標 */
  void *(funp)(int,char *);
  int d,a=0;
  char s[20];

  ......

  if (!a)
    funp=funA;
  else
    funp=funB;

  funp(d,s);
}

Note

函數指標不只傳回值型態要一樣,連傳入參數數目和型態也要一樣。如果函數指標是要在數個函數中擇一執行,那麼所有相關函數型態都要一樣。

2.1.9. 常數指標的作用

char *const name_ptr="TEST" 和 char const *name_ptr="TEST" 兩者作用有何不同。

慣性漂移

char const *name_ptr="TEST";表示指標 name_ptr 所指向的位址所包含的內容值不可變,亦即字串 "TEST" 不可被修改。但 name_ptr 本身卻可改變,指向另一個變數位址。

char *const name_ptr="TEST";表示指標 name_ptr 所指向的位址不可變,但字串 "TEST" 可以被修改。

2.1.10. 改變呼叫端指標的指向位址

小州

應該在被呼叫的副函式端使用指標的指標參數:

#include <stdio.h>

  char msg1[]="test1";
  char msg2[]="test2";

  void func(char **p);

  void func(char **p)
  {
     *p = msg2;
  }

  int main()
  {
     char *p=msg1;

     func(&p);

     printf("%s\n",p);

  }

2. 資料轉換

2.2.1. 十六進位字串轉數值

決不輕言放棄 wrote:我想把 char v[]="1000" 這樣的字串轉成數值,不過是十六進位的,請問該如何做?

>請使用 strtol 函式,例如:

int value;
char v[5]="1000";
value=strtol(v,NULL,16);

strtol 最後一個參數是指定進位模式,10 進位用 10, 16 進位用 16, 8 進位用 8。

2.2.2. 十六進位字串轉二進位字串

請問各位,有辦法將16進制數值轉為2進制數值嗎?譬如說,原本資料是 002D 十六進位顯示,希望改成 0000 0000 0010 1101 二進位顯示。

#include <stdio.h>

int main(int argc, char *argv[])
{
  char value[]="002D";  /* 十六位元數字串 */
  int i;
  unsigned long bn,mask;

  bn=strtol(value,NULL,16);     /* 將十六位數字表示式轉為一般數值 */
  mask=1;

  for (i=1;i<(sizeof(unsigned long)*8);i++)
     mask=mask << 1;                    /* 製作位元過濾板,並把他推到最高位。 */

  for (i=(sizeof(unsigned long)*8);i>0;i--) {
    printf("%u",((mask & bn)==0)?0:1);  /* 數值和過濾版做 and 運算,求位元值。*/
    mask=mask >> 1;                     /* 位元真值下移一個位元 */
  }
  printf("\n");
}

2.2.3. 二進位字串轉一般數值

我想請問如何用二進制(binary)的 01010101 這種表示式來輸入數值,請問該如何轉成一般數值使用??

使用位元位移運算子搭配二進位字串表示式來轉成一般數值,試試下面的:

#include<stdio.h>

int bton(char *bs)	/* 位元字串轉數值函式 */
{
  char *bp=bs;
  unsigned char cal=0;
  int ret;

  while (*bp != '\0')
  {
    cal = cal << 1;	/* 數值向高位元位移一單位 */
    switch(*bp)
    {
      case '1': cal++;break;
      case '0':break;
      default :return -1;
    }
    bp++;
  }
  ret=cal;
  return ret;
}

int main(int argc,char *argv[])
{
  char bstring[9];
  int acc;

  printf("請輸入位元格式數字串(限 8 個): ");
  scanf("%s",bstring);

  acc=bton(bstring);
  if (acc < 0)
    printf("binary 數字串格式錯誤\n");
  else
    printf("bit is %s ,dec is %d.\n",bstring,acc);
}

2.2.4. 內碼輸出成中文字串

> 例如在 C 中讀到 0xA1B1 數值,我要把它當做big5碼顯示出該對應的國字,該怎麼做呢?

  • 首先要建立的觀念是其實字元也是一種數值,字串在 C 語言中,不過是一個數值陣列而已。

  • 其次的觀念是不同型態的變數,其實只是不同長度的記憶區塊而已,所以你也可以把一個 int variable 看成是一個 char[4] array。

  • 透過指標的參照運作,你可以把原來變數的內涵值做另外一種意義的解釋。

  • 下面這個依內碼顯示中文字的範例,雖然簡單,卻清楚的利用了上面的觀念來運作,也是 C 語言較其他語言較為擅長處理的項目之一。

      int charnum;
      char str[5];
      char *cp;
    
      printf("請輸入中文字 16 進位之內碼: ");
      scanf("%s",&str);
    
      charnum=strtol(str,NULL,16);
      cp=(char *)&charnum;
      str[0]=*cp++;
      str[1]=*cp;
      str[2]='\0';
    
      printf("%s\n",str);

    2.2.5. 觀察一個 byte 裡的每個 bit 值

    #include <stdio.h>
    
    union byte {
      char n;
      struct bit {
        unsigned char bit0 : 1;
        unsigned char bit1 : 1;
        unsigned char bit2 : 1;
        unsigned char bit3 : 1;
        unsigned char bit4 : 1;
        unsigned char bit5 : 1;
        unsigned char bit6 : 1;
        unsigned char bit7 : 1;
      } bits;
    };
    
    int main(int argc, char *argv[])
    {
      union byte bn;
      char c;
    
      printf("請輸入 255 以下的數字:");
      scanf("%d",&c);
      bn.n=c;
      printf("binary is : %d%d%d%d%d%d%d%d\n",
                          bn.bits.bit7,
                          bn.bits.bit6,
                          bn.bits.bit5,
                          bn.bits.bit4,
                          bn.bits.bit3,
                          bn.bits.bit2,
                          bn.bits.bit1,
                          bn.bits.bit0);
    }

    3. 演算法

    2.3.1. 樂透隨機程式範例

    #include <stdio.h>
    #include <stdlib.h>
    #define TOTAL_NUMBER 42        /* 總共多少組號碼 */
    #define GUEST_NUMBER 6         /* 一次抽出幾個號碼 */
    
    int main(int argc, char *argv[])
    {
      int mgroup[TOTAL_NUMBER],i,tmp,rn;
    
      for(i=0;i<TOTAL_NUMBER;i++)
        mgroup[i]=i+1;
    
      srand(time(NULL));
      for(i=(TOTAL_NUMBER-1);i>0;i--)
      {
        rn=rand() % i;
        tmp=mgroup[i];
        mgroup[i]=mgroup[rn];
        mgroup[rn]=tmp;
      }
    
      for(i=0;i<GUEST_NUMBER;i++)
        printf("%d ",mgroup[i]);
    
      printf("\n");
      exit(0);
    }

    2.3.2. 固定次數,隨機出現。

    別再讓我傷心 wrote: 欲使用 C 的隨機函數產生 0、1及2,並各出現3次,例如:0 1 1 0 2 1 0 2 2 ,請問該如何撰寫?

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
                                                                                                     
    int main(int argc, char *argv[])
    {
      int arr[]={0,0,0,1,1,1,2,2,2};	/* 先製作符合選取比例的隨機抽樣母體 */
      int i,j,k,temp;
                                                                                                     
      k=sizeof(arr)/4;
      srand(time(0));	/* 再對其出現次序做隨機抽樣 */
      for (i=k-1;i>0;i--) { /* 迴圈遞減則每次候選陣列元素減一,亦即選過的不再選。 */
        j=rand()%i;         /* 隨機選取陣列元素 */
                                                                                                     
        temp=arr[i];        /* 將選取到的陣列,與候選陣列最後一個元素內涵值互換。 */
        arr[i]=arr[j];
        arr[j]=temp;
      }
                                                                                                     
      for (i=0;i<k;i++)     /* 輸出隨機選取結果 */
        printf("%d ",arr[i]);
                                                                                                     
      printf("\n");
    }

    2.3.3. 檔案加密的簡單範例

    /*
    filename:mycode
    command:
      加密: mycode 密碼鍵字串 < 加密來源檔 > 加密目的檔
      解密: mycode 密碼鍵字串 < 加密目的檔 > 解密目的檔
    */
    #include <stdio.h>
    #define KEY_LENGTH 3	/* 密碼鍵長度 */
    
    int main(int argc,char *argv[])
    {
      int i,gc;
      char key[KEY_LENGTH];
      char *cp,c;
    
      cp=argv[1];
    
      for (i=0;i<KEY_LENGTH;i++)
      {
         key[i]='\0';
      }
    
      for (i=0;i<KEY_LENGTH;i++)
      {
        if (*cp != '\0')
        {
          key[i]=*cp;
          cp++;
        }
        else
          break;
      }
    
      i=0;
      while ((gc=getchar()) != EOF)
      {
         c=gc;
         c ^= key[i];	/* 把密碼鍵值和來源檔字元做 xor 運算 */
         putchar(c);
         i++;
         if(i >= KEY_LENGTH)
           i=0;
      }
    }

    2.3.4. 動態輸入資料排序

    無聊中之銘言:我想寫一個數字的排序,這個程式有一個功能,就是不能限制我只能輸入幾個數字,我實在想不出來這個功能要如何寫...,可以提示一下嗎?

    # include <stdio.h>
    # include <stdlib.h>
    
    /* 排序用數值結構 */
    struct node {
      int value;
      struct node *next;
    };
    
    void addnode(struct node *);	/* 動態新增不限個數之整數 */
    void nodeshow(struct node *);	/* 顯示升冪排序後的數字串列 */
    void nodeclear(struct node *);	/* 清除動態新增的記憶區塊 */
    
    int main(int argc, char *argv[])
    {
      int num;
      char c;
      struct node *mp =malloc(sizeof(struct node));
    
      mp->next=NULL;
    
      while (1)
      {
        printf("請輸入一個加入排序的正整數: ");
        scanf("%d",&num);
        mp->value=num;
        addnode(mp);
    
        printf("繼續輸入其他數字?(y/n)");
        c=getchar();c=getchar();	/* 連續使用 getchar 兩次是為了清除
        上次數字輸入時結尾的 [enter] 按鍵 */
    
        if ((c == 'n') || (c == 'N' ))
          break;
      }
    
      nodeshow(mp);
      nodeclear(mp);
    }
    
    void addnode(struct node *p)
    {
      struct node *ap;
      struct node *op;
      struct node *new_p=malloc(sizeof(struct node));
      ap=p;
      new_p->value=ap->value;new_p->next=NULL;
    
      if (ap->next == NULL)
      {
        ap->next=new_p;
      }
      else
      {
         while(1)
         {
    	op=ap;
            ap=ap->next;
            if (new_p->value <= ap->value)
    	{
    	  new_p->next=ap;
    	  op->next=new_p;
    	  break;
    	}
    	else
    	{
    	  if (ap->next == NULL)
    	  {
    	     ap->next=new_p;
    	     break;
    	  }
    	}
         }
      }
    }
    
    void nodeshow(struct node *p)
    {
      struct node *ap;
      ap=p;
    
      while(1)
      {
        ap=ap->next;
        printf("%d ",ap->value);
    
        if (ap->next == NULL)
          break;
      }
      printf("\n");
    }
    
    void nodeclear(struct node *p)
    {
      struct node *ap;
      ap=p;
     
      while (p->next != NULL) {
        p=p->next;
        free(ap);
        ap=p;
      }
    
      free(p);
    }

    2.3.5. 簡易賽程表

    想要寫一個有關排賽程的東東(循環賽),會自動排賽程,而每隊都要互相比過一次。

    #include <stdio.h>
    
    void Schedule(char **teams,unsigned int n)
    {
      int i,j,k=1;
      for (i=0;i<n;i++)
      {
        for (j=i+1;j<n;j++)
        {
          printf("第 %d 場: %s  出賽  %s\n",k,teams[i],teams[j]);
          k++;
        }
      }
    }
    
    int main( int argc, char *argv[] )
    {
      char *games[]={"統一獅","兄弟象","味全龍","興農牛","時報鷹","中信鯨"};
      Schedule(games,6);
    }

    2.3.6. 簡易撲克(showhand)

    /* filename:showhand.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define CARDFACE_NO 4	/* 花色種類 */
    #define CARD_SERIAL 13	/* 撲克點數 */
    #define CHOICE_NO 5	/* 發牌數 */
    
    /* 撲克的資料結構 */
    typedef struct pattern {
      unsigned short face;	/* 花色 */
      unsigned short serial;	/* 點數 */
    } Card;
    
    void card_initialize(Card *,int,int);	/* 全副牌初始化 */
    void choose_card(Card *,Card *,int,int);	/* 隨機選牌 */
    void show_card(Card *,char **,char **,int);	/* 顯示選牌資料 */
    void card_calc (Card *,char **,char **);	/* 計算牌型點數 */
    
    Card cardset[CARDFACE_NO * CARD_SERIAL]; /* 全牌紀錄 */
    Card cardchoose[CHOICE_NO];	/* 選牌紀錄 */
    
    int main(int argc,char *argv[])
    {
      char *face[]={"spare","heart","diamond","club"}; /* 花色名稱 */
      char *serial[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
      /* 點數名稱 */
      Card test[CHOICE_NO];	/* 測試特殊牌型用,編譯前可刪除。 */
    
      card_initialize(cardset,CARDFACE_NO,CARD_SERIAL);
      choose_card(cardset,cardchoose,(sizeof(cardset)/sizeof(Card)),
    		  (sizeof(cardchoose)/sizeof(Card)));
      show_card(cardchoose,face,serial,(sizeof(cardchoose)/sizeof(Card)));
      card_calc(cardchoose,face,serial);
    
    /* 測試用敘述,正式編譯前請刪除。 */
    /*
      test[0].face=2;test[0].serial=9;
      test[1].face=3;test[1].serial=10;
      test[2].face=2;test[2].serial=11;
      test[3].face=2;test[3].serial=12;
      test[4].face=2;test[4].serial=8;
      show_card(test,face,serial,(sizeof(test)/sizeof(Card)));
      card_calc(test,face,serial);
    */
    }
    
    void card_initialize (Card *set,int kind,int num)
    {
      int i,total=kind*num;
    
      /* 逐筆把花色點數資料放進 card 變數中。 */
      for (i=0;i<total;i++) {
        set[i].face=i/num;
        set[i].serial=i%num;
      }
    }
    
    void choose_card (Card *src,Card *dest,int count,int num)
    {
      Card *buf[count],*tmp;
      unsigned int i,j;
    
      /* 用指標變數陣列幫忙,在不變動原陣列順序下,加以隨機排列。 */
      for (i=0;i<count;i++)
        buf[i]=&src[i];
    
      srand((unsigned int) time(0));
      /* 以 buf 陣列為抽樣母體,加以隨機化。 */
      for (i=count;i>0;i--)
      {
        j=rand()%i;	/* 除數隨著步進遞減,可以避免重複的抽樣。 */
        tmp=buf[i];
        buf[i]=buf[j];
        buf[j]=tmp;
        /* 藉著 swap 功能,把抽過的陣列元素排除在下次抽取範圍之外。 */
      }
    
      /* 取出隨機化後的資料,放進發牌陣列組中。 */
      for (i=0;i<num;i++) {
        dest[i].face=buf[i]->face;
        dest[i].serial=buf[i]->serial;
      }
    }
    
    void show_card (Card *set,char **f,char **s,int count)
    {
      int i;
    
      printf("Your got card detail is:\n\t");
      /* 依發牌組陣列的紀錄顯示花色和點數 */
      for (i=0;i<count;i++)
        printf("%8s[%2s]",f[set[i].face],s[set[i].serial]);
    
      printf("\n\n");
    }
    
    void card_calc (Card *result,char **f,char **s)
    {
      int i,j,calc[CARD_SERIAL],none=1,same,check;
      /* none 是紀錄是否沒有對子,預設是沒對子。*/
      int sequence[CARD_SERIAL+1],seq;
      /* sequence 加一的目的是要算 10,J,Q,K,A 這種順子牌型。 */
    
      /* calc 陣列是用來計算每種點數在發牌組中出現了幾次,
         這裡先把他門都初始化為零。 */
      for (i=0;i<CARD_SERIAL;i++)
        calc[i]=0;
    
      /* 計算每個點數牌出現幾次 */
      for (i=0;i<CHOICE_NO;i++)
        calc[result[i].serial]++;
    
      printf("Your point is:\n\t");
      for (i=0;i<CARD_SERIAL;i++) {
        /* 比 2 大表示是對子,就顯示出來。 */
        if (calc[i] >= 2) {
          printf("%3s have %2d card",s[i],calc[i]);
          none=0;	/* 出現對子就表示 none 不成立 */
        }
      }
    
      /* none 表示沒有對子,沒有對子就有可能是同花或順子,因此要接著判斷。 */
      if (none) {
        same=1;	/* 預設是同花 */
    
        /* 把點數記數陣列資料放到順子判斷陣列中 */
        for (i=0;i < CARD_SERIAL;i++)
          sequence[i]=calc[i];
        /* 把頭一個 A 記數資料放到 sequence 序列中 */
        sequence[CARD_SERIAL]=calc[0];
    
        check=result[0].face;	/* 同花要五張一樣,因此使用 check 來判斷。 */
        for (i=1;i<CHOICE_NO;i++) {
          /* 有一張不一樣就表示失敗 */
          if (check != result[i].face) {
    	same=0;break;	
          }
        }
    
        if (same) 	/* 依 same 來判斷是否同花 */
          printf("!!%8s five card same color !!\n",f[check]);
    
        /* 判斷是否順子 */
        for (i=0;i<=(CARD_SERIAL-4);i++) {
          /* 預設是順子 */
          seq=1;
          /* 五個一組連續判斷 */
          for (j=i;j<(i+5);j++) {
    	if (sequence[j] == 0) {
    	  seq=0;
    	  break;
    	}
          }
          if (seq) {
    	printf("\t!! Sequence card,from %2d to %2d\n",i,(i+4));
    	break;
          }
        }
      }
    
      printf("\n");
    }

    2.3.7. 選課程式範例

    20人選6門課,每人可填三個志願,每門課最多五人;試寫程式分發選課之結果。

    #include <stdio.h>
    #define STUDENT_NUMBER 20	/* 總學生人數 */
    #define CHOICE_ITEM 4	/* 每一學生的選科註記 */
    #define LAST_RESULT CHOICE_ITEM - 1	/* 最後一個元素記載選到的科目 */
    #define CLASS_NUMBER 6	/* 可供選擇的科目數目 */
    #define CLASS_LIMIT 5	/* 每科選修人數上限 */
    
    int student[STUDENT_NUMBER][CHOICE_ITEM];
    /* 設定學生選課紀錄的二唯變數,共 STUDENT_NUMBER 個學生,
       每個學生可選 CHOICE_ITEM - 1 個科目,並保留最後一個 CHOICE_ITEM
       變數,作為選擇判斷後的結果紀錄。 */
    
    int class[CLASS_NUMBER];
    /* 紀錄每一科被選了幾次,超過 CLASS_LIMIT 就不可以再選。 */
    
    char *item[]={"國文","英文","歷史","地理","數學","自然"};
    /* 顯示科目名稱方便選課輸入 */
    
    int get_item()
    {
      int i,choice=0;
      char dummy;
    
      while (choice == 0)
      {
        for (i=0;i<CLASS_NUMBER;i++)
          printf("%d.%s  ",i+1,item[i]);
        printf("\n");
    
        printf("請按編號輸入選課號碼: ");
        scanf("%d",&choice);
        scanf("%c",&dummy);
    
        if (choice < 1 || choice > CLASS_NUMBER)
        /* 限制選課輸入在 1 ~ CLASS_NUMBER 之間 */
          choice=0;
      }
    
      return choice;
    }
    
    void student_choice()
    {
      int i,j,choice,calc;
    
      for (i=0;i<STUDENT_NUMBER;i++)
      {
        calc=0;
        while (calc < (CHOICE_ITEM -1))
        {
          printf("編號 [ %d ] 的學生選擇課程,",i+1);
          printf("還有 [ %d ] 次選課的機會。\n",(CHOICE_ITEM - 1 - calc));
          choice=get_item();
    
          for (j=0;j < (CHOICE_ITEM - 1);j++)
          {
            if (student[i][j] == choice)
            /* 避免同樣的課程選擇兩次 */
    	{
    	  choice = 0;
    	  break;
    	}
          }
    
          if (choice <= 6 && choice >=1)
          {
            student[i][calc]=choice;
    	calc++;
          }
        }
      }
    }
    
    void choice_list()
    {
      int i,j;
    
      for (i=0;i < STUDENT_NUMBER;i++)
      {
        printf("第 %d 名學生選課資料\t",i+1);
        for (j=0;j < CHOICE_ITEM;j++)
        {
          printf("%s  ",item[student[i][j]-1]);
        }
        printf("\n");
      }
    }
    
    main (void)
    {
      int i,j;
    
      for (i=0;i<STUDENT_NUMBER;i++)	/* student 陣列初始化 */
        for (j=0;j<CHOICE_ITEM;j++)
          student[i][j]=0;
    
      for (i=0;i<CLASS_NUMBER;i++)	/* class 陣列初始化 */
        class[i]=0;
    
      student_choice();	/* 執行選課輸入 */
    
      for (i=0;i<STUDENT_NUMBER;i++)	/* 執行排課判斷 */
      {
        for (j=0;j < (CHOICE_ITEM - 1);j++)
        {
          if (class[student[i][j]-1] < CLASS_LIMIT)
          /* class 課程項目只有不超過選修人數,才可以開放選修。 */
          {
            class[student[i][j]-1]++;
    	student[i][LAST_RESULT]=student[i][j];
    	break;
          }
        }
      }
    
      choice_list();	/* 逐一列出每個學生選課及選到那科的資料,
      非必要,可以不做這顯示也可以。 */
    
      /* 顯示最後課程排定結果 */
      printf("\n\n");
      for (i=1;i<=CLASS_NUMBER;i++)
      {
        printf("選修到 %s 課程的學生有:  ",item[i-1]);
        for (j=0;j<STUDENT_NUMBER;j++)
        {
           if (student[j][LAST_RESULT] == i)
    	 printf("%d號 ",j+1);
        }
        printf("\n");
      }
    
      printf("需要重新選擇課程的學生有:  ");
      for (j=0;j<STUDENT_NUMBER;j++)
      {
         if (student[j][LAST_RESULT] == 0)
           printf("%d號 ",j+1);
      }
      printf("\n");
    }

    4. 字串處理

    2.4.1. C 語言的字串處理特色

    C 語言裡沒有字串變數這種東西,只有字元陣列。

    char carr[] = "apple"; 這是 carr 字串陣列宣告,只有在宣告中才可以使用 '=' 這個指定值算符。

    carr="orange"; 這是錯誤的寫法,因為陣列變數無法用 '=' 符號指定其內涵值。

    要更改字元陣列的內涵值,必須使用字串處理功能函式,如:strcpy(carr,"orange");

    一個完整的字串必須使用一個字元陣列,並以一個 '\0' 字元做結尾,所以一個完整的字串,最少要有兩個 char 元素,也就是 char carr[2 以上的數值]。

    一個字元陣列不管是否有排列使用到最後一個元素,一律遇到 '\0' 字元算結束,萬一整個字元陣列都無 '\0' 字元出現,將在記憶體中無限延伸,直到遇到 '\0' 字元為止。不在字元陣列中放入適當的 '\0' 字元,將會顯示不相關的記憶體內容。

    2.4.2. 消除連續空白字元

    譬如原始字串是:

    "      I\tlove       you.     \nSo much.    "

    我希望剔除字串內的空白字元,最後顯示成這樣:

    "I love you. So much."

    請問該如何做?

    你可以考慮套用下面範例中,cutspace 函式的作法。

    /*
      Filename:cutspace.c
      Compile :gcc cutspace.c -o cutspace
      Function:將字串中重複的空白字元加以清除。(tab 與 enter 字元亦算做空白鍵)
    */
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    
    /* 去除重複空白字元函數
    參數 sour : 來源字串
    參數 dest : 目的字串(dest 字串長度至少要等於或長於 sour) */
    int cutspace(char *sour,char *dest) {
      int last, now;		/* 字元比較運算子 */
      char *sp, *dp;		/* 字串指標 */
    
      sp=sour; dp=dest;
      now=last=*sp;			/* 先將運算子都設為 sour 字串的第一個字元 */
      *dp='\0';
    
      while(now != '\0') {
        if(!isspace(now) || !isspace(last)) {	/* 現字元或前字元非空白字元才加以保留 */
          if ((now == '\t') || (now == '\n'))	/* '\t' 和 '\n' 字元都算是 ' ' 空白字元 */
            now=' ';
    
          *dp=now;
          dp++;
        }
        last=now;			/* 現字元推入上一字元,運算子取下一字當現字元 */
        sp++;
        now=*sp;
      }
    
      if(*dp == '\0')		/* dp 內涵值為 0 ,表傳入字串無非空白字元。 */
        return 1;
    
      if(*(dp-1) == ' ')		/* 消去行尾的空白字元 */
        *(dp-1)='\0';
      else
        *dp='\0';
    
      return 0;
    }
    
    int main(int argc, char *argv[]) {
      char ca[256]="      I\tlove       you.     \nSo much.    ";
      char cb[256];
      cutspace(ca,cb);
    
      printf("%s--\n",cb);
    }

    2.4.3. 標記字串範例

    輸入文字資料 ,然後加上已經自定的標籤,輸出成xml格式的檔案。 請問該怎麼下手去做呢 ?

    /*
     * FileName:tagm,c
     * Function:將輸入字串加上指定的標籤
     * Execute Command:
     *   1.無字串內容之空標籤: tagm <標籤名稱>
     *   2.有字串內容的標簽: tagm <標籤名稱> <標籤內容>
     * Compile Command: gcc tagm.c -o tagm
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define HEADCHAR "<"	/* 標籤前置字元 */
    #define ENDCHAR ">"	/* 標籤結尾字元 */
    #define ENDMARK "/"	/* 後標籤標示字元 */
    
    /*
     * 空標籤輸出函式
     * 參數說明:
     * ts -- 標籤名稱字串
     */
    void singletag(char *ts)
    {
      char *dynp;
      int calc=0;
    
      /* 計算標籤字串加前置尾置字元之總長度 */
      calc+=strlen(ts);
      calc+=strlen(HEADCHAR);
      calc+=strlen(ENDCHAR);
      calc+=strlen(ENDMARK);
      calc+=2;
    
      /* 依總長度配置記憶體 */
      dynp=(char *) malloc(calc * sizeof(char));
    
      /* 將標籤字串加上頭尾字元 */
      dynp=strcat(dynp,HEADCHAR);
      dynp=strcat(dynp,ts);
      dynp=strcat(dynp," ");
      dynp=strcat(dynp,ENDMARK);
      dynp=strcat(dynp,ENDCHAR);
    
      printf("%s\n",dynp);	/* 輸出結果 */
      free(dynp);		/* 解除動態記憶體內容 */
    }
    
    /*
     * 包含標籤內容字串輸出函式
     * 參數說明:
     * ts -- 標籤名稱字串
     * cs -- 標籤內容字串
     */
    void headendtag(char *ts, char *cs)
    {
      char *dynp;
      int calc=0;
    
      calc+=strlen(ts);
      calc+=strlen(cs);
      calc+=2 * strlen(HEADCHAR);
      calc+=2 * strlen(ENDCHAR);
      calc+=strlen(ENDMARK);
      calc+=1;
    	
      dynp=(char *) malloc(calc * sizeof(char));
      dynp=strcat(dynp,HEADCHAR);
      dynp=strcat(dynp,ts);
      dynp=strcat(dynp,ENDCHAR);
      dynp=strcat(dynp,cs);
      dynp=strcat(dynp,HEADCHAR);
      dynp=strcat(dynp,ENDMARK);
      dynp=strcat(dynp,ts);
      dynp=strcat(dynp,ENDCHAR);
    
    
      printf("%s\n",dynp);
      free(dynp);
    }
    
    int main(int argc, char *argv[])
    {
      char *tagp,*contp;
      int mode=0;
    
      /* 依據輸入參數字串分配給標籤名稱及標籤內容 */
      switch (argc)
      {
         case 2 :
           /* 只有一個參數表示是空標籤 */
           mode=1;tagp=argv[1];
           break;
         case 3 :
           /* 兩個參數表示有標籤內容,參數 1 標籤名稱,參數 2 標籤內容 。 */
           mode=2;tagp=argv[1];contp=argv[2];
           break;
         default :
           fprintf(stderr,"Command type fault : tagm <tag string> <content string>\n");
           exit(1);
      }
    
      switch (mode)
      {
        case 1 :
          /* 執行空標籤輸出函式 */
          singletag(tagp);
          break;
        case 2 :
          /* 執行有標籤內容字串輸出函式 */
          headendtag(tagp,contp);
          break;
      }
      exit(0);
    }

    5. 輸出輸入

    2.5.1. fwrite如何一次寫入兩個變數?

    把握現在,瞻望未來 wrote:請問一下,fwrite如何一次寫入兩個變數?謝謝告知!

    struct mix {
      int length;
      char sentence[256];
    } mixvar;
    
    int twoL[2];
    
    FILE *stream
    stream=fopen("filepath","w");
    
    /* 一次寫入兩個同型變數 */
    fwrite(twoL,sizeof(int),2,stream);
    
    /* 一次寫入兩個不同型變數 */
    fwrite(&mixvar,sizeof(struct mix),1,tream);

    2.5.2. 一次讀入整個檔案的方法

    All You Can Eat ? wrote:請問各位 C 要怎麼樣複製檔案比較快呢?我現在是使用 fopen 再 fputc 來複製檔案,可是感覺很沒有效率 有其他更好的方法嗎?還是改用 fwrite 會比較好呢?

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
      FILE *fp,*op;
      int calc=0;
      char *cp;
    
      fp=fopen("/etc/passwd","r");
      fseek(fp,0,SEEK_END);		/* 檔案指標移到檔尾 */
      calc=ftell(fp);		/* 報告檔尾位置以取得檔案大小 */
    
      if(calc != -1)
      {
        cp=(char *)malloc(calc);	/* 依據檔案大小宣告記憶區塊 */
        fseek(fp,0,SEEK_SET);	/* 檔案指標移到開頭準備複製 */
        fread(cp,calc,1,fp);	/* 依據檔案大小把資料一次讀入 */
    
        op=fopen("./mycount","w");
        fwrite(cp,calc,1,op);	/* 一次寫入資料 */
    
        fclose(op);
        free(cp);
        printf("檔案複製成功 !!\n");
      }
      else
        printf("檔案開啟錯誤 !!\n");
    
      fclose(fp);
    }

    上面範例這種讀入一整個檔案,再一次寫出的方式,是否真比 char by char 逐字檔案 IO 快速,並未經實地測試比較。不過,把整個檔案都抓到記憶體中來處理,其實是一個容易把系統搞當掉的程式,因為萬一碰到個數百 mega 的大檔案,一個 copy 動作,會拉垮所有其他在系統中執行的程式。

    2.5.3. 檔案 16 進位編碼顯示器範例

    /* 檔案名稱:charshow.c
       編譯命令:gcc charshow.c -o charshow
       命令範例:charshow inputfilename      */
    
    #include <stdio.h>
    #include <ctype.h>
    #include <string.h>
    #define LINE_LENGTH 16		/* 一行幾個字 */
    
    int main(int argc, char *argv[])
    {
      char *fname,c;
      /* xbuf:置放檔案字元 16 進位顯示字串
         cbuf:置放正常列印字元顯示字串
       */
      char *cp,cbuf[LINE_LENGTH+1],xbuf[LINE_LENGTH*3 + 1];
      FILE *fp;
      /* n:控制一行顯示長度記數
         len:目前行在檔案位置記數
      */
      int n=0,len=0;
    
      if(argc < 2 )
      {
        printf("parameter wrong:no input file\n");
        exit(1);
      }
    
      fname=argv[1];
      fp=fopen(fname,"r");
      /* 以上開啟輸入檔判斷 */
    
      while (!feof(fp))
      {
        if(n == 0)		/* 如果一行總顯示字元歸零 */
        {
          bzero(cbuf,sizeof(cbuf));		/* 清除列印字元顯示串 */
          bzero(xbuf,sizeof(xbuf));		/* 清除16 進位字元顯示串 */
          cp=xbuf;				/* 字串指標回歸開頭 */
          printf("\n%04x",len);		/* 顯示目前行在檔案位置 */
        }
    
        c=fgetc(fp);			/* 從檔案中取出一字元 */
    
        sprintf(cp,"%02x ",(unsigned char)c);	/* 寫入 16 顯示串 */
        cp += 3;					/* 每字元佔用 3 個空間 */
        /* %02x:x 表 16 進位顯示,2 表示佔兩個字元寬度,0 表示不滿兩字前面補
    零 */
    
        if(isgraph(c))		/* 判斷是否為可列印字元 */
          cbuf[n]=c;		/* 是就直接將編碼寫進一般字串 */
        else
          cbuf[n]='.';		/* 不是就改成 '.' 字元寫入 */
        n++;len++;			/* 行字數累計進一,檔案字數累計進一。 */
    
        if(n >= LINE_LENGTH)	/* 如果行字數累進值超過上限 */
        {
          /* 標準輸出顯示 16 字串及一般字串
             %-*s:s 顯示字串,* 設定顯示寬度,不直接寫寬度數目,而
                  使用 '*' 字元,會把參數 LINE_LENGTH*3 數值抓進來
                  當寬度設定,- 字元靠左顯示。
          */
          printf("  %-*s %-*s",LINE_LENGTH*3,xbuf,LINE_LENGTH,cbuf);
          n=0;			/* 行字數記數值歸零 */
        }
      }
    
      /* 輸出最後一行資料 */
      printf("  %-*s %-*s\n",LINE_LENGTH*3,xbuf,LINE_LENGTH,cbuf);
      fclose(fp);
      exit(0);
    }

    2.5.4. 動態不等長資料輸入的讀取

    如果一個程式可讓程式輸入長度不等,而且數目不等的字串資料,請問該如何為輸入資料字串安排大小不等的記憶體。

    /*
     * filename:dynstring.c
     * compiled:gcc -o dynstring dynstring.c
     * command:dynstring < 資料來源檔路徑
     * function:全動態配置記憶體區塊,文字檔欄位分析程式
     */
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #define STRING_LENGTH 256
    /*
     * 這是整個程式唯一需要事先設定長度的地方,類似磁碟上
     * sector 的作用,可以取任意值,但大抵上長度愈大,有利大檔處理,
     * 但較耗記憶體。
     */
    #define FIELD_DELIMETER ' '
    
    /* 設定動態記憶體串列結構 */
    struct m_string
    {
      char string[STRING_LENGTH];
      struct m_string *next;
    };
    
    /* 增加一塊動態記憶體 */
    struct m_string *add_newstring(void)
    {
      struct m_string *mp;
      mp=(struct m_string *)malloc(sizeof(struct m_string));
      bzero(mp->string,STRING_LENGTH);
      mp->next=NULL;
      return mp;
    }
    
    /* 把所有序列動態記憶體串成一個大 buffer */
    char *get_buffer(struct m_string *mp)
    {
      struct m_string *bp;
      int calc=1,i=0;
      char *buffer,*cp,*sp;
    
      bp=mp;
      while(bp->next != NULL)	/* 如果動態區塊下還有子區塊,記數加 1 */
      {
        calc++;
        bp=bp->next;
      }
      /* 看一共用了幾塊動態記憶體決定總 buffer 要多大 */
      buffer=(char *)malloc(STRING_LENGTH*calc);
      bzero(buffer,STRING_LENGTH*calc);	/* buffer 初始化 */
      cp=buffer;
      bp=mp;
      /* 逐 char 把 m_string 的資料搬到 buffer 上 */
      while(1)
      {
        /* 如果搬移量超過一個 m_string 大小 */
        if(i >= STRING_LENGTH)
        {
          i=0;
          /* 有子區塊就切換子區塊,否則結束搬移動作。 */
          if (bp->next == NULL)
    	break;
          else
            bp=bp->next;
        }
        *cp=bp->string[i];
        cp++;
        i++;
      }
      return buffer;
    }
    
    /* 釋放整個動態串列記憶體區塊 */
    void clear_string(struct m_string *mp)
    {
      if (mp->next != NULL)
        clear_string(mp->next);
      free(mp);
    }
    
    /* 這裡是最後欄位資料處理動作設定處 */
    void field_print(char *sp)
    {
      printf("[%s]  ",sp);
    }
    
    /* 把一行資料按分隔字符切成欄位 */
    void fields_cut(char *sp,char sep)
    {
      struct m_string *start,*prev,*now;
      int i=0;
      char *cp,*buf;
    
      cp=sp;
      now=add_newstring();	/* 取得一塊動態記憶體 */
      start=prev=now;	/* 先讓開始區塊,前一區塊,現在區塊指在同一位置。 */
    
      while(*cp != '\0')
      {
        /* 行字串中有分隔字符的處理動作 */
        if (*cp == sep)
        {
          i=0;
          buf=get_buffer(start);	/* 取得所有區塊的綜合 buffer 資料 */
          field_print(buf);		/* 進行欄位資料處理 */
          clear_string(start);	/* 釋放目前欄位佔用的動態記憶體串列 */
          free(buf);		/* 釋放綜合 buffer 區塊記憶體 */
          now=add_newstring();	/* 重新取得一個新的記憶體區塊,為下個欄位準備儲存位置。 */
          start=prev=now;
          cp++;
          continue;
        }
        /* 如果一個欄位資料大小超過 m_sring 大小 */
        if (i >= STRING_LENGTH)
        {
          i=0;
          prev=now;			/* 把目前區塊資料推給前一個區塊 */
          now=add_newstring();	/* 目前區塊宣告一塊新 m_string */
          prev->next=now;		/* 設定目前區塊是前一區塊的子區塊 */
        }
        now->string[i]=*cp;
        i++;
        cp++;
      }
    
      /* 最後一個分隔字符後的欄位資料在此輸出 */
      buf=get_buffer(start);
      field_print(buf);
      clear_string(start);
      free(buf);
    }
    
    int main()
    {
      struct m_string *start,*prev,*now;
      int c,i=0;
      char *buf;
    
      now=add_newstring();
      start=prev=now;
      /* 前面幾行變數設定與 field_cut 函式大同小異,請自行參考。 */
    
      /* 逐 char 把標準輸入讀到結束 */
      while((c=getchar()) != EOF)
      {
        /* 一行做一個處理段落 */
        if (c == '\n')
        {
          i=0;
          buf=get_buffer(start);		/* 取得一行所有資料 */
          fields_cut(buf, FIELD_DELIMETER);	/* 進行分欄處理 */
          printf("\n");
          /* 以下與 field_cut 用意寫法類似 */
          clear_string(start);
          free(buf);
          now=add_newstring();
          start=prev=now;
          continue;
        }
        if (i >= STRING_LENGTH)
        {
          i=0;
          prev=now;
          now=add_newstring();
          prev->next=now;
        }
        now->string[i]=c;
        i++;
      }
    }

    6. 系統呼叫

    2.6.1. 如何顯示系統時間

    沉默 wrote:請問一下,C的程式語言裡面有沒有辦法可以紀錄時間點阿?就是像scanf,可以把現在的時間點儲存起來?

    #include <stdio.h>
    #include <time.h>
    
    int main(int argc, char *argv[])
    {
      time_t timep;
      time(&timep);
      printf("%s\n",ctime(&timep));
    }

    7. C 語言的編撰工具

    2.7.1. trace C source

    老貢生:最近因為研究上的需要,展開了 trace linux 上的 C source 的苦差。很苦惱的事當一個程式 include 了一大堆的 H ,又 link 一大堆的 O 時,常常一個副函式的呼叫,他的 source 卻不知藏在那個檔案裡,一個個打開看,慢慢找真是折磨人。有沒有啥辦法,能快速找出整個 project 中,一個特定的 function ,是在那個檔案,那個位置上?