葡萄牙文口譯價位C/C++ 語言新手十三誡(The Thirteen Commandments for Newbie C/C++ Programmers) by Khoguan Phuann 請注意: (1) 本篇旨在提示新手,避免初學常犯 翻譯毛病(其實熟手在行也常犯:-Q) 翻譯社 但不能庖代完全的進修,請本身好好研讀一兩本 C 說話的好書, 並多多實作演習 翻譯社 (2) 強烈建議新手先看過此文再提問,你的問題極可能此文已經提出並 解答了。 (3) 以下所舉 翻譯毛病例子如果在你 翻譯電腦上印出和准確例子溝通 翻譯成績, 那只是不足為恃的一時僥倖 翻譯社 (4) 不守十三誡者,輕則履行結果的輸出數據毛病,或是程式當掉,重則 引爆核彈、撲滅地球(若是你的 C 程式是用來控制核彈發射器 翻譯話)。 ============================================================= 目錄: (頁碼/行號) 2/24 01. 不行以使用還沒有給予恰當初值的變數 3/46 02. 不克不及存取跨越陣列既定範圍的空間 5/90 03. 不成以提取不知指向何方 翻譯指標 7/134 04. 不要試圖用 char* 去更改一個"字串常數" 12/244 05. 不克不及在函式中回傳一個指向區域性主動變數的指標 16/332 06. 不行以只做 malloc() 翻譯公司 而不做響應的 free() 19/398 07. 在數值運算、賦值或對照中不成以隨意混用分歧型別的數值 21/442 08. ++i/i++/--i/i--/f(&i)哪一個先履行跟挨次有關 24/508 09. 慎用Macro 27/574 10. 不要在 stack 設置過大的變數以免堆疊溢位(stack overflow) 32/684 11. 利用浮點數精確度造成 翻譯誤差問題 35/750 12. 不要猜想二維陣列可以用 pointer to pointer 來傳遞 36/772 13. 函式內 new 出來 翻譯空間記得要讓主程式的指標接住 40/860 直接輸入數字可跳至該頁碼 或用:指令輸入行號 01. 你不成以使用還沒有賜與恰當初值的變數 毛病例子: int accumulate(int max) /* 從 1 累加到 max,傳回結果 */ { int sum; /* 未賜與初值的區域變數,其內容值是垃圾 */ for (int num = 1; num <= max; num++) { sum += num; } return sum; } 准確例子: int accumulate(int max) { int sum = 0; /* 正確的賦予適當 翻譯初值 */ for (int num = 1; num <= max; num++) { sum += num; } return sum; } 備註:  按照 C Standard,具有靜態貯存期(static storage duration)的變數, 例如 全域變數(global variable)或帶有 static 修飾符者等,  如果沒有顯式初始化的話,按照分歧的資料型態予以進行以下初始化:  若變數為算術型別 (int , double , ...) 時,初始化為零或正零。  若變數為指標型別 (int* 翻譯公司 double*, ...) 時,初始化為 null 指標 翻譯社  若變數為複合型別 (struct, double _Complex 翻譯公司 ...) 時,遞迴初始化所有成員。  若變數為聯合型別 (union) 時,只有其中的第一個成員會被遞迴初始化 翻譯社 (以上感激Hazukashiine板友斧正) (可是有些MCU 編譯器可能不睬會這個劃定,所以照樣請養成設定初值 翻譯好習慣) 增補資料: - 精髓區z->5->1->1->1 - C11 Standard 5.1.2, 6.2.4, 6.7.9 02. 你不可以存取跨越陣列既定規模的空間 錯誤例子: int str[5]; for (int i = 0 ; i <= 5 ; i++) str[i] = i; 准確例子: int str[5]; for (int i = 0; i < 5; i++) str[i] = i; 申明:宣佈陣列時,所給的陣列元素個數值若是是 N, 那麼我們在後面 透過 [索引值] 存取其元素時,所能使用 翻譯索引值局限是從 0 到 N-1 C/C++ 為了履行效率,其實不會主動檢查陣列索引值是不是跨越陣列鴻溝, 我們要本身來確保不會越界。一旦越界,操作 翻譯不再是正當 翻譯空間, 將導致沒法預期的後果 翻譯社 備註: C++11以後可以用Range-based for loop提取array、 vector(或是其他有供應准確.begin()和.end()的class)。-> 翻譯社|,-> 翻譯公司|的-> 翻譯翻譯元素 可以確保提取的元素必然落在准確範圍內。 例: // vector std::vector<int> v = {0, 1 翻譯公司 2 翻譯公司 3, 4, 5}; for(const int &i : v) // access by const reference std::cout << i << ' '; std::cout << ' '; // array int a[] = {0, 1, 2, 3, 4, 5}; for(int n: a) // the initializer may be an array std::cout << n << ' '; std::cout << ' '; 增補資料: http://en.cppreference.com/w/cpp/language/range-for 03. 你不成以提取(dereference)不知指向何方的指標(包括 null 指標)。 毛病例子: char *pc1; /* 未給予初值,不知指向何方 */ char *pc2 = NULL; /* pc2 肇端化為 null pointer */ *pc1 = 'a'; /* 將 'a' 寫到不知何方,錯誤 */ *pc2 = 'b'; /* 將 'b' 寫到「位址0」,毛病 */ 准確例子: char c; /* c 的內容還沒有起始化 */ char *pc1 = &c; /* pc1 指向字元變數 c */ *pc1 = 'a'; /* c 的內容變為 'a' */ /* 動態分派 10 個 char(其值不決) 翻譯公司並將第一個char 翻譯位址賦值給 pc2 */ char *pc2 = (char *) malloc(10); pc2[0] = 'b'; /* 動態配置來 翻譯第 0 個字元,內容變為 'b' free(pc2); 申明:指標變數必需先指向某個可以合法操作的空間,才能進行操作 翻譯社 ( 利用者記得要查抄 malloc 回傳是不是為 NULL, 礙於篇幅本文假定利用上皆合法,也有准確清償記憶體 ) 毛病例子: char *name; /* name 還沒有指向有用的空間 */ printf("Your name 翻譯公司 please: "); fgets(name,20,stdin); /* 您肯定要寫入的那塊空間正當嗎??? */ printf("Hello 翻譯公司 %s ", name); 准確例子: /* 如果編譯期就能決意字串的最大空間,那就不要宣佈成 char* 改用 char[] */ char name[21]; /* 可讀入字串最長 20 個字元,保存一格空間放 '\0' */ printf("Your name, please: "); fgets(name,20 翻譯公司stdin); printf("Hello, %s " 翻譯公司 name); 准確例子(2): 若是在執行期間才能決議字串的最大空間,C供應兩種作法: a. 利用 malloc() 函式來動態分派空間,用malloc宣告的陣列會被存在heap 須注重:若是宣佈較大陣列,要確認malloc的回傳值是不是為NULL size_t length; printf("請輸入字串 翻譯最大長度(含null字元): "); scanf("%u", &length); name = (char *)malloc(length); if (name) { // name != NULL printf("您輸入 翻譯是 %u ", length); } else { // name == NULL puts("輸入值太大或系統已無足夠空間"); } /* 最跋文得 free() 掉 malloc() 所分配 翻譯空間 */ free(name); name = NULL; //(註1) b. C99最先可以使用variable-length array (VLA) 須留意: - 因為VLA是被寄存在stack裡,利用前要確認array size不克不及太大 - 不是每一個compiler都支援VLA(註2) - C++ Standard不支援(固然有些compiler支援) float read_and_process(int n) { float vals[n]; for (int i = 0; i < n; i++) vals[i] = read_val(); return process(vals, n); } 准確例子(3): C++的利用者也有兩種作法: a. std::vector (不管你的陣列巨細會不會變都可用) std::vector<int> v1; v1.resize(10); // 從新設定vector size b. C++11今後,若是肯定陣列巨細不會變,可以用std::array 須注意:一般利用下(存在stack)一樣要確認array size不能太大 std::array<int, 5> a = { 1, 2 翻譯公司 3 }; // a[0]~a[2] = 1 翻譯公司2 翻譯公司3; a[3]以後為0; a[a.size() - 1] = 5; // a[4] = 0; 備註: 註1. C++的使用者,C++03或之前請用0取代NULL,C++11最先請改用nullptr 註2. gcc和clang支援VLA,Visual C++不支援 增補資料: http://www.cplusplus.com/reference/vector/vector/resize/ 04. 你不成以試圖用 char* 去更改一個"字串常數" 試圖去更改字串常數(string literal) 翻譯成績會是undefined behavior。 毛病例子: char* pc = "john"; /* pc 目下當今指著一個字串常數 */ *pc = 'J'; /* undefined behaviour,效果沒法展望*/ pc = "jane"; /* 合法,pc指到在其余位址的另外一個字串常數*/ /* 但是"john"這個字串照樣存在本來的處所不會消逝*/ 因為char* pc = "john"這個動作會新增一個內含元素為"john\0"的static char[5], 然後pc會指向這個static char 翻譯位址(平常是唯讀) 翻譯社 若是試圖存取這個static char[],Standard並沒有定義成效為何。 pc = "jane" 這個動作會把 pc 指到另外一個沒在用的位址然後新增一個 內含元素為"jane\0" 翻譯static char[5]。 可是之前那個字串 "john " 仍是留在原地沒有消逝。 通常編譯器的作法是把字串常數放在一塊read only(.rdata)的區域內, 此區域大小是有限的,所以如果你反複把pc指給分歧的字串常數, 是有可能會出問題的。 正確例子: char pc[] = "john"; /* pc 現在是個正當的陣列,裡面住著字串 john */ /* 也就是 pc[0]='j', pc[1]='o', pc[2]='h', pc[3]='n', pc[4]='\0' */ *pc = 'J'; pc[2] = 'H'; 申明:字串常數 翻譯內容應該如果"唯讀"的。您有利用權,但是沒有更改的權利。 若您希望使用可以更改的字串,那您應當將其放在正當空間 錯誤例子: char *s1 = "Hello 翻譯公司 "; char *s2 = "world!"; /* strcat() 不會另行配置空間,只會將資料附加到 s1 所指唯讀字串 翻譯後面, 造成寫入到程式無權碰觸的記憶體空間 */ strcat(s1 翻譯公司 s2); 准確例子(2): /* s1 宣佈成陣列,並保留足夠空間寄存後續要附加的內容 */ char s1[20] = "Hello 翻譯公司 "; char *s2 = "world!"; /* 因為 strcat() 的返回值等於第一個參數值,所以 s3 就不需要了 */ strcat(s1, s2); C++對於字串常數的嚴厲界說為const char* 或 const char[]。 但是由於要相容C,char* 也是許可 翻譯寫法(不建議就是)。 不過,在C++試圖更改字串常數(要先const_cast)一樣是undefined behavior。 const char* pc = "Hello"; char* p = const_cast<char*>(pc); p[0] = 'M'; // undefined behaviour 備註: 由於不加const輕易造成攪渾, 建議不論是C照樣C++一概用 const char* 定義字串常數。 補充資料: http://en.cppreference.com/w/c/language/string_literal http://en.cppreference.com/w/cpp/language/string_literal 字串函數相幹:#1IOXeMHX undefined behavior : 精華區 z -> 3 -> 3 -> 23 05. 你不成以在函式中回傳一個指向區域性主動變數 翻譯指標 翻譯社不然,會獲得垃圾值 [感激 gocpp 網友供應程式例子] 毛病例子: char *getstr(char *name) { char buf[30] = "hello, "; /*將字串常數"hello 翻譯公司 " 翻譯內容複製到buf陣列*/ strcat(buf, name); return buf; } 說明:區域性自動變數,將會在分開該區域時(本例中就是從getstr函式返回時) 被消滅,是以呼喚端獲得的指標所指的字串內容就失效了。 准確例子: void getstr(char buf[], int buflen 翻譯公司 char const *name) { char const s[] = "hello, "; strcpy(buf, s); strcat(buf 翻譯公司 name); } 准確例子: int* foo() { int* pInteger = (int*) malloc( 10*sizeof(int) ); return pInteger; } int main() { int* pFromfoo = foo(); } 申明:上例固然回傳了函式中的指標,但由於指標內容所指的位址並非區域變數, 而是用動態的方式抓取而得,換句話說這塊空間是長在 heap 而非 stack, 又因 heap 空間並不會主動回收,因此這塊空間在離開函式後,仍然有用 (但是這個例子可能會因為 programmer 翻譯忽視,忘記 free 而造成 memory leak) [針對字串操作,C++提供了更利便安全更直觀的 string class, 能用就盡量用] 正確例子: #include <string> /* 並不是 #include <cstring> */ using std::string; string getstr(string const &name) { return string("hello 翻譯公司 ") += name; } 06. [C]你不成以只做 malloc(), 而不做響應的 free(). 不然會造成記憶體漏失 但若不是用 malloc() 所得到的記憶體,則不可以 free()。已 free()了 所指記憶體 翻譯指標,在它指向另外一塊有效的動態分派得來的空間之前,不行 以再被 free(),也不可以提取(dereference)這個指標。 小技巧: 可在 free 以後將指標指到 NULL,free不會對空指標感化 翻譯社 例: int *p = malloc(sizeof(int)); free(p); p = NULL; free(p); // free不會對空指標有感化 [C++] 你不成以只做 new, 而不做響應的 delete (除unique_ptr之外) 註:new 與 delete 對應,new[] 與 delete[] 對應, 不成與malloc/free混用(成果不可猜測) 切記,做了幾回 new,就必需做幾回 delete 小技巧: 可在 delete 之後將指標指到0或nullptr(C++11開始), 由於 delete 自己會先做查抄,是以可以避免掉多次 delete 的錯誤 正確例子: int *ptr = new int(99); delete ptr; ptr = nullptr; delete ptr; /* delete 只會處置指向非 NULL 的指標 */ 備註: C++11後新增智能指標(smart pointer): unique_ptr 當unique_ptr所指物件消逝時,會主動釋放其記憶體,不需要delete。 例: #include <memory> // 含unique_ptr的標頭檔 std::unique_ptr<int> p1(new int(5)); 增補資料: http://en.cppreference.com/w/cpp/memory/unique_ptr 07. 你不成以在數值運算、賦值或比力中隨便混用分歧型別 翻譯數值,而不謹慎考 慮數值型別轉換可能帶來 翻譯「不測欣喜」(錯愕)。必需隨時留意數值運算 的成績,其局限是不是會超出變數的型別 毛病例子: unsigned int sum = 2000000000 + 2000000000; /* 超出 int 寄存局限 */ unsigned int sum = (unsigned int) (2000000000 + 2000000000); double f = 10 / 3; 准確例子: /* 悉數都用 unsigned int, 留意數字後面的 u, 大寫 U 同樣成 */ unsigned int sum = 2000000000u + 2000000000u; /* 或是用顯式的轉型 */ unsigned int sum = (unsigned int) 2000000000 + 2000000000; double f = 10.0 / 3.0; 毛病例子: unsigned int a = 0; int b[10]; for(int i = 9 ; i >= a ; i--) { b[i] = 0; } 申明:由於 int 與 unsigned 共同運算的時辰,會轉換 int 為 unsigned, 因此迴圈條件永久滿足,與預期行為不符 毛病例子: (感激 sekya 網友供應) unsigned char a = 0x80; /* no problem */ char b = 0x80; /* implementation-defined result */ if( b == 0x80 ) { /* 紛歧定恒真 */ printf( "b ok " ); } 說明:說話並未劃定 char 生成為 unsigned 或 signed,是以將 0x80 放入 char 型態 翻譯變數,將會視各家編譯器分歧作法而有不同後果 錯誤例子(以下假設為在32bit機械上執行): #include <math.h> long a = -2147483648 ; // 2147483648 = 2 的 31 次方 while (labs(a)>0){ // labs(-2147483648)<0 有可能發生 ++a; } 申明:如果你去看C99/C11 Standard,你會發現long 變數的最大/最小值為(被define在limits.h) LONG_MIN -2147483647 // compiler實作時最小值不可大於 -(2147483648-1) LONG_MAX 2147483647 // compiler實作時最小值不行小於 (2147483648-1) 不過由於32bit能顯示 翻譯範圍就是2**32種,所以一般16/32bit功課系統會把 LONG_MIN多減去1,也就是int 的顯示規模為(-LONG_MAX - 1) ~ LONG_MAX。 (64bit的作業系統long多為8 bytes,可是照舊契合Standard要求的最小局限) 當程式跑到labs(-2147483648)>0時,由於2147483648大於LONG_MAX, Standard告訴我們,當labs的結果沒法被long有限的範圍表示, 編譯器會怎麼幹就看他高興(undefined behavior)。 (不只long,其他如int、long long等以此類推) 彌補資料: - C11 Standard 5.2.4.2.1, 7.22.6.1 - https://www.fefe.de/intof.html 08. ++i/i++/--i/i--/f(&i)哪個先履行跟挨次有關 ++i/i++ 和--i/i-- 翻譯問題幾近每個月城市出現,所以迥殊強調。 當一段程式碼中,某個變數的值用某種體例被改變一次以上, 例如 ++x/--x/x++/x--/function(&x)(能改變x 翻譯函式) - 如果Standard沒有特別去界說某段敘述中哪一個部分必需被先履行, 那結果會是undefined behavior(效果未知)。 - 假如Standard有迥殊去界說執行遞次,那了局就憑據執行遞次決議。 C/C++均准確的例子: if (--a || ++a) {} // ||左側先較量爭論,若是左邊為1右邊就不會算 if (++i && f(&i)) {} // &&左側先計算,若是左側為0右邊就不會算 a = (*p++) ? (*p++) : 0 ; // 問號左側先計較 int j = (++i 翻譯公司 i++); // 這裡 翻譯逗號為運算子,表示依序較量爭論 C/C++均毛病的例子: int j = ++i + i++; // undefined behavior,Standard沒界說+號哪邊先執行 x = x++; // undefined behavior 翻譯公司 Standard沒界說=號哪邊先履行 printf( "%d %d %d", I++ 翻譯公司 f(&I), I++ ); // undefined behavior, 原因同上 foo(i++, i++); // undefined behavior,這裡的逗號是用來分隔引入參數的 // 分隔符(separator)而非運算子,Standard沒界說哪邊先執行 在C與C++03毛病然則在C++11入手下手(但不包括C)准確的例子: C++11中,++i/--i為左值(lvalue),i++/i--為右值(rvalue)。 左值可以被assign value給它,右值則不可 翻譯社 而在C中,++i/--i/i++/i--都是右值。 所以以下的code在C++會正確,C則否 翻譯社 ++++++++++phew ; // C++11會把它注釋為++(++(++(++(++phew)))); i = v[++i]; // ++i會先完成 i = ++i + 1; // ++i會先完成 在C++17開始(但不包孕C)才准確的例子: cout << i << i++; // 先左後右 a[i] = i++; // i++先做 a[x++] = --x; // 先處理--x,再處置懲罰a[x++] (loveflames增補) 增補資料 - Undefined behavior and sequence points http://stackoverflow.com/questions/4176328/undefined-behavior-and- sequence-points) - C11 Standard 6.5.13-17,Annex C - Sequence poit https://en.wikipedia.org/wiki/Sequence_point - Order of evaluation http://en.cppreference.com/w/cpp/language/eval_order 09. 慎用macro(#define) Macro是個像鐵鎚一樣好用又危險的東西: 用得好可以釘釘子,用欠好可以把釘子打彎、敲到你手指或被抓去吃槍彈。 因為macro 界說出 翻譯「偽函式」有以下缺點: (1) debug會變得複雜 翻譯社 (2) 沒法遞迴呼喚。 (3) 無法用 & 加在 macro name 之前,獲得函式位址。 (4) 沒有namespace。 (5) 可能會導致新鮮的side effect或其他沒法預測的問題。 所以,利用macro前,請先確認以上 翻譯缺點是不是會影響你的程式運行。 替換方案:enum(界說整數),const T(定義常數),inline function(界說函式) C++ 翻譯template(界說可用分歧type參數的函式), 或C++11入手下手的匿名函式(Lambda function)與constexpr T(編譯期常數) 以下就針對macro的瑕玷做說明: (1) debug會變得複雜 翻譯社 編譯器不克不及對macro自己做語法檢查,只能搜檢預處置懲罰(preprocess)後的後果。 (2) 沒法遞迴呼喚 翻譯社 憑據C standard 6.10.3.4, 假如某macro的界說裡裏面含有跟此macro名稱一樣 翻譯的字串, 該字串將不會被預處理。 所以: #define pr(n) ((n==1)? 1 : pr(n-1)) cout<< pr(5) <<endl; 預處置過後會釀成: cout<< ((5==1)? 1 : pr(5 -1)) <<endl; // pr沒有定義,編譯會犯錯 (3) 沒法用 & 加在 macro name 之前,取得函式位址 翻譯社 因為他不是函式,所以你也弗成以把函式指標套用在macro上。 (4) 沒有namespace 翻譯社 毛病例子: #define begin() x = 0 for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) // begin是std的保留字 std::cout << ' ' << *it; 改良方式:macro名稱一概用大寫,如BEGIN() (5) 可能會致使希奇 翻譯side effect或其他無法猜測的問題。 毛病例子: #include <stdio.h> #define SQUARE(x) (x * x) int main() { printf("%d ", SQUARE(10-5)); // 預處置懲罰後釀成SQUARE(10-5*10-5) return 0; } 准確例子:在 Macro 界說中, 務必為它的參數個體加上括號 #include <stdio.h> #define SQUARE(x) ((x) * (x)) int main() { printf("%d " 翻譯公司 SQUARE(10-5)); return 0; } 不外遇到以下有side effect的例子就算加了括號也沒用。 毛病例子: (感激 yaca 網友供應) #define MACRO(x) (((x) * (x)) - ((x) * (x))) int main() { int x = 3; printf("%d ", MACRO(++x)); // 有side effect return 0; } 彌補資料: - http://stackoverflow.com/questions/14041453/why-are-preprocessor- macros-evil-and-what-are-the-alternatives - http://stackoverflow.com/questions/12447557/can-we-have-recursive-macros - C11 Standard 6.10.3.4 - http://en.cppreference.com/w/cpp/language/lambda 10. 不要在 stack 設置過大 翻譯變數以避免堆疊溢位(stack overflow) 由於編譯器會自行決意 stack 翻譯上限,某些預設是數 KB 或數十KB,當變數所需 翻譯空 間過大時,很輕易造成 stack overflow,程式亦隨之當掉(segmentation fault) 翻譯社 可能造成堆疊溢位的原因包孕遞迴太屢次(多為程式設計缺點), 或是在 stack 設置過大的變數。 毛病例子: int array[10000000]; // 在stack宣佈過大陣列 std::array<int 翻譯公司 10000000> myarray; //在stack宣佈過大std::array 准確例子: C: int *array = (int*) malloc( 10000000*sizeof(int) ); C++: std::vector<int> v; v.resize(10000000); 申明:建議將利用空間較大的變數用malloc/new配置在 heap 上,由於此時 stack 上只需設置裝備擺設一個 int* 翻譯空間指到在heap 翻譯該變數,可避免 stack overflow 翻譯社 利用 heap 時,雖然全部 process 可用的空間是有限的,但採用動態抓取 的體例,new 無法設置裝備擺設時會丟出 std::bad_alloc 破例,malloc 沒法設置裝備擺設 時會回傳 null(註2),不會影響到正常使用下 翻譯程式功能 備註: 註1. 利用 heap 時,全部 process 可用的空間一樣是有限的,若是需要頻仍地 malloc / free 或 new / delete 較大的空間,需留意避免造成記憶體破裂 (memory fragmentation)。 註2. 由於Linux利用overcommit機制經管記憶體,malloc即使在記憶體不足時 依然會回傳非NULL的address,一樣景象在Windows/Mac OS則會回傳NULL (感激 LiloHuang 彌補) 補充資料: - https://zh.wikipedia.org/wiki/%E5%A0%86%E7%96%8A%E6%BA%A2%E4%BD%8D - http://stackoverflow.com/questions/3770457/what-is-memory-fragmentation - http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/ overcommit跟malloc: - http://goo.gl/V9krbB - http://goo.gl/5tCLQc 11. 使用浮點數萬萬要注意切確度所造成的誤差問題 根據 IEEE 754 翻譯規範,又電腦中是用有限的二進位貯存數字,是以常有可 能因為切確度而造成誤差,例如加減乘除,等號大小判定,分配律等數學上 常用到的操作,很有可能是以而出錯(不成立) 更詳細的申明可以參考精華區 z-8-11 或參考冼鏡光老師所頒發 翻譯一文 "利用浮點數最最根基的觀念" http://blog.dcview.com/article.php?a=VmhQNVY%2BCzo%3D 12. 不要猜想二維陣列可以用 pointer to pointer 來傳遞 (感激 loveme00835 legnaleurc 版友 翻譯幫忙) 首先必需有個觀念,C 語言中陣列是無法直接拿來傳遞的! 不外這時候會有人跳出來辯駁: void pass1DArray( int array[] ); int a[10]; pass1DArray( a ); /* 可以正當編譯,並且履行結果准確!! */ 事實上,編譯器會這麼對待 void pass1DArray( int *array ); int a[10]; pass1DArray( &a[0] ); 我們可以趁便看出來,array 變數自己可以 decay 成記憶體開端的位置 是以我們可以 int *p = a; 這種體例,拿指標去接陣列。 也因為上述 翻譯例子,許多人以為那二維陣列是否是也能夠改成 int ** 毛病例子: void pass2DArray( int **array ); int a[5][10]; pass2DArray( a ); /* 這時候編譯器就會報錯啦 */ /* expected ‘int **’ but argument is of type ‘int (*)[10]’*/ 在一維陣列中,指標的移動操作,會恰好覆蓋到陣列的規模 例如,宣告了一個 a[10],那我可以把 a 當做指標來操作 *a 至 *(a+9) 是以我們可以得到一個概念,在操作的時辰,可以 decay 成指標來利用 也就是我可以把一個陣列當做一個指標來利用 (again, 陣列!=指標) 可是多維陣列中,沒法如斯利用,事實上這也很直觀,試圖拿一個 pointer to pointer to int 來操作一個 int 二維陣列,這是不公道 翻譯! 儘管我們沒法將二維陣列直接 decay 成兩個指標,可是我們可以換個角度想, 二維陣列可以看成 "外層大的一維陣列,每維內層各又包括著一維陣列" 若是想通了這一點,我們可以仿制之前 翻譯法則, 把外層大的一維陣列 decay 成指標,該指標指向內層 翻譯一維陣列 void pass2DArray( int (*array) [10] ); // array 是個指標,指向 int [10] int a[5][10]; pass2DArray( a ); 這時候候就很好理解了,函數 pass2DArray 內的 array[0] 會代表什麼呢? 謎底是它代表著 a[0] 外層的那一維陣列,裡面包括著內層 [0]~[9] 也是以 array[0][2] 就會對應到 a[0][2],array[4][9] 對應到 a[4][9] 結論就是,只有最外層的那一維陣列可以 decay 成指標,其他維陣列都要 明白的指出陣列巨細,如許多維陣列的傳遞就不會有問題了 也因為方才的例子,我們可以清晰的知道在傳遞陣列時,現實行為是在傳遞 指標,也是以假如我們想用 sizeof 來求得陣列元素個數,那是不可行的 毛病例子: void print1DArraySize( int* arr ) { printf("%u", sizeof(arr)/sizeof(arr[0])); /* sizeof(arr) 只是 */ } /* 一個指標 翻譯巨細 */ 受此限制,我們必需手動傳入巨細 void print1DArraySize( int* arr, size_t arrSize ); C++ 提供 reference 的機制,使得我們不需再這麼麻煩, 可以直接傳遞陣列的 reference 給函數,巨細也能夠直接求出 准確例子: void print1DArraySize( int (&array)[10] ) { // 傳遞 reference cout << sizeof(array) / sizeof(int); // 准確取得陣列元素個數 } 13. 函式內 new 出來 翻譯空間記得要讓主程式的指標接住 對指標不熟悉的利用者會以為以下 翻譯程式碼是相符預期 翻譯 void newArray(int* local, int size) { local = (int*) malloc( size * sizeof(int) ); } int main() { int* ptr; newArray(ptr, 10); } 接著就會找了很久 翻譯 bug,最後依然搞不懂為什麼 ptr 沒有指向方才拿到 翻譯正當空間 讓我們再回首一次,而且用圖默示 (感激Hazukashiine板友供給圖解) ┌────┐ ┌────┐ ┌────┐ ┌────┐ Heap │ │ │ │ │ 新設置裝備擺設 │ │ 已泄露 │ │ │ │ │ │ 的空間 <─┐ │ 的空間 │ │ │ │ │ │(allocd)│ │ │(leaked)│ │ │ │ │ ├────┤ │ ├────┤ │ │ │ │ │ : │ │ │ │ │ │ │ │ │ : │ │ │ : │ │ │ ├────┤ ├────┤ │ │ : │ │ │ │ local ├─┐ │ local ├─┘ │ │ ├────┤ ├────┤ │ ├────┤ ├────┤ Stack │ ptr ├─┐ │ ptr ├─┤ │ ptr ├─┐ │ ptr ├─┐ └────┘ ╧ └────┘ ╧ └────┘ ╧ └────┘ ╧   未初始化 函式呼喚 配置空間 函式返回 int *ptr; local = ptr; local = malloc(); 用圖看應當一切就都明白了,我也不需冗言注釋 或許有人會想問,指標不是傳址嗎? 正確來說,指標也是傳值,只不過該值是一個位址 (ex: 0xfefefefe) local 接到了 ptr 指向的誰人位置,接著函式內 local 要到了新的位置 然則 ptr 指向的位置照舊沒變 翻譯,因此離開函式後就彷佛事什麼都沒發生 ( 嚴厲說起來還産生了 memory leak ) 以下是一種解決法子 int* createNewArray(int size) { return (int*) malloc( size * sizeof(int) ); } int main() { int* ptr; ptr = createNewArray(10); } 改成如許亦可 ( 為何用 int** 就可以?想想他會傳什麼曩昔給local ) void createNewArray(int** local, int size) { *local = (int*) malloc( size * sizeof(int) ); } int main() { int *ptr; createNewArray(&ptr, 10); } 如果是 C++,別忘了可以善用 Reference void newArray(int*& local, int size) { local = new int[size]; } 跋文:從「古時刻」撒播下來一篇文章 "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 看板眾多網友供應貴重意見及程式實例。 nowar100 屢次加以點竄整理,擴充至 13 項,而且製作成動畫版。 wtchen 應板友要求移除動畫並憑據C/C++標準點竄內容(Ver.2016) 如發現 Bug 請推文回報,謝謝您

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

出生證明翻譯

娛樂中心/綜合報導

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

醫療翻譯推薦

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

論文翻譯社服務

ef學術資深研究員minh tran(中) 、ef台灣區總司理謝慧英(daphn...
ef學術資深研究員minh tran(中) 、ef台灣區總經理謝慧英(daphne hsieh)密斯(右二)、紫牛創業協會理事長蔣志薇密斯(右一)、美商萬寶華團體台灣總司理吳璧昇師長教師(左二) 與ef epi 主持人劉傑中師長教師(左一) ,預會接頭2017 ef epi 全球英語力指標呈文。郭耿瑞/攝影
文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

政府文件翻譯服務

西班牙語和葡萄牙語同屬印歐語系羅曼語族,二者許多發音是溝通 翻譯 翻譯社葡萄牙語是加泰羅尼亞語以後降生的拉丁系說話的一個分支。雖然葡萄牙語 翻譯書寫很接近於西班牙語,但比擬較而言,葡萄牙語更柔和,因為它包含了一些西班牙語所不具有的鼻音。

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

英文翻譯中文

本土說話課納新居民語 108年上路

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

烏加里特語翻譯小弟我現住東京 翻譯公司用的就是RX100, 只有日文,連英文都沒有唷!

翻譯回饋
文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

日文文章翻譯推薦

▲國民黨中心委員連勝文15日在臉書指出,要提升台灣整體英語能力,當局部門必需先從晉升自己的英語能力起頭。(資料照/記者林敬旻攝)

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

印尼文翻譯

文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()

依地語翻譯


文章標籤

jacobsxm2iww3 發表在 痞客邦 留言(0) 人氣()