顯示具有 零錯誤程式 標籤的文章。 顯示所有文章
顯示具有 零錯誤程式 標籤的文章。 顯示所有文章

2013年8月30日 星期五

善用自動偵錯工具

compiler,它能夠偵錯功能到底有那些
 1.文法(while(i<=j) error meg:(i
 2.設計or 邏輯 (itoa(int i,char *str)error msg:itoa fails when i is -32768
 3.演算法(memcpy(malloc(len),str,len) error msg:memcpy fails when malloc return NULL

不太可能有compiler具有上述的功能,那要如何避免呢?及如何對上述來進行除錯呢?

1.1 善用compiler的預警功能
 "有些語法本身雖然合法,但是卻不常使用,通常是你誤用"
  ex:
while (i
   k++;
 本來while是對k動作的,但你在while行尾誤加";".(這行為誤寫,但有時它為正確的,要去判斷)
 若你要避免此一警告訊號,可以
 while(i
 NULL;

要寫出零錯誤的程式,最重要的就是要熟悉各種錯誤發生的原因.
  • 要怎樣才能讓錯誤在內部測試階段即能自動浮上?
  • 我又要如何避免犯同樣的錯誤?
"更完整的測試".不過這個做法即不"自動",而且也沒有達到"預防".

    本書將提出一些實用的技巧及規範,用以減少所有可能的錯誤.
  "規範"不是固定的法則.就像是"goto"的限制一樣:大部份的時候我們應謹守,但是如果有更好的方法,也可以打破.

變數及函式的名稱
匈牙利命名慣例迫使所有的名稱都依照資料型別來命名,例如:

 char       ch;
 byte       b;
 flag        f;
 symbol  sym;
這種慣例並沒有嚴格規定每一種資料型別一定得用怎樣的縮寫,只要我們堅守同一種型別只用同一個縮寫來命名即可.
 char     *pch;
 byte     *pb;
 flag      *pf;
 symbol *psym;
 char    **pch;

 這種拼湊的資料名稱不容易唸出,但是這樣子的命名摜例可以允許程式師在型別之後接一兩個有意義的字,若再加上其單字開頭為"大寫".這樣子就可以改進其"可讀性"

char *strcpy( char *pchTo, char* pchFrom);
 不過這樣子又引發一些問題,因為匈牙利命名慣例的目的是要強調名稱的涵意,所以要把"資料型別"得交代清楚;不過卻漏了形容資料"實際被宣告為什麼用途".
其strcpy中的參數,實際上是指向一個以空字元(null 字元,'\0')結束的字串.故可改為
  char *strcpy( char *strTo, char* strFrom);
這樣子,str仍是字元指標,不過在見到這個名稱時,會更容易聯想到它不只指向一個字元,而是一個字串.

--------------------
 使用匈牙利的好處,就是可以更容易的解讀包含指位器的表示式.
 *ppb=pbNew
 在這行中,可以暫時把成對的*(代表位址的內容)和p(代表位址)相低,故成為
 pb=pbNew; //這樣子,兩邊的式子型別,即可相等.
 同樣子的,&,->也可;
 pb=&bTest; //p與&
 b=psysTest->bLeng; //p和->相抵成 "."

---------------------------
 認識本書所指的"錯誤"
 "bug"分成二大類:
1.還在程式發展過程中所發生的錯誤,
2.當自認完成某項能之後,成為漏掉的.

 一般,程式設計師可以已完成的程式中以查閱的方式將須修改的部份找出來(check out);不過與我們在圖書館查閱書籍不同的是,在圖書館借書,我們是將原書直接借出來;但在查閱程式時,只是從程式控制系統將原穚複製一份,而不更動原來的.這樣子就可以確保程式設計師在擴增新功能時,不至於影響原有的程式主檔;等到程式師將修改的部份完成後並且確認無誤後,再將"借閱"的程式還給控制系統.

 本書所提到的錯誤,指就是那些混進主檔內的錯誤;因為它們在發展過程被疏漏了,所以一直保持在程式中.

2012年9月5日 星期三

程式設計專家手冊(the practice of programming) 筆記1

第一章 風格
主要的議題有: 描述性的名稱、表示式的清晰、直觀的控制流程(control follow)、程式碼和註解的可讀性以及一玫性和利用慣用法。
良好的風格應該是一種習慣。如困你從一開始撰寫程式時就考慮到風格問題,而且花時間去修改,那麼就可以培出良好的習慣。一但出習慣,就能在下意識的狀庇下處理許多細節,即使程式碼是在壓力下趕出的。

第二章 演算法與資料結構
   選擇演算法時,可先評估各種可能的演算法和資料結構。及其輸入的資料量大小及性質(成長否?)來排除會因資料大小而需改變的演算法。
若許可應先採用芋種函式庫及語言本身的功能,若無法逹成時,則先以最簡單方法處理。並加以測試是否可符合要求(不合時再修改)
   而其資料結構種類(串列、樹等),對於特定的環境下對效能的影響很大。其各種類資料結構都有基本的操作(建立、新增及刪除等)

而每個操作都有一個預期的運算時間,它表示著對於資料型庇對於特別應用的合適程度。如陣列的存取為O(1)常數時間存取,但其新增刪除則需O(N)。
以此來做為問題的演算法及資料結構的選取。

2012年6月29日 星期五

Top-Down程式策略

程式變數的功能和用途。
1.計數器(counter):用來標示次數、序號,如工作次數和資料的序號。
2.累積器(ccumulator):用來存放計算結果、累算結果。
3.旗標(flag、indicator):用來標示狀態。
其中計數器為進行累加、減的變數。為能清楚表現其次序、經常執行加一或減一的運算。;
而累積器則是用來暫存各種累算的結果,無法由計數器來得。
//每仰臥起坐,每得相對的$.

獎金=0;

for(口令=1;口令<=100;口令=口令+1)

做一次仰臥起坐;

獎金=獎金+口令;

}

口令為計數值,每變化一次即為為仰臥起坐。

口令也為累積器,代表做過的仰臥起坐總數。

獎金為累積器,代表為其獎金總數。(已無法直接由計數器(口令)的最終值推算出來。

而在旗標,標示不同狀態來執行不同程序,而在設定旗標的初始化值的設定上。

需依程式來決定,初始值否定原則


//任意指定127給整數變數no,判斷是否為質數

1.判斷(no-1)是否可整除no,如果可以,no就不為質數

2.判斷(no-2)是否可整除no,如果可以,no就不為質數

 

n-2.判斷(2)是否可整除no,如果可以,no就不為質數


初始值否定原則,故我們先設為no為質數,然後在程序中去檢測來推翻。


no=127;

prime=1; //1為質數;0反之.

for(k=no-1;k>1;k--)

    if(no%k==0) prime=0; //推翻

if(prime)

  為質數

else

  不為質數




Top-Down程式策略:


1.寫出解決問題的外迴圈敘述,必要時可先用中文及(工作)數數描朮要重複執行的工作

2.逐步將迴圈內的中文句子轉成C語言,必要時使用直線方程式決定(工作)變數迴圈變數間的關係(方程式)

即類似系統分析的Top-Down,先寫出解決問題的幾個大步驟,再分若干小步驟,直到可用C描述為之。

而其中直線方程式最為重要。

EX:

印出:

image

1.由最外圍的for,可先用中文及(工作)數數描朮要重複執行的工作


for(i=0;i<5;i++)

{

印出空白;

印出*;

/n;

}

2.轉成C語言,必要時使用直線方程式決定(工作)變數迴圈變數間的關係(方程式)

(i可由0開始,除了在C語中是以從0開始外。還可方便計算出其直線關係方程。

















i012
" "864
*147

3.其" "中,其之間的直線關係式

8=a*0+b;

6=a+b;

a=-2; b=8;

其*中,其之間的直線關係式

1=a*0+b;

4=a+b;

b=1,a=3

4.

for(i=0;i<5;i++)

    印出(-2i+8)個" ";

    印出(3i+1)個*;

    \n;

5.轉換成C


#include <stdio.h>

#include <stdlib.h>

 

int main(int argc, char *argv[])

{

  int i;

  for(i=0;i<5;i++)

  {

  //space=-2i+8;

  int j;

  for(j=0;j< -2*i+8;j++) 

    printf(" ");

  //start=3i+1    

  for(j=0;j<3*i+1;j++)  

    printf("*"); 

  printf("\n"); 

                  }                   

  system("PAUSE");    

  return 0;

}



EX:求面積:

求y=x^2和y=0以x=1所來之面積

1.求其切割的次數

   因為最好為可整除的,故令1/5

   故為

iSum=0;

   for(i=0;i<=5;i++)


{

所在的面積,並做累加的動作 。y=x^2

}

2.其i與x之間的線性方程.












i013
y0.20.40.6

  故為z=0.2i+0.2

iSum=0;

   for(i=0;i<=5;i++)


{

x=(0.2*i+0.2); //x

y=x*x;//y

sum=sum+y*0.2;

}

//在未接觸Bottom-UP程式策略的什都是使用Top-Down程式策略來撰寫程式。

且少先去規劃其程式流程,而在Top-Down程式策略與規劃流程圖一樣,需要程式的經驗、直觀與巧技。

所以用Top-Down時,若寫不出時,可改以Bottom-UP程式策略。

2012年6月26日 星期二

queue

佇列;

定義

,只允許在一端進行插入操作,而在另一端中進行刪除操作的線性表(有限、有序)
其為一種先進先出(FIFO,first in first out)的線性表。
q=(a1,a2,...,an-1,an);則可設定a1為首,而an為尾。
image

queue的ADT(抽象資料類型)

ADT queue

Data

    元素為相同的類型,相鄰元素具有前後關係.

Operater

    InitQueue(*Q);//初始化,建立一個空queue

    DestoryQueue(*Q);//若queue存在,則刪除

    ClearQueue(*Q);//清除queue.

    QueueEmpty(Q);//若Q存在且為空,則回傳true

    GetHead(Q,*e);//若queue且非空,則e傳回Q首

    EndQueue(*Q,e);//若queue存在,插入新元素e到queue中.其e成為queue尾

    DeQueue(*Q,*e);//刪除queue首元素,並用e傳回

    QueueLenght(Q);//反回queue中的元素個數.

 

endADT


因為是線性表,故相同地有


順序儲存



其順序存儲時,有下列問題


image


其加入元素時,其時間O(1)


image


刪除時,則時間變為O(n)。這是因為使其queue首必須要在前面的位置上,所導致的。


而其可以改為下列,用二個變數指標來指定其首'尾.

(front,rear,front則是指下一次可讀(刪)的位置;rear指下一次可寫的位置)
以此來避免每刪一次,則需花O(n)來搬移資料。

image

1.空


image


2.滿


image


3.非空非滿


image


在此時,會出現"虛溢出",指還有空間(0)。但無法存放。


image


而為了改進此我們把rear放在0來形成循環,稱為循環queue


image


但其該如何來判斷其是是滿或空


方法1:


加一個flag


在初始時front==rear,flag=0.為空


若下次出現front==rear且flag==0.則改flag=1(滿)


方2:


保留一個空間。如下圖的情況即代表為滿。


image


image


而其


判斷是否為滿



(rear+1)%QueueSize==front


EX:


rear=0,front=1


((0+1)%5==1)?滿:非滿


rear=4,front=0


(4+1)%5==0? 滿


rear=4,front=1


(4+1)%5==1?非滿





長度



(rear-front+QueueSize)%QueueSize




typedef int QElemType;

typedef struct

{

QElemType data[MAXSIZE];

int front;

int rear;

}SqQueue;




Status InitQueue(SqQueue *Q)

{

Q->front=0;

Q->rear=0;

return OK;

}




int QueueLength(Q)

{

return (Q.rear-Q.front+MAXSIZE)%MAXSIZE);




Status EnQueue(SqQueue *q,QElemType e)

{

if((q->rear+1)%MAXSIZE==q->front))

  return ERROR;

q->data[q->rear]=e;

q->rear=q->rear+1%MAXSIZE;

}




Status DeQueue(SqQueue *q, QElemType *e)

{

if(q->rear==q->front)

    return ERROR;

*e=q->data[q->front];

q->front=q->front+1%MAXSIZE;

return OK;

}




queue link list


queue link list,與一般的link list之間.差在於 queue link list中,只會記錄其front,rear.及在新增'刪除時的操作的不同.

一般link list

image
  1: typedef int Status; 
  2: 
  3: typedef int QElemType; /* QElemType類型根據實際情形而定,這裡假設為int */
  4: 
  5: typedef struct QNode /* 節點結構 */
  6: {
  7:    QElemType data;
  8:    struct QNode *next;
  9: }QNode,*QueuePtr



而為了以link list來構成queue

則需記錄link list 的front,rear
  1: typedef struct   /* 佇列的鏈結串列結構 */
  2: {
  3:    QueuePtr front,rear; /* 隊首、隊尾指標 */
  4: }LinkQueue;
  5: 


1.空queue

image

2.新增element(rear)

image

3.刪除element(front)

image
  1: #include "stdio.h"
  2: #include "stdlib.h"
  3: #include "io.h"
  4: #include "math.h"
  5: #include "time.h"
  6: 
  7: #define OK 1
  8: #define ERROR 0
  9: #define TRUE 1
 10: #define FALSE 0
 11: #define MAXSIZE 20 /* 儲存空間初始分配量 */
 12: 
 13: typedef int Status;
 14: 
 15: typedef int QElemType; /* QElemType類型根據實際情形而定,這裡假設為int */
 16: 
 17: typedef struct QNode  /* 節點結構 */
 18: {
 19:    QElemType data;
 20:    struct QNode *next;
 21: }QNode,*QueuePtr;
 22: 
 23: typedef struct      /* 佇列的鏈結串列結構 */
 24: {
 25:    QueuePtr front,rear; /* 隊首、隊尾指標 */
 26: }LinkQueue;
 27: 
 28: Status visit(QElemType c)
 29: {
 30:   printf("%d ",c);
 31:   return OK;
 32: }
 33: /* 初始化一個空佇列Q */
 34: Status InitQueue(LinkQueue *Q)
 35: {
 36:   Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
 37:   if(!Q->front)
 38:     exit(OVERFLOW);
 39:   Q->front->next=NULL;
 40:   return OK;
 41: }
 42: 
 43: /* 銷毀佇列Q */
 44: Status DestroyQueue(LinkQueue *Q)
 45: {
 46:   QueuePtr p;
 47:   //已為空Q
 48:   if(Q->front==Q->rear)
 49:     return OK;
 50:   else
 51:   {
 52: 
 53:   }
 54: }
 55: /* 將佇列Q清空 */
 56: Status ClearQueue(LinkQueue *Q)
 57:   {
 58:   if(Q->front==Q->rear)
 59:     return OK;
 60:   QueuePtr p=Q->front->next;
 61:   QueuePtr q;
 62:   Q->rear=Q->front;
 63:   //**
 64:   Q->front->next=NULL;
 65:   while(p)
 66:   {
 67:     q=p->next;
 68:     free(p);
 69:     p=q;
 70:   }
 71:   return OK;
 72:   }
 73: /* 若佇列Q為空佇列,回傳TRUE,否則回傳FALSE */
 74: Status QueueEmpty(LinkQueue Q)
 75:   {
 76:     if(Q.front==Q.rear)
 77:       return TRUE;
 78:     else
 79:       return FALSE;
 80:   }
 81: /* 求佇列長度 */
 82: int QueueLength(LinkQueue Q)
 83:   {
 84:     int iCo=0;
 85:     QueuePtr p=(Q.front)->next;
 86:     for(iCo=0;p!=NULL;p=p->next,iCo++)
 87:       ;
 88:     return iCo;
 89:     //  p=Q.front;
 90:     //  while(Q.rear!=p)
 91:   }
 92: /* 若佇列Q非為空,用e返回佇列Q的隊首元素,回傳TRUE,否則回傳FALSE */
 93: Status GetHead(LinkQueue Q,QElemType *e)
 94:   {
 95:   if(Q.rear==Q.front)
 96:     return ERROR; //emty
 97:   QueuePtr p;
 98:   p=(Q.front)->next;
 99:   *e=p->data;
100:   return OK;
101:   }
102: /* 插入元素e為Q的新的隊尾元素 */
103: Status EnQueue(LinkQueue *Q,QElemType e)
104: {
105:   QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
106:   if(p==NULL)
107:     return ERROR;
108:   p->data=e;
109:   p->next=Q->rear->next;
110:   Q->rear->next=p;
111:   Q->rear=p;
112:   return OK;
113: }
114: /* 若佇列不空,刪除Q的隊首元素,用e回傳其值,並回傳OK,否則回傳ERROR */
115: Status DeQueue(LinkQueue *Q,QElemType *e)
116: {
117:   if(Q->rear==Q->front)
118:     return ERROR; //emty
119:   QueuePtr p;
120:   p=Q->front->next;
121:   *e=p->data;
122:   Q->front->next=p->next;
123:   //**
124:   if(Q->rear==p)
125:     Q->rear=Q->front;
126:   free(p);
127:   return OK;
128: }
129: /* 從隊首到隊尾依次對佇列Q中每個元素輸出 */
130: Status QueueTraverse(LinkQueue Q)
131:   {
132:     QueuePtr p=(Q.front)->next;
133:     while(p!=NULL)
134:     {
135:       visit(p->data);
136:       p=p->next;
137:     }
138:     printf("\n");
139:     return OK;
140:   }
141: int main()
142: {
143:   int i;
144:   QElemType d;
145:   LinkQueue q;
146:   i=InitQueue(&q);
147:   if(i)
148:     printf("成功建立了一個空佇列!\n");
149:   printf("是否為空佇列?%d(1:空 0:否)  ",QueueEmpty(q));
150:   printf("佇列的長度為%d\n",QueueLength(q));
151:   EnQueue(&q,-5);
152:   EnQueue(&q,5);
153:   EnQueue(&q,10);
154:   printf("插入3個元素(-5,5,10)後,佇列的長度為%d\n",QueueLength(q));
155:   printf("是否為空佇列?%d(1:空 0:否)  ",QueueEmpty(q));
156:   printf("佇列的元素依次為:");
157:   QueueTraverse(q);
158:   i=GetHead(q,&d);
159:   if(i==OK)
160:    printf("隊首元素是:%d\n",d);
161:   DeQueue(&q,&d);
162:   printf("刪除了隊首元素%d\n",d);
163:   i=GetHead(q,&d);
164:   if(i==OK)
165:     printf("新的隊首元素是:%d\n",d);
166:   ClearQueue(&q);
167:   printf("清空佇列後,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
168:   DestroyQueue(&q);
169:   printf("銷毀佇列後,q.front=%u q.rear=%u\n",q.front, q.rear);
170: 
171:   return 0;
172: }
173: 

stack是只限定在list的尾進行新增、刪除動作的線性表。

queue則是只可在線性表一端做新增;而在另一端做刪除動作。