中國國民黨建議的逃生手冊

李志銘 也來談談KMT版的小橘書 民國六十八年(1979)在國民黨主政下,由國防部總政治作戰部編印的《揭穿中共統戰陰謀答問》 這本小冊子裡雖然只有短短7頁8題,但卻字字珠璣,尤其放到今天來看,更是完全凸顯了中國國民黨從反共到舔共的事實。 . 譬如其中第三題問::國共會有兩度合作、一次和談的歷史,爲什麼不能再談? 答:我們就因爲過去每次都上了中共的當,最後失去了大陸,所以現在不能再和他們談了。 第一次是民國十三年的「容共」。中共聲稱服膺三民主義,參加國民革命,但他們却發展自己的組織,分化革命陣營,使北伐革命幾乎失敗。 第二次在民國廿六年的所謂「聯合抗日」。中共趁日本向我國侵略,聲稱接受政府領導,擁護三民主義,取消叛亂政權和紅軍名義,共同抵抗日本。實際上他們却擴張自己勢力,並勾結日本偷襲國軍,打下了他們擴大叛亂的基礎。 最後一次是戰後的「國共和談」。經過美國的調處,中共以邊談邊打的策略,以「談判」保護自己,以軍事行動擴張地盤,並在此談談打打的過程中,挑撥中美關係,動搖我民心土氣,最後終於竊據大陸。我們有了這些經驗,知道和談就是解除自己武裝向他們投降,所以再不能和中共談判了。 . 又譬如其中第七題問:中共現在已不講「解放臺灣」「血洗臺灣」,而講「回歸祖國、完成統一」是不是他們已放棄使用武力? 答:共產黨最善於在名詞上玩魔術,我們千萬不能上當,一定要找出其所用名詞的真正涵義。基本上,「解放臺灣」已訂入了中共的「憲法」,目標已定,正如中共自己所說的,十年、二十年,甚至一百年、一千年,總要達到目標。所以併吞臺灣這個目標是不會改變的。 . . 《揭穿中共統戰陰謀答問》全書PDF電子檔下載 https://reurl.cc/6b5zvM

C 語言新手十誡

轉錄自ptt

作者: khoguan (Khoguan Phuann) 看板: C_and_CPP
標題: C 語言新手十誡(增修一版)
時間: Sat Sep 3 01:07:31 2005

C 語言新手十誡(The Ten Commandments for Newbie C Programmers)

by Khoguan Phuann

請注意:

(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, 我們透過指標只可以去讀該字串常數的內容,而不應該做 寫入的動作。而正確例子,則是另外宣告一個獨立的字元陣列,它的大小我們 未明文指定([]),編譯器會自動將其設為剛好可以容納後面的字串常數起始 值的大小,包括字串後面隱含的 '\0' 字元,並將字串常數的內容複製到字元 陣列中,因此可以自由的對該字元陣列的內容進行讀和寫。 錯誤例子(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 個字元,另加一個 '\0' */ 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'的位置,填入 '\0' if ((p = strchr(name, '\n')) != NULL) *p = '\0'; 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
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
#define SQUARE(x) (x * x)
int main()
{
printf("%d\n", SQUARE(10-5));
return 0;
}

正確例子:
#include
#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 的初學者,一方面它特意模仿中古英文
聖經的用語,寫得文謅謅。所以我現在另外寫了這篇,希望
能涵蓋最重要的觀念以及初學甚至老手最易犯的錯誤。

作者:潘科元(Khoguan Phuann) (c)2005.
感謝 ptt.cc BBS 的 C_and_CPP
看板眾多網友提供寶貴意見及程式實例。

Comments

Post a Comment

本月熱門文章

什麼是 OTA ?

如何關閉 QNAP Multimedia Console 產生縮圖功能

QNAP NAS 安裝openHAB 來實現智慧家庭

Amazon/ AWS 領導力準則 14 Amazon Leadership Principles

QNAP NVR Solution Safeguards Montebelo Aguieira Lake Resort & Spa in Portugal

推薦韓劇 機智的醫生生活 充滿友情的五人組 Mido and Falasol

India's Largest Petroleum Company Upgrades Its Surveillance System with QNAP Security's Solution

Japanese Mall Nakasu Syotengai Selects QNAP VioStor NVR for Remote Monitoring

QNAP Network Surveillance System Successfully Launched in Kaohsiung Veterans General Hospital

QNAP NAS 作為 IIoT Gateway QIoT Suite 支援 OPC UA,工業物聯網最佳 Gateway