[ 電腦 ] 09 十二月, 2008 11:06

本篇從 ptt c_and_cpp 轉錄過來的

 請注意:

(1) 本篇旨在提醒新手,避免初學常犯的錯誤(其實老手也常犯:-Q)。
    但不能取代完整的學習,請自己好好研讀一兩本 C 語言的好書,
    並多多實作練習。

(2) 強烈建議新手先看過此文再發問,你的問題極可能此文已經提出並
    解答了。

(3) 以下所舉的錯誤例子如果在你的電腦上印出和正確例子相同的結果,
    那只是不足為恃的一時僥倖。

(4) 不守十誡者,輕則執行結果的輸出數據錯誤,或是程式當掉,重則
    引爆核彈、毀滅地球(如果你的 C 程式是用來控制核彈發射器的話)。


一、你不可以使用尚未給予適當初值的變數。

    錯誤例子:
    int accumulate(int max)    /* 從 1 累加到 max,傳回結果 */
    {
        int sum;    /* 未給予初值的區域變數,其內容值是垃圾 */
        int num;
        for (num = 1; num <= max; num++) {
            sum += num;
        }
        return sum;
    }

    正確例子:
    int accumulate(int max)
    {
        int sum = 0;    /* 正確的賦予適當的初值 */
        int num;
        for (num = 1; num <= max; num++) {
            sum += num;
        }
        return sum;
    }

二、你不可以存取超過陣列既定範圍的空間。

    錯誤例子:
    int str[5];
    int i;
    for (i = 0; i <= 5; i++) str[i] = i;

    正確例子:
    int str[5];
    int i;
    for (i = 0; i < 5; i++) str[i] = i;

    說明:宣告陣列時,所給的陣列元素個數值如果是 N, 那麼我們在後面
    透過 [索引值] 存取其元素時,所能使用的索引值範圍是從 0 到 N-1,
    也就是 C 和 C++ 的陣列元素是從第 0 個開始算起,最後一個元素的
    索引值是 N-1, 不是 N。

    C/C++ 為了執行效率,並不會自動檢查陣列索引值是否超過陣列邊界,
    我們要自己寫程式來確保不會越界。一旦越界,將導致無法預期的後果。

三、你不可以提取(dereference)不知指向何方的指標(包含 null 指標)。

    錯誤例子:
    char *pc1;      /* 未給予初值,不知指向何方 */
    char *pc2 = 0;  /* pc2 起始化為 null pointer */
    *pc1 = 'a';     /* 將 'a' 寫到不知何方,錯誤 */
    *pc2 = 'b';     /* 將 'b' 寫到「位址0」,錯誤 */

    正確例子:
    char c;          /* c 的內容尚未起始化 */
    char *pc1 = &c;  /* pc1 指向字元變數 c */

    /* 動態分配 10 個 char(其值未定),並將第一個char的位址賦值給 pc2 */
    char *pc2 = (char *)malloc(10);
    *pc1 = 'a';      /* c 的內容變為 'a' */
    pc2[0] = 'b';    /* 動態配置來的第 0 個字元,內容變為 'b'
    /* 最後記得 free() 掉 malloc() 所分配的空間 */
    free(pc2);

    說明:指標變數必需先指向某個明確的東西(object),才能進行操作。

四、你不可以將字串常數賦值(assign)給 char* 變數,然後透過該變數
    改寫字串的內容(只能讀不能寫)。

    錯誤例子:
    char* pc = "john";
    *pc = 'J';
    printf("Hello, %s\n", pc);

    正確例子:
    char pc[] = "john";
    *pc = 'J';  /* 或  pc[0] = 'J';  */
    printf("Hello, %s\n", pc);

    說明:字串常數的內容是唯讀的。上面的錯誤例子,是將其內容所在的位址賦
    值給字元指標 pc, 我們透過指標只可以去讀該字串常數的內容,而不應該做
    寫入的動作。而正確例子,則是另外宣告一個獨立的字元陣列,它的大小我們
    未明文指定([]),編譯器會自動將其設為剛好可以容納後面的字串常數起始
    值的大小,包括字串後面隱含的 '' 字元,並將字串常數的內容複製到字元
    陣列中,因此可以自由的對該字元陣列的內容進行讀和寫。

    錯誤例子(2):
    char *s1 = "Hello, ";
    char *s2 = "world!";
    /* strcat() 不會另行配置空間,只會將資料附加到 s1 所指唯讀字串的後面,
       造成寫入到程式無權碰觸的記憶體空間 */
    char *s3 = strcat(s1, s2);

    正確例子(2):
    /* s1 宣告成陣列,並保留足夠空間存放後續要附加的內容 */
    char s1[20] = "Hello, ";
    char *s2 = "world!";
    /* 因為 strcat() 的返回值等於第一個參數值,所以 s3 就不需要了 */
    strcat(s1, s2);

五、你不可以對尚未分配所指空間的 char* 變數,進行(字串)陣列的相關操作。
    其他型別的指標亦然。

    錯誤例子:
    char *name;   /* name 尚未指向有效的空間 */
    printf("Your name, please: ");
    gets(name);
    printf("Hello, %s\n", name);

    正確例子(1):
    /* 如果編譯期就能決定字串的最大空間,那就不要宣告成 char* 改用 char[] */
    char name[21]; /* 字串最長 20 個字元,另加一個 '' */
    printf("Your name, please: ");
    gets(name);
    printf("Hello, %s\n", name);

    正確例子(2):
    /* 若是在執行時期才能決定字串的最大空間,則需利用 malloc() 函式來動態
       分配空間 */

    size_t length;
    char *name;
    printf("請輸入字串的最大長度(含null字元): ");
    scanf("%u", &length);
    name = (char *)malloc(length);
    printf("Your name, please: ");
    scanf("%s", name);
    printf("Hello, %s\n", name);
    /* 最後記得 free() 掉 malloc() 所分配的空間 */
    free(name);

    注意:上例用 gets() 或 scanf() 來讀入字串,是不安全的。 因為這些函式
    不會幫我們檢查使用者所輸入的字串長度是否超過我們所分配的 buffer 空間,
    很可能會發生 buffer overflow。比較安全的做法是用 fgets() 來取代。如:

    char *p;
    char name[21];
    printf("Your name, please: ");
    fgets(name, sizeof(name), stdin);
    /* fgets()會連行末的'\n'也讀進字串中,所以要找出存入'\n'的位置,填入 ''
    if ((p = strchr(name, '\n')) != NULL)
        *p = '';
    printf("Hello, %s\n", name);

六、你不可以在函式中回傳一個指向區域性自動變數的指標。否則,會得到垃圾值。
    [感謝 gocpp 網友提供程式例子]

    錯誤例子:
    char *getstr(char *name)
    {
        char buf[30] = "hello, "; /*將字串常數"hello, "的內容複製到buf陣列*/
        strcat(buf, name);
        return buf;
    }

    說明:區域性自動變數,將會在離開該區域時(本例中就是從getstr函式返回時)
    被消滅,因此呼叫端得到的指標所指的字串內容就失效了。【不過,倒是可以從
    函式中直接傳回字串常數,賦值給呼叫端的一個 const char * 變數,它既是唯
    讀的(參見第四誡),同時也具有恒常的儲存期(static storage duration),其
    內容將一直有效。】

    正確例子:
    void getstr(char buf[], int buflen, char const *name)
    {
        char const s[] = "hello, ";
        assert(strlen(s) + strlen(name) < buflen);

        strcpy(buf, s);
        strcat(buf, name);
    }

    [針對字串操作,C++提供了更方便安全的 string class, 能用就盡量用]
    #include <string>
    using std::string;

    string getstr(string const &name)
    {
        return string("hello, ") += name;
    }

七、你不可以只做 malloc(), 而不做相應的 free(). 否則會造成記憶體漏失。

    但若不是用 malloc() 所得到的記憶體,則不可以 free()。已經 free()了
    所指記憶體的指標,在它指向另一塊有效的動態分配得來的空間之前,不可
    以再被 free(),也不可以提取(dereference)這個指標。

    [C++] 你不可以只做 new, 而不做相應的 delete.

八、你不可以在數值運算、賦值或比較中隨意混用不同型別的數值,而不謹慎考
    慮數值型別轉換可能帶來的「意外驚喜」(錯愕)。必須隨時注意數值運算
    的結果,其範圍是否會超出變數的型別。

    錯誤例子(1):
    unsigned int sum = 2000000000 + 2000000000; /* 20 億 */
    double f = 10 / 3;

    正確例子(1):
    /* 全部都用 unsigned int, 注意數字後面的 u, 大寫 U 也成 */
    unsigned int sum = 2000000000u + 2000000000u;

    /* 或是用顯式的轉型 */
    unsigned int sum = (unsigned int)2000000000 + 2000000000;

    double f = 10.0 / 3.0;

    說明:在目前最普遍的32位元PC作業平台上,整數常數2000000000的型別為
    signed int(簡寫為 int),相加後,其結果仍為 int, 但是 signed int
    放不下 4000000000, 造成算術溢位(arithmetic overflow),很可能無法
    將正確的值指派給 unsigned int sum,縱使 unsigned int 放得下4000000000
    的數值。注意:寫成

    unsigned int sum = (unsigned int)(2000000000 + 2000000000);

    也是不對的。

    例子(2):(感謝 sekya 網友提供)
    unsigned char a = 0x80;
    char b = 0x80;           /* implementation-defined result */
    if( a == 0x80 ) {        /* 恒真 */
        printf( "a ok\n" );
    if( b == 0x80 ) {        /* 不一定恒真 */
        printf( "b ok\n" );
    }

    說明:在將 char 型別定義為範圍從 -128 至 +127 的系統上,int 0x80
    (其值等於 +128)要轉成 char 會放不下,會產生編譯器自行定義的值。
    這樣的程式就不具可移植性了。

九、你不可以在一個運算式(expression)中,對一個基本型態的變數修改其值
    超過一次以上。否則,將導致未定義的行為(undefined behavior)。

    錯誤例子:
    int i = 7;
    int j = ++i + i++;

    正確例子:
    int i = 7;
    int j = ++i;
    j += i++;

    你也不可以在一個運算式(expression)中,對一個基本型態的變數修改其值,
    而且還在同一個式子的其他地方為了其他目的而存取該變數的值。(其他目的,
    是指不是為了計算這個變數的新值的目的)。否則,將導致未定義的行為。

    錯誤例子:
    int arr[5];
    int i = 0;
    arr[i] = i++;

    正確例子:
    int arr[5];
    int i = 0;
    arr[i] = i;
    i++;

    [C++程式]
    錯誤例子:
    int i = 10;
    cout << i << "==" << i++;

    正確例子:
    int i = 10;
    cout << i << "==";
    cout << i++;

十、你不可以在macro的定義中,不為它的參數個別加上括號。

    錯誤例子:
    #include <stdio.h>
    #define SQUARE(x)    (x * x)
    int main()
    {
        printf("%d\n", SQUARE(10-5));
        return 0;
    }

    正確例子:
    #include <stdio.h>
    #define SQUARE(x)    ((x) * (x))
    int main()
    {
        printf("%d\n", SQUARE(10-5));
        return 0;
    }

    說明:如果是用 C++, 請多多利用 inline function 來取代上述的 macro,
    以免除 macro 定義的種種危險性。如:

    inline int square(int x) { return x * x; }

    macro 定義出的「偽函式」至少缺乏下列數項函式本有的能力:

    (1) 無法進行參數型別的檢查。
    (2) 無法遞迴呼叫。
    (3) 無法用 & 加在 macro name 之前,取得函式位址。
    (4) 呼叫時往往不能使用具有 side effect 的引數。例如:

    錯誤例子:(感謝 yaca 網友提供)
    #define MACRO(x)     (((x) * (x)) - ((x) * (x)))
    int main()
    {
        int x = 3;
        printf("%d\n", MACRO(++x));
        return 0;
    }

    MACRO(++x) 展開來後變成 (((++x) * (++x)) - ((++x) * (++x)))
    違反了第九誡。在 gcc 4.3.3 下的結果是 -24, 在 vc++ 下是 0.


後記:從「古時候」流傳下來一篇文章

      "The Ten Commandments for C Programmers"(Annotated Edition)
      by Henry Spencer
      http://www.lysator.liu.se/c/ten-commandments.html

      一方面它不是針對 C 的初學者,一方面它特意模仿中古英文
      聖經的用語,寫得文謅謅。所以我現在另外寫了這篇,希望
      能涵蓋最重要的觀念以及初學甚至老手最易犯的錯誤。

[ 電腦 ] 01 六月, 2008 14:38

這個管理支援度都不錯,蠻推的。網站位址如下:

http://rpm.livna.org/rlowiki/ 

[ 電腦 ] 29 五月, 2008 20:33
我是安裝RealPlayer11GOLD.rpm,安裝完畢後還要在安裝alsa-oss套件,接下來打開realplayer,請到Tools->Preferences->Hardware->將使用Use Xvideo給取消掉,將Audio driver選擇OSS。按ok離開後,重新啟動realplayer,但是記得要用aoss realplay這個指令來啟動,這樣就可以順利播放rmvb或rm檔案了!
[ 電腦 ] 29 五月, 2008 12:50

當安裝完flash播放檔之後,雖然可以看到flash了,但是播放出來卻沒有聲音,此時還要再多加裝一個package,那就是 libflashsupport (yum install libflashsupport),就可以讓flash播放出聲音。

[ 電腦 ] 24 五月, 2008 14:50

在安裝 xfig 於 fedora core 9 上時,安裝完畢之後,執行 xfig時候出現錯誤:

xfig 3.2.5 SIGSEGV abort

於是搜尋了一下google找到有類似的問題,那就是fedora core 9的XAW3D問題,因為版本的不一樣,導致xfig啟動完後之後,執行失敗,兩個可以解決的辦法:

1. 在Imakefile中將 #define XAW3D功能關閉!然後,xmkmf ; make即可。

2.或是在fedora core 9 下安裝XAW3D(但是fedora core 9本身就已經安裝最新的XAW3D1.5E以上版本),所以變成是降版本。

 

[ 電腦 ] 16 四月, 2008 11:50

The standard Divide-and-Conquer algorithm usually works as follows:

  1. spend some time handling the base case
  2. if not at the base case, divide the problem up into smaller subproblems: D(n)
  3. make recursive calls to handle all the subproblems created on step 2: T(n/b)
  4. combine results created by recursive calls on step 3: C(n)

利用遞迴分析: 

T(n) = a * T(n/b) + f(n)

  1. a = number of subproblems
  2. n/b = size of the subproblems
  3. f(n) = D(n) + C(n)

以Max-Heapify為例來分析:

  1. a = 1, i.e., there is only one recursive call made
  2. n/b = (2/3) * n, is the worst case size of the one subproblem. The size of the left subtree is (2/3) * n. 也就是說 when the left subtree has one entire full level as compared to the right subtree. The worst case is when the last level of the tree is half full.
  3. f(n) = Q(1), all the work done in lines 1 - 9 above takes a constant time
  4. T(n) = T(2n/3) + Q(1)

Max-Heapify演算法如下:

Max-Heapify(A,i)
1 l ← Left(i)
2 r ← Right(i)
3 if l ≤ heap-size[A] and A[l] > A[i]
4 then largest ← l
5 else largest ← i
6 if r ≤ heap-size[A] and A[r] > A[largest]
7 then largest ← r
8 if largest ≠ i
9 then exchange A[i] ↔ A[largest]
10 Max-Heapify(A, largest)

 

 

 

[ 電腦 ] 14 四月, 2008 12:50

CodeCogs - Latex Equation Editor

利用CodeCogs提供 Latex Equation Editor來線上顯示方程式:

在HTML中插入 

<img src="http://www.codecogs.com/eq.latex?p\in \cal{P} \rm{\ and\ } t\in T" />

會顯示如下:

[ 電腦 ] 24 三月, 2007 11:36

\makeatletter
\long\def\@makecaption#1#2{%
\vskip\abovecaptionskip
\sbox\@tempboxa{#1\quad #2}%
\ifdim \wd\@tempboxa >\hsize
  #1\quad #2\par
\else
  \global \@minipagefalse
  \hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
\fi
\vskip\belowcaptionskip}
\makeatother

 從大陸網站ctex中看到的

[ 電腦 ] 13 元月, 2007 15:23
最近在工作當中常會看到一個以隨身碟上的autorun.inf檔案為媒介到處傳播的病毒
在使用NOD32掃瞄後會看到這個病毒叫/win32/Small.R
受到感染的電腦掃瞄出來會看到下面的一些檔案:

中了隨身碟病毒的病徵:
1.隨身碟或C磁碟機用點選兩下會打不開。
解決方法:在我的電腦或檔案總管點選資料夾的圖來開啟。
2.在工作管理員中的處理程序中會看到影像名稱為svchost.exe、使用者名稱為user的項目。工作管理員按ctrl+alt+del
或滑鼠移到時鐘上方按右鍵也可以看見
3.在隨身碟中會存在autorun.inf、Recycled的隱藏檔及目錄,而RECYCLED中為存在著一個INFO.EXE的檔案,windows
預設會看不到這些隱藏檔和目錄,此時要修改一下設定。
作法:開啟我的電腦或檔案總管,選取工具--->資料夾選項
再點選檢視,找到下列三個選項:
a.隱藏已知檔案類型的副檔名
b.隱藏保護的作業系檔案
c.顯示所有檔案和資料夾
在隨身碟中看到autorun.inf及Recycled後把它刪除就可以了。

其實它是之前隨身碟病毒Inetsrv的變種板本
它會在使用者的電腦c:\windows\system\的目錄中製造一個目錄名叫_SV_CMD_
目錄當中會存在一個_U_.EXE的執行檔,基本_SV_CMD_資料夾和_U_.EXE檔也都是隱藏檔找到後一樣刪除即可。

如果想透過命令提示字元視窗清除_SV_CMD_目錄以及_U_.EXE檔案
在開始--->執行中輸入cmd
接著依照下面的圖示到\windows\system\下作業
因為它們是隱藏的所以用dir的指令會找不到要用attrib來找才行
c:
cd \%windir%\system\
attrib -r -h -s svchost.exe
del svchost.exe
attrib -r -h -s *.*
cd _SV_CMD_
attrib -r -h -s *.*
del _U_.EXE
cd ..
rd _SV_CMD_


要預防這種透過autorun.inf的隨身碟病毒最好的方式還是關閉電腦中的autorun功能
關閉的方法如下請到開始--->執行中輸入gpedit.msc進入windows的群組原則當中去修改
電腦設定--->系統管理範本--->系統--->右邊視窗中的關閉自動播放
在關閉自動播放--->已啟用--->停用自動播放在--->所有磁碟機--->套用--->確定

 資料來源:http://www.wretch.cc/blog/flikkao2&article_id=5792722