1. 程式人生 > >《C和C++程式設計師面試祕笈[精品]》-筆記

《C和C++程式設計師面試祕笈[精品]》-筆記

2015-12-16
原文:在C++中可以通過域操作符“::”來直接操作全域性變數


2015-12-16
原文:字尾式(i++)必須返回物件的值,所以導致在大物件的時候產生了較大的複製開銷,引起效率降低。因此處理使用者自定義型別(注意不是指內建型別)的時候,應該儘可能地使用字首式遞增/遞減,因為它天生“體質”較佳。


2015-12-16
原文:內建資料型別的情況,效率沒有區別。 自定義資料型別的情況,++i效率較高。


2015-12-16
原文:當表示式中存在有符號型別和無符號型別時,所有的運算元都自動轉換成無符號型別


2015-12-18
原文:按位異或運算子“^”的功能是將參與運算的兩數各對應的二進位制位相異或,如果對應的二進位制位相同,則結果為0,否則結果為1


2015-12-22
原文:C是一個結構化語言,它的重點在於演算法和資料結構。對語言本身而言,C是C++的子集。C程式的設計首要考慮的是如何通過一個過程,對輸入進行運算處理,得到輸出。對於C++,首要考慮的是如何構造一個物件模型,讓這個模型能夠配合對應的問題,這樣就可以通過獲取物件的狀態資訊得到輸出或實現過程控制。 因此,C與C++的最大區別在於,它們用於解決問題的思想方法不一樣。


2015-12-22
原文:C 是面向過程化的,但是 C++不是完全面向物件化的。在 C++中也完全可以寫出與 C一樣過程化的程式,所以只能說C++擁有面向物件的特性。Java是真正面向物件化的。


2015-12-24
原文:很多時候,我們需要在程式退出的時候做一些諸如釋放資源的操作,但程式退出的方式有很多種,例如main()函式執行結束,在程式的某個地方用exit()結束程式,使用者通過Ctrl+C等操作發訊號來終止程式,等等,因此需要有一種與程式退出方式無關的方法來進行程式退出時的必要處理。方法就是用atexit()函式來註冊程式正常終止時要被呼叫的函式。 atexit()函式的引數是一個函式指標,函式指標指向一個沒有引數也沒有返回值的函式。


2015-12-24
原文:可以用 atexit()函式來註冊程式正常終止時要被呼叫的函式,並且在 main()函式結束時,呼叫這些函式的順序與註冊它們的順序相反。


2015-12-25
原文:巨集只是簡單的文字替換


2015-12-25
原文:使用#把巨集引數變為一個字串,用##把兩個巨集引數貼合在一起。


2015-12-25
原文:1 #define WORD_LO(xxx) ((byte) ((word)(xxx) & 255)) 2 #define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8)) 一個字由兩個位元組組成。因此WORD_LO(xxx)取引數xxx的低8位,WORD_HI(xxx)取引數xxx的高8位。


2015-12-25
原文:#define ARR_SIZE(a) (sizeof((a)) / sizeof((a[0]))) 假設一個數組定義如下。 int array[100]; 它含有100個int型的元素。如果int為4個位元組,那麼這個陣列總共有400個位元組。sizeof(array)為總大小,即400個位元組;sizeof(array[0])為一個int大小,即4個位元組。兩個大小相除就是 100,也就是陣列的元素個數。這裡,為了保證巨集定義不會發生“二義性”,在a以及a[0]上都加了括號。


2015-12-31
原文:a1定義為const int*型別,注意這裡的const在int*的左側,它是用修飾指標所指向的變數,即指標指向為常量


2015-12-31
原文:a2定義為int* const型別,注意這裡的const在int*的右側,它是用修飾指標本身,即指標本身為常量


2015-12-31
原文:a3 定義為const int* const型別,這裡有兩個const,分別出現在int*的左右兩側,因此它表示不僅指標本身不能修改,並且其指向的內容也不能修改


2016-01-04
原文:#define常量則是一個Compile-Time概念,它的生命週期止於編譯期,它存在於程式的程式碼段,在實際程式中它只是一個常數、一個命令中的引數,並沒有實際的存在。 const常量存在於程式的資料段,並在堆疊分配了空間。const常量是一個Run-Time的概念,它在程式中確確實實地存在著並可以被呼叫、傳遞。const常量有資料型別,而巨集常量沒有資料型別。編譯器可以對const常量進行型別安全檢查。


2016-01-04
原文:C++中const有什麼作用(至少說出3個)


2016-01-04
原文:(1)const用於定義常量:const定義的常量編譯器可以對其進行資料靜態型別安全檢查。


2016-01-04
原文:(2)const修飾函式形式引數:當輸入引數為使用者自定義型別和抽象資料型別時,應該將“值傳遞”改為“const &傳遞”,可以提高效率。比較下面兩段程式碼: 1 void fun(A a); 2 void fun(A const &a); 第一個函式效率低。函式體內產生A型別的臨時物件用於複製引數a,臨時物件的構造、複製、析構過程都將消耗時間。而第二個函式提高了效率。用“引用傳遞”不需要產生臨時物件,節省了臨時物件的構造、複製、析構過程消耗的時間。但光用引用有可能改變a,所以加const。


2016-01-04
原文:(3)const修飾函式的返回值:如給“指標傳遞”的函式返回值加const,則返回值不能被直接修改,且該返回值只能被賦值給加const修飾的同類型指標。例如。 1 const char *GetChar(void){}; 2 char *ch = GetChar(); // error 3 const char *ch = GetChar(); // correct


2016-01-04
原文:(4)const修飾類的成員函式(函式定義體):任何不會修改資料成員的函式都應用const修飾,這樣,當不小心修改了資料成員或呼叫了非const成員函式時,編譯器都會報錯。const修飾類的成員函式形式為: 1 int GetCount(void) const;


2016-01-05
原文:在C語言中,關鍵字static有3個明顯的作用: (1)在函式體,一個被宣告為靜態的變數在這一函式被呼叫的過程中維持其值不變。 (2)在模組內(但在函式體外),一個被宣告為靜態的變數可以被模組內所有函式訪問,但不能被模組外其他函式訪問。它是一個本地的全域性變數。 (3)在模組內,一個被宣告為靜態的函式只可被這一模組內的其他函式呼叫。那就是這個函式被限制在宣告它的模組的本地範圍內使用。


2016-01-05
原文:static 全域性變數與普通的全域性變數有什麼區別?static 區域性變數和普通的區域性變數有什麼區別?static函式與普通函式有什麼區別?


2016-01-05
原文:全域性變數的說明之前再加上static就構成了靜態的全域性變數。全域性變數本身就是靜態儲存方式,靜態全域性變數當然也是靜態儲存方式。這兩者在儲存方式上並無不同。這兩者的區別在於,非靜態全域性變數的作用域是整個源程式,當一個源程式由多個原始檔組成時,非靜態的全域性變數在各個原始檔中都是有效的;而靜態全域性變數則限制了其作用域,即只在定義該變數的原始檔內有效,在同一源程式的其他原始檔中不能使用它。由於靜態全域性變數的作用域侷限於一個原始檔內,只能為該原始檔內的函式公用,因此可以避免在其他原始檔中引起錯誤。


2016-01-05
原文:從以上分析可以看出,把區域性變數改變為靜態變數後是改變了它的儲存方式,即改變了它的生存期;把全域性變數改變為靜態變數後是改變了它的作用域,限制了它的使用範圍。


2016-01-05
原文:static 函式與普通函式的作用域不同。static函式的作用域僅在本檔案,只在當前原始檔中使用的函式應該說明為內部函式(static),內部函式應該在當前原始檔中說明和定義。對於可在當前原始檔以外使用的函式,應該在一個頭檔案中說明,要使用這些函式的原始檔要包含這個標頭檔案。 因此,static全域性變數與普通的全域性變數的區別是,static全域性變數只初始化一次,防止在其他檔案單元中被引用;static區域性變數和普通區域性變數的區別是,static區域性變數只被初始化一次,下一次依據上一次結果值;static函式與普通函式的區別是,static函式在記憶體中只有一份,普通函式在每個被呼叫中維持一份複製品。


2016-01-05
原文:static全域性變數與普通全域性變數的區別是,static全域性變數只初始化一次,防止在其他檔案單元中被引用。 static區域性變數和普通區域性變數的區別是,static區域性變數只被初始化一次,下一次依據上一次結果值。 static函式與普通函式的區別是,static函式在記憶體中只有一份,普通函式在每個被呼叫中維持一份複製品。


2016-01-06
原文:類中的靜態成員或方法不屬於類的例項,而屬於類本身並在所有類的例項間共享。在呼叫它們時應該用類名加上操作符“::”來引用。


2016-01-06
原文:對陣列變數做 sizeof 運算得到的是陣列佔用記憶體的總大小。在這裡,str的總大小為strlen("Hello")+1,注意陣列中要有一個元素儲存字串結束符


2016-01-06
原文:這是因為當我們呼叫函式 Func(str)時,由於陣列是“傳址”的,程式會在棧上分配一個4位元組的指標來指向陣列,因此結果也是4。


2016-01-06
原文:總之,陣列和指標的 sizeof 運算有細微的區別。如果陣列變數被傳入函式中做 sizeof運算,則和指標的運算沒有區別,否則得到整個陣列佔用記憶體的總大小。對於指標,無論是何種型別的指標,其大小都是固定的,在32位WinNT平臺下都是4。


2016-01-12
原文:位元組對齊引起的。對齊的作用和原因:各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。其他平臺可能沒有這種情況,但是最常見的是,如果不按照適合其平臺的要求對資料存放進行對齊,會給存取效率帶來損失。例如,有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)存放在偶地址開始的地方,那麼一個讀週期就可以讀出;而如果存放在奇地址開始的地方,可能會需要 2 個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該int型資料。顯然,在讀取效率上下降很多。這也是空間和時間的博弈。


2016-01-12
原文:位元組對齊的細節和編譯器實現相關,一般而言,需要滿足3個準則: 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除; 結構體每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要,編譯器會在成員之間加上填充位元組(internal adding); 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要,編譯器會在最末一個成員之後加上填充位元組(trailing padding)。


2016-01-12
原文:現在以下面的結構體為例。 1 struct S 2 { 3  char c1; 4  int i; 5  char c2; 6 }; c1的偏移量為0,i的偏移量為4,c1與i之間便需要3個填充位元組。c2的偏移量為8,加起來就是1+3+4+1,等於9個位元組。由於這裡最寬的基本型別為int,大小為4個位元組,再補3個位元組湊成4的倍數。這樣一共是12個位元組。


2016-01-14
原文:普通函式不佔用記憶體,只要有虛擬函式,就會佔用一個指標大小的記憶體,原因是系統多用了一個指標維護這個類的虛擬函式表,並且注意這個虛擬函式無論含有多少項(類中含有多少個虛擬函式)都不會再影響類的大小。


2016-01-14
原文:使用sizeof計算虛擬繼承的類物件的空間大小 出現頻率:★★★★ 1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 }; 7 8 class B 9 { 10 }; 11 12 class C:public A, public B 13 { 14 }; 15


2016-01-14
原文:16 class D:virtual public A 17 { 18 }; 19 20 class E:virtual public A, virtual public B 21 { 22 }; 23


2016-01-14
原文:24 class F 25 { 26 public: 27  int a; 28  static int b; 29 } 30 31 int F::b = 10; 32 33 int main() 34 { 35  cout << "sizeof(A) = " << sizeof(A) << endl; 36  cout << "sizeof(B) = " << sizeof(B) << endl; 37  cout << "sizeof(C) = " << sizeof(C) << endl; 38  cout << "sizeof(D) = " << sizeof(D) << endl; 39  cout << "sizeof(E) = " << sizeof(E) << endl; 40  cout << "sizeof(F) = " << sizeof(F) << endl;


2016-01-14
原文:42  return 0; 43 } 【解析】 程式說明如下。 程式碼第 35 行,由於 A 是空類,編譯器會安插一個 char 給空類,用來標記它的每一個物件。因此其大小為1個位元組。 程式碼第36 行,類B 大小和A 相同,都是1 個位元組。 程式碼第37 行,類C 是多重繼承自A 和B,其大小仍然為1 個位元組。


2016-01-14
原文:程式碼第38 行,類D 是虛繼承自A,編譯器為該類安插一個指向父類的指標,指標大小為4。由於此類有了指標,編譯器不會安插一個char了。因此其大小是4個位元組。 程式碼第 39 行,類 E 虛繼承自 A 並且也虛繼承自 B,因此它有指向父類 A 的指標與父類B的指標,加起來大小為8個位元組。 程式碼第40行,類F含有一個靜態成員變數,這個靜態成員的空間不在類的例項中,而是像全域性變數一樣在靜態儲存區中,被每一個類的例項共享,因此其大小是4個位元組。 【答案】 sizeof(A) = 1 sizeof(B) = 1 sizeof(C) = 1 sizeof(D) = 4


2016-01-14
原文:sizeof(E) = 8 sizeof(F) = 4


2016-01-14
原文:理解sizeof與strlen的區別 出現頻率:★★★ 【解析】 它們的區別如下。 sizeof 是操作符,strlen 是函式。 sizeof 操作符的結果型別是size_t,它在標頭檔案中typedef 為unsignedint 型別,該型別保證能容納實現所建立的最大物件的位元組大小。 sizeof 可以用型別做引數,strlen 只能用char*做引數,且必須是以''\0''結尾的。 陣列做sizeof 的引數不退化,傳遞給strlen 就退化為指標了。 大部分編譯程式在編譯的時候 sizeof 就被計算過了,這就是 sizeof(x)可以用來定義陣列維數的原因。strlen的結果要在執行的時候才能計算出來,它用來計算字串的長度,不是型別佔記憶體的大小。 sizeof 後如果是型別,必須加括弧;如果是變數名,可以不加括弧。這是因為sizeof是個操作符,而不是個函式。


2016-01-14
原文:在計算字串陣列的長度上有區別。例如, 1 char str[20]="0123456789"; 2 int a=strlen(str); 3 int b=sizeof(str); a計算的是以0x00結束的字串的長度(不包括0x00結束符),這裡結果是10。 b計算的則是分配的陣列str[20]所佔的記憶體空間的大小,不受裡面儲存內容的改變而改變,這裡結果是20。 如果要計算指標指向的字串的長度,則一定要使用strlen。例如。 1  char* ss = "0123456789"; 2  int a = sizeof(ss); 3  int b = strlen(ss); a計算的是ss指標佔用的記憶體空間大小,這裡結果是4。 b計算的是ss指向的字串的長度,這裡結果是10。


2016-01-14
原文:(1)函式UpperCase(char str[])的意圖是將str 指向的字串中小寫字母轉換為大寫字母。於是在程式碼第9行利用sizeof(str)/sizeof(str[0])獲得陣列中的元素個數以便做迴圈操作。然而,sizeof(str)得到的並不是陣列佔用記憶體的總大小,而是一個字元指標的大小,為4位元組。因此這裡只能迴圈4次,在程式碼第18行main()函式的呼叫中只能改變對陣列的前四個字元進行轉換。轉換的結果為“ABCDe”。 (2)在程式碼第 17 行,這裡的意圖是使用要列印字串的長度。然而,sizeof(str)/sizeof(str[0])計算的是陣列元素的個數,比字串的長度大1,原因是陣列的長度還包括字串的結束符'\0'。 應該用strlen()函式來代替sizeof計算字串長度。正確的程式碼如下。


2016-01-14
原文:1 #include <iostream.h> 2 #include <string.h> 3 4 void UpperCase(char str[]) 5 { 6  int test = sizeof(str); 7  int test2 = sizeof(str[0]); 8 9  for(size_t i=0; i<strlen(str); ++i) //計算字串的長度 10   if('a'<=str[i] && str[i]<='z') 11     str[i] -= ('a'-'A'); 12 } 13 14 int main() 15 { 16  char str[] = "aBcDe"; 17 18  cout << "The length of str is " << strlen(str) << endl; //計算字串的長度 19  UpperCase( str ); 20  cout << str << endl; 21  return 0;


2016-01-14
原文:聯合體的大小取決於它所有的成員中佔用空間最大的一個成員的大小。並且對於複合資料型別,如 union、struct、class 的對齊方式為成員中最大的成員對齊方式。 對於u來說,大小就是最大的double型別成員a了,即sizeof(u)=sizeof(double)=8。 對於 u2 來說,最大的空間是 char[13]型別的陣列。這裡要注意,由於它的另一個成員int b 的存在,u2 的對齊方式變成4,也就是說,u2 的大小必須在4的對齊上,所以佔用的空間變為最接近13的對齊,即16。 對於u3 來說,最大的空間是char[13]型別的陣列,即sizeof(u3)=13。


2016-01-14
原文:union u 4 { 5  double a; 6  int b; 7 }; 8 9 union u2 10 { 11  char a[13]; 12  int b; 13 }; 14 15 union u3 16 { 17  char a[13]; 18  char b; 19 };


2016-01-14
原文:編譯器會盡量把資料放在它的對齊上以提高記憶體的命中率。對齊是可以更改的,使用#pragma pack(x)可以改變編譯器的對齊方式。C++固有型別的對齊取編譯器對齊方式與自身大小中較小的一個。例如,指定編譯器按 2 對齊,int型別的大小是4,則int的對齊為2和4中較小的2。在預設的對齊方式下,因為幾乎所有的資料型別都不大於預設的對齊方式8(除了long double),所以所有的固有型別的對齊方式可以認為就是型別自身的大小


2016-01-14
原文:#pragma pack(2) 4 5 union u 6 { 7  char buf[9]; 8  int a; 9 };


2016-01-14
原文:C++固有型別的對齊取編譯器對齊方式與自身大小中較小的一個。 上面的程式中,由於使用手動更改對齊方式為2,所以int的對齊也變成了2(int自身對齊為4),u的對齊取成員中最大的對齊,也是2,所以此時sizeof(u)=10。 如果註釋上面的第3行,int的對齊使用4,u的對齊取成員中最大的對齊,也是4,所以此時sizeof(u)=12。


2016-01-15
原文:引入行內函數的主要目的是,用它替代 C 語言中表達式形式的巨集定義來解決程式中函式呼叫的效率問題。在C語言裡可以使用如下的巨集定義。 1 #define ExpressionName(Var1,Var2) (Var1+Var2)*(Var1-Var2) 這種巨集定義在形式及使用上像一個函式,但它使用前處理器實現,沒有了引數壓棧、程式碼生成等一系列的操作,因此效率很高。這種巨集定義在形式上類似於一個函式,但在使用它時,僅僅只是做前處理器符號表中的簡單替換,因此它不能進行引數有效性的檢測,也就不能享受 C++編譯器嚴格型別檢查的好處。另外,它的返回值也不能被強制轉換為可轉換的合適型別,這樣,它的使用就存在著一系列的隱患和侷限性。 另外,在 C++中引入了類及類的訪問控制,這樣,如果一個操作或者說一個表示式涉及類的保護成員或私有成員,你就不可能使用這種巨集定義來實現(因為無法將 this 指標放在合適的位置)。


2016-01-15
原文:inline推出的目的,也正是為了取代這種表示式形式的巨集定義。它消除了它的缺點,同時又很好地繼承了它的優點。


2016-01-15
原文:理解行內函數相比於巨集定義的優越之處 出現頻率:★★★ 【解析】 有如下幾種原因: inline定義的類的行內函數,函式的程式碼被放入符號表中,在使用時直接進行替換(像巨集一樣展開),沒有了呼叫的開銷,效率也很高。 類的行內函數也是一個真正的函式。編譯器在呼叫一個行內函數時,首先會檢查它的引數的型別,保證呼叫正確;然後進行一系列的相關檢查,就像對待任何一個真正的函式一樣。這樣就消除了它的隱患和侷限性。 inline 可以作為某個類的成員函式,當然就可以在其中使用所在類的保護成員及私有成員。


2016-01-15
原文:理解行內函數的作用場合 出現頻率:★★★ 【解析】 首先使用inline函式可以完全取代表達式形式的巨集定義。 行內函數在 C++類中應用最廣的,應該是用來定義存取函式。我們定義的類中一般會把資料成員定義成私有的或者保護的,這樣,外界就不能直接讀寫我們類成員的資料了。對於私有或者保護成員的讀寫就必須使用成員介面函式來進行。如果我們把這些讀寫成員函式定義成行內函數的話,將會獲得比較好的效率。例如下面的程式碼:


2016-01-15
原文:1 Class A 2 { 3 Private: 4   int nTest; 5 Public: 6   int readTest() 7   { 8    return nTest; 9   } 10  void setTest(int i); 11 }; 12 13 inline void A::setTest(int i) 14 { 15  nTest = i; 16 }; 類 A 的成員函式 readTest()和 setTest()都是 inline 函式。readTest()函式的定義體被放在類宣告之中,因而readTest()自動轉換成inline函式;setTest()函式的定義體在類宣告之外,因此要加上inline關鍵字。


2016-01-19
原文:內聯是以程式碼膨脹(複製)為代價的,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。如果執行函式體內程式碼的時間相比於函式呼叫的開銷較大,那麼效率的收穫會很少。另一方面,每一處行內函數的呼叫都要複製程式碼,將使程式的總程式碼量增大,消耗更多的記憶體空間。以下情況不宜使用內聯。 如果函式體內的程式碼比較長,使用內聯將導致記憶體消耗代價較高。 如果函式體內出現迴圈,那麼執行函式體內程式碼的時間要比函式呼叫的開銷大。 另外,類的建構函式和解構函式容易讓人誤解成使用內聯更有效。要當心建構函式和解構函式可能會隱藏一些行為,如“偷偷地”執行了基類或成員物件的建構函式和解構函式。


2016-01-19
原文:所以不要隨便地將建構函式和解構函式的定義體放在類宣告中。一個好的編譯器將會根據函式的定義體,自動地取消不值得的內聯(這說明了 inline 不應該出現在函式的宣告中)。


2016-01-19
原文:理解行內函數與巨集定義的區別 出現頻率:★★★★ 【解析】 二者區別如下: 行內函數在編譯時展開,巨集在預編譯時展開。 在編譯的時候,行內函數可以直接被鑲嵌到目的碼中,而巨集只是一個簡單的文字替換。 行內函數可以完成諸如型別檢測、語句是否正確等編譯功能,巨集就不具有這樣的功能。 巨集不是函式,inline 函式是函式。 巨集在定義時要小心處理巨集引數(一般情況是把引數用括號括起來),否則容易出現二義性。而行內函數定義時不會出現二義性。


2016-01-19
原文:7  int a = 10; 8  int b = 20; 9  int &rn = a; 10  int equal; 11 12  rn=b; 13  cout << "a = " << a << endl; 14  cout << "b = " << b << endl; 15 16  rn = 100; 17 18  cout << "a = " << a << endl; 19  cout << "b = " << b << endl; 20 21  equal = (&a == &rn)? 1: 0; 22 23  cout << "equal = " << equal << endl;


2016-01-19
原文:程式碼第7 行和第8 行,整型變數a 和整型變數b 分別被初始化為10 和20。 程式碼第9 行,宣告rn 為變數a 的一個引用。 程式碼第12 行,將rn 的值賦為b 的值。此時rn 其實就是a 的一個別名,對rn 的賦值其實就是對a的賦值。因此執行完賦值後,a的值就是b的值,即都是20。 程式碼第16 行,將rn 的值賦為100,於是a 的值變成了100。 程式碼第21行,將a的地址與rn的地址進行比較,如果相等,變數equal的值為1,否則為0。將rn宣告為a的引用,不需要為rn另外開闢記憶體單元。rn和a佔記憶體中的同一個儲存單元,它們具有同一地址,所以equal為1。


2016-02-01
原文:引用只能在宣告的時候被賦值,以後都不能再把該引用名作為其他變數名的別名。


2016-02-01
原文:把const放在引用之前表示宣告的是一個常量引用。不能使用常量引用修改引用的變數的值。


2016-02-01
原文:對於常量型別的變數,其引用也必須是常量型別的;對於非常量型別的變數,其引用可以是非常量的,也可以是常量的。但是要注意,無論什麼情況,都不能使用常量引用修改其引用的變數的值。


2016-02-02
原文:引用和指標的區別


2016-02-02
原文:區別如下: (1)初始化要求不同。引用在建立的同時必須初始化,即引用到一個有效的物件;而指標在定義的時候不必初始化,可以在定義後面的任何地方重新賦值。 (2)可修改性不同。引用一旦被初始化為指向一個物件,它就不能被改變為另一個物件的引用;而指標在任何時候都可以改變為指向另一個物件。給引用賦值並不是改變它和原始物件的繫結關係。 (3)不存在 NULL 引用,引用不能使用指向空值的引用,它必須總是指向某個物件;而指標則可以是NULL,不需要總是指向某些物件,可以把指標指向任意的物件,所以指標更加靈活,也容易出錯。 (4)測試需要的區別。由於引用不會指向空值,這意味著使用引用之前不需要測試它的合法性;而指標則需要經常進行測試。因此使用引用的程式碼效率比使用指標的要高。


2016-02-02
原文:(5)應用的區別。如果是指一旦指向一個物件後就不會改變指向,那麼應該使用引用。如果有存在指向 NULL(不指向任何物件)或在不同的時刻指向不同的物件這些可能性,應該使用指標。 實際上,在語言層面,引用的用法和物件一樣;在二進位制層面,引用一般都是通過指標來實現的,只不過編譯器幫我們完成了轉換。總體來說,引用既具有指標的效率,又具有變數使用的方便性和直觀性。


2016-02-03
原文:由於不存在空引用,並且引用一旦被初始化為指向一個物件,它就不能被改變為另一個物件的引用,因此引用很安全。 對於指標來說,它可以隨時指向別的物件,並且可以不被初始化,或為NULL,所以不安全。const 指標仍然存在空指標,並且有可能產生野指標。


2016-02-19
原文:擴充套件知識:解讀複雜指標宣告 使用右左法則:首先從最裡面的圓括號看起,然後往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號裡面所有的東西,就跳出圓括號。重複這個過程,直到整個宣告解析完畢。 這裡對這個法則進行一個小小的修正,應該是從未定義的識別符號開始閱讀,而不是從括號讀起。這是因為一個聲明裡面未定義的識別符號只會有一個。


2016-02-19
原文:現在通過幾個例子來討論如何運用右左法則解讀複雜指標宣告。


2016-02-19
原文:1 int (*func)(int *p); 首先找到那個未定義的識別符號,就是func,它的外面有一對圓括號,而且左邊是一個*號,這說明func是一個指標;然後跳出這個圓括號,先看右邊,也是一個圓括號,這說明(*func)是一個函式,而 func 是一個指向這類函式的指標,就是一個函式指標,這類函式具有int*型別的形參,返回值型別是int。


2016-02-19
原文:2 int (*func)(int *p, int (*f)(int*)); func被一對括號包含,且左邊有一個*號,說明func是一個指標,跳出括號,右邊也有個括號,那麼func 是一個指向函式的指標,這類函式具有int *和int (*)(int*)這樣的形參,返回值為int型別。再來看一看func 的形參int (*f)(int*),類似前面的解釋,f 也是一個函式指標,指向的函式具有int*型別的形參,返回值為int。


2016-02-19
原文:3 int (*func[5])(int *p); func右邊是一個[]運算子,說明func是一個具有5個元素的陣列;func的左邊有一個*,說明 func 的元素是指標。要注意這裡的*不是修飾 func 的,而是修飾 func[5]的,原因是[]運算子優先順序比*高,func先跟[]結合,因此*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明 func 陣列的元素是函式型別的指標,它所指向的函式具有 int*型別的形參,返回值型別為int。


2016-02-23
原文:4 int (*(*func)[5])(int *p); func被一個圓括號包含,左邊又有一個*,那麼func是一個指標;跳出括號,右邊是一個[]運算子號,說明func是一個指向陣列的指標。現在往左看,左邊有一個*號,說明這個陣列的元素是指標;再跳出括號,右邊又有一個括號,說明這個陣列的元素是指向函式的指標。總結一下,就是:func 是一個指向陣列的指標,這個陣列的元素是函式指標,這些指標指向具有int*型別的形參,返回值為int型別的函式。


2016-02-23
原文:5 int (*(*func)(int *p))[5]; func是一個函式指標,這類函式具有int*型別的形參,返回值是指向陣列的指標,所指向的陣列的元素是具有5個int元素的陣列。


2016-02-23
原文: #include <stdio.h> 2 3 int main(void) 4 { 5  int a[5]={1,2,3,4,5}; 6  int *ptr=(int *)(&a+1); 7 8  printf("%d\n", *(a+1)); 9  printf("%d\n", *(ptr-1)); 10 11  return 0; 12 } 【解析】 這裡主要是考查關於指標加減操作的理解。 對指標進行加1操作,得到的是下一個元素的地址,而不是原有地址值直接加1。所以,一個型別為t的指標的移動,以sizeof(t)為移動單位。


2016-02-23
原文:程式碼第5 行,宣告一個一維陣列a,並且a 有5 個元素。 程式碼第6行,ptr是一個int型的指標&a + 1,即取a的地址,該地址的值加sizeof(a)的值,即&a + 5*sizeof(int),也就是a[5]的地址,顯然,當前指標已經越過了陣列的界限。(int *)(&a+1)則是把上一步計算出來的地址,強制轉換為int *型別,賦值給ptr。 程式碼第 8 行,a 與&a 的地址是一樣的,但意思不一樣。a 是陣列首地址,也就是a[0]的地址;&a是物件(陣列)首地址,a+1是陣列下一元素的地址,即a[1];而&a+1是下一個物件的地址,即a[5]。因此這裡輸出為2。 程式碼第9 行,因為ptr 指向a[5],並且ptr 是int*型別,所以*(ptr-1)指向a[4],輸出5。


2016-02-23
原文: char str1[]  = "abc"; 7  char str2[]  = "abc"; 8  const char str3[] = "abc"; 9  const char str4[] = "abc"; 10  const char* str5 = "abc"; 11  const char* str6 = "abc"; 12  char* str7 = "abc"; 13  char* str8 = "abc";


2016-02-23
原文:陣列str1、str2、str3和str4都是在棧中分配的,記憶體中的內容都為"abc"加一個'\0',但是它們的位置是不同的。因此程式碼第15行和第16行的輸出都是0。 指標str5、str6、str7和str8也是在棧中分配的,它們都指向"abc"字串,注意"abc"存放在資料區,所以str5、str6、str7 和str8 其實指向同一塊資料區的記憶體。因此第17、18和19行的輸出是1。


2016-02-24
原文:這裡有個小規則,像這樣連著的兩個詞,前面的一個通常是修飾部分,中心詞是後面一個詞。 常量指標,表述為“是常量的指標”,它首先應該是一個指標。 指標常量,表述為“是指標的常量”,它首先應該是一個常量。 接下來進行詳細分析。 常量指標,它是一個指向常量的指標。設定常量指標指向一個常量,為的就是防止寫程式過程中對指標誤操作出現了修改常量這樣的錯誤,編譯系統就會提示我們出錯資訊。因此,常量指標就是指向常量的指標,指標所指向的地址的內容是不可修改的。


2016-02-24
原文:指標常量,它首先是一個常量,然後才是一個指標。指標常量就是不能修改這個指標所指向的地址,一開始初始化指向哪兒,它就只能指向哪兒了,不能指向其他的地方了,就像一個數組的陣列名一樣,是一個固定的指標,不能對它移動操作。如果使用p++,系統就會提示出錯。但是注意,這個指向的地方里的內容是可以替換的,這和上面說的常量指標是完全不同的概念。總之,指標常量就是指標的常量,它是不可改變地址的指標,但是可以對它所指向的內容進行修改。 【答案】 常量指標就是指向常量的指標,它所指向的地址的內容是不可修改的。 指標常量就是指標的常量,它是不可改變地址的指標,但是可以對它所指向的內容進行修改。


2016-02-25
原文:下述4個指標有什麼區別? 1 char * const p1; 2 char const * p2; 3 const char *p3; 4 const char *constp4; 【解析】 如果const位於*號的左側,則const就是用來修飾指標所指向的變數,即指標指向為常量;如果const位於*號的右側,const就是修飾指標本身,即指標本身是常量。因此,p1指標本身是常量,但它指向的內容可以被修改。p2和p3的情況相同,都是指標所指向的內容為常量。p4則表示指標本身是常量,並且它指向的內容也不可被修改。


2016-02-25
原文:下列關於this指標的敘述中,正確的是(  )。 A.任何與類相關的函式都有this指標 B.類的成員函式都有this指標 C.類的友元函式都有this指標 D.類的非靜態成員函式才有this指標 【解析】 A 錯誤。類的非靜態成員函式是屬於類的物件,含有this指標。而類的static 函式屬於類本身,不含this指標。 B 錯誤。類的非靜態成員函式是屬於類的物件,含有this指標。而類的static 函式屬於類本身,不含this指標。 C 錯誤。友元函式是非成員函式,所以它無法通過this 指標獲得一份拷貝。 D 正確。


2016-02-25
原文:下面的程式碼輸出結果是什麼?如果取消第14行的註釋,輸出又是什麼? 1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 public: 7  int data; 8  MyClass(int data) 9  { 10   this->data = data; 11  } 12  void print() 13  { 14   //cout << data << endl; 15   cout << "hello!" << endl; 16  } 17 };


2016-02-25
原文:20 int main() 21 { 22  MyClass * pMyClass; 23  pMyClass = new MyClass(1); 24  pMyClass->print(); 25  pMyClass[0].print(); 26  pMyClass[1].print(); 27  pMyClass[10000000].print();


2016-02-25
原文:這裡需要明白類函式是如何被編譯以及如何被執行的。 對於類成員函式而言,並不是一個物件對應一個單獨的成員函式體,而是此類的所有物件共用這個成員函式體。當程式被編譯之後,此成員函式地址即已確定。我們常說,呼叫類成員函式時,會將當前物件的 this 指標傳給成員函式。沒錯,一個類的成員函式體只有一份,而成員函式之所以能把屬於此類的各個物件的資料區別開,就在於每次執行類成員函式時,都會把當前物件的 this 指標(物件首地址)傳入成員函式,函式體內所有對類資料成員的訪問,都會被轉化為this->資料成員的方式。 如果print函式裡沒有訪問物件的任何資料成員,那麼此時傳進來的物件this指標實際上是沒有任何用處的。這樣的函式,其特徵與全域性函式並沒有太大區別。但如果取消第 14行的註釋,由於print函式要訪問類的資料成員data,而類的資料成員是伴隨著物件宣告而產生的。但是,我們只 new 了一個 MyClass,顯然,下標"1"和下標"10000000"的 MyClass物件根本不存在,那麼對它們的資料成員訪問也顯然是非法的。


2016-02-25
原文:【答案】 註釋程式碼第14行時的輸出: 1 hello! 2 hello! 3 hello! 4 hello! 取消程式碼第 14行註釋後的輸出: 1 1 2 hello! 3 1 4 hello! 5 -33686019 6 hello! 7 段錯誤


2016-03-13
原文:指標陣列表示它是一個數組,並且陣列中的每一個元素是指標。 陣列指標表示它是一個指標,並且指向了一個數組。


2016-03-14
原文:指標函式是指帶指標的函式,即本質是一個函式,並且返回型別是某一型別的指標。其定義如下: 1 返回型別識別符號 *返回名稱(形式引數表){函式體 } 事實上,每一個函式,即使它不帶有返回某種型別的指標,它本身都有一個入口地址,該地址相當於一個指標。比如函式返回一個整型值,實際上也相當於返回一個指標變數的值,不過這時的變數是函式本身而已,而整個函式相當於一個“變數”。 函式指標是指向函式的指標變數,因而它本身首先應是指標變數,只不過該指標變數指向函式。有了指向函式的指標變數後,可用該指標變數呼叫函式,就如同用指標變數可引用其他型別的變數一樣。


2016-03-14
原文:指標函式是返回指標型別的函式。 函式指標是指向函式地址的指標。


2016-03-14
原文:定義下面的幾種型別變數: a.含有10個元素的指標陣列 b.陣列指標 c.函式指標 d.指向函式的指標陣列 【答案】 a.int *a[10]; b.int *a = new int[10]; c.void (*fn)(int, int); d.int (*fnArray[10])(int, int);


2016-03-14
原文:void (*f)(int, int),f 是指向void max(int x, int y)型別的函式指標。 int *fn(),fn 是返回int 指標型別的函式。 const int *p,p 是一個指向const 的指標,指向一個常量。 int* const q,q 是一個const 指標。 const int* const ptr,ptr 是指向const 的const 指標。


2016-03-14
原文:1 #include <stdio.h> 2 int add1(int a1,int b1); 3 int add2(int a2,int b2); 4 int main(int argc,char* argv[]) 5 { 6  int numa1=1,numb1=2; 7  int numa2=2,numb2=3; 8  int (*op[2])(int a,int b); 9  op[0]=add1; 10  op[1]=add2; 11  printf("%d %d\n",op[0](numa1,numb1),op[1](numa2,numb2)); 12  getchar(); 13 14  return 0; 15 } 16


2016-03-14
原文:17 int add1(int a1,int b1) 18 { 19  return a1+b1; 20 } 21 22 int add2(int a2,int b2) 23 { 24  return a2+b2; 25 }


2016-03-14
原文:【解析】 在程式碼第8行,定義了一個函式指標陣列op,它含有兩個指標元素。在第9行和第10行把這兩個元素分別指向了add1和add2兩個函式地址。最後在第11行打印出使用函式指標呼叫add1和add2這兩個函式返回的結果。 【答案】 1 3 5


2016-03-14
原文:下面的定義有什麼作用? 1 typedef int (*pfun)(int x,int y); 【解析】 這裡的pfun是一個使用typedef自定義的資料型別。它表示一個函式指標,其引數有兩個,都是int型別,返回值也是int型別。可以按如下步驟使用: 1 typedef int (*pfun)(int x,int y); 2 int fun(int x, int y); 3 pfun p = fun; 4 int ret = p(2, 3); 簡單說明: 第1 行定義了pfun 型別,表示一個函式指標型別。 第2 行定義了一個函式。 第3 行定義了一個pfun 型別的函式指標p,並賦給它fun 的地址。 第4 行呼叫p(2, 3),實現fun(2, 3)的呼叫功能。


2016-03-14
原文:malloc與free是C++/C的標準庫函式,new/delete是C++的運算子。它們都可用於申請動態記憶體和釋放記憶體。 對於非內部資料型別的物件而言,光用 malloc/free 無法滿足動態物件的要求。物件在建立的同時要自動執行建構函式,物件在消亡之前要自動執行解構函式。由於 malloc/free是庫函式而不是運算子,不在編譯器控制權限之內,不能夠把執行建構函式和解構函式的任務強加於malloc/free。


2016-03-14
原文:因此,C++需要一個能完成動態記憶體分配和初始化工作的運算子new,以及一個能完成清理與釋放記憶體工作的運算子delete。注意:new/delete不是庫函式。請看下面的例子。 1 #include <iostream> 2 using namespace std; 3 4 class Obj 5 { 6 public: 7  Obj(void) 8  { 9   cout << "Initialization" << endl; 10  } 11 ~Obj(void) 12  { 13   cout << "Destroy" << endl; 14  } 15 }; 16


2016-03-14
原文:17 void UseMallocFree(void) 18 { 19  cout << "in UseMallocFree()..." << endl; 20  Obj *a = (Obj *)malloc(sizeof(Obj)); 21  free(a); 22 } 23


2016-03-14
原文:void UseNewDelete(void) 25 { 26  cout << "in UseNewDelete()..." << endl; 27  Obj *a = new Obj; 28  delete a; 29 } 30 31 int main() 32 { 33  UseMallocFree(); 34  UseNewDelete(); 35 36  return 0; 37 }


2016-03-14
原文:在這個示例中,類 Obj 只有建構函式和解構函式的定義,這兩個成員函式分別列印一句話。函式UseMallocFree()中呼叫malloc/free 申請和釋放堆記憶體;函式UseNewDelete ()中呼叫new/delete申請和釋放堆記憶體。可以看到函式UseMallocFree()執行時,類Obj的建構函式和解構函式都不會被呼叫;而函式UseNewDelete ()執行時,類Obj的建構函式和解構函式都會被呼叫。執行結果如下: in UseMallocFree()... in UseNewDelete()... Initialization Destroy


2016-03-14
原文:對於非內部資料型別的物件而言,物件在消亡之前要自動執行解構函式。由於malloc/free是庫函式而不是運算子,不在編譯器控制權限之內,不能把執行建構函式和解構函式的任務強加於malloc/free,因此只有使用new/delete運算子。


2016-03-15
原文: #include <stdio.h> 2 #include <malloc.h> 3 4 struct Tag_Node 5 { 6  struct Tag_Node* left; 7  struct Tag_Node* right; 8  int value; 9 }; 10 typedef struct Tag_Node TNode; 11 12 TNode* root = NULL; 13


2016-03-15
原文:14 void append(int N); 15 void print(); 16 17 int main() 18 { 19  append(63); 20  append(45); 21  append(32); 22  append(77); 23  append(96); 24  append(21); 25  append(17); 26  printf("head: %d\n", root->value); 27  print();     //列印連結串列所有元素 28 } 29 30 void append(int N) 31 { 32  TNode* NewNode = (TNode *)malloc(sizeof(TNode)); 33  NewNode->value = N; 34  NewNode->left = NULL;  //初始化left 35  NewNode->right = NULL;  //初始化right 36


2016-03-15
原文:37  if(root == NULL) 38  { 39   root = NewNode; 40   return; 41  } 42  else 43  { 44   TNode* temp; 45   temp=root; 46 48     (N < temp->value && temp->right != NULL)) 47   while((N >= temp->value && temp->left != NULL) || 49   { 50     while(N >= temp->value && temp->left != NULL) 51      temp = temp->left; 52     while(N < temp->value && temp->right != NULL) 53      temp = temp->right; 54   } 55   if(N >= temp->value)


2016-03-15
原文:56   { 57     temp->left = NewNode; 58     NewNode->right = temp; //形成雙向連結串列 59   } 60   else 61   { 62     temp->right = NewNode; 63     NewNode->left = temp;  //形成雙向連結串列 64   } 65   return; 66  } 67 } 68 69 void print() 70 { 71  TNode* leftside = NULL; 72 73  if (root == NULL) 74  { 75   printf("There is not any element1"); 76   return; 77  } 78


2016-03-15
原文:79  leftside = root->left; 80 81  while(1) 82  { 83   if (leftside->left == NULL) 84   { 85    break; 86   } 87   leftside = leftside->left; 88  } 89


2016-03-15
原文:90  while(leftside != NULL) 91  { 92   printf("%d ", leftside->value); 93   leftside = leftside->right; 94  } 95 }


2016-03-16
原文:C語言的標準記憶體分配函式:malloc、calloc、realloc、free等。 malloc與calloc的區別為1塊與n塊的區別。 malloc 的呼叫形式為(型別*)malloc(size):在記憶體的動態儲存區中分配一塊長度為“size”位元組的連續區域,返回該區域的首地址,此時記憶體中的值沒有初始化,是個隨機數。 calloc 的呼叫形式為(型別*)calloc(n,size):在記憶體的動態儲存區中分配n 塊長度為“size”位元組的連續區域,返回首地址,此時記憶體中的值都被初始化為0。 realloc 的呼叫形式為(型別*)realloc(*ptr,size):將 ptr 記憶體大小增大到 size,新增加的記憶體塊沒有初始化。 free 的呼叫形式為free(void*ptr):釋放ptr 所指向的一塊記憶體空間。 C++中,new/delete函式可以呼叫類的建構函式和解構函式。


2016-03-16
原文:1 #include <iostream> 2 using namespace std; 3 4 void GetMemory(char *p, int num) 5 { 6  p = (char *)malloc(sizeof(char) *num); 7 }; 8 9 void GetMemory2(char **p, int num) 10 { 11  *p = (char *)malloc(sizeof(char) *num); 12 }; 13 14 void GetMemory3(char* &p, int num) 15 { 16  p = (char *)malloc(sizeof(char) *num);


2016-03-16
原文:17 }; 18 19 char *GetMemory4(int num) 20 { 21  char *p = (char *)malloc(sizeof(char) *num); 22 23  return p; 24 } 25 26 int main(void) 27 { 28  char *str1 = NULL; 29  char *str2 = NULL; 30  char *str3 = NULL; 31  char *str4 = NULL; 32 33  //GetMemory(str1, 20); 34  GetMemory2(&str2, 20); 35  GetMemory3(str3, 20); 36  str4 = GetMemory4(20); 37


2016-03-16
原文:38  strcpy(str2, "GetMemory 2"); 39  strcpy(str3, "GetMemory 3"); 40  strcpy(str4, "GetMemory 4"); 41 42  cout << "str1 == NULL? " << (str1 == NULL? "yes":"no") << endl; 43  cout << "str2:" << str2 << endl; 44  cout << "str3:" << str3 << endl; 45  cout << "str4:" << str4 << endl; 46 47  free(str2); 48  free(str3); 49  free(str4); 50  str2 = NULL; 51  str3 = NULL; 52  str4 = NULL; 53 54  return 0; 55 } 在上面的程式碼中,GetMemory2()函式採用二維指標作為引數傳遞;GetMemory3()函式採用指標的引用作為引數傳遞;GetMemory4()函式採用返回堆記憶體指標的方式。可以看到這3個函式


2016-03-16
原文:都能起到相同的作用。 另外注意第47~52行,這裡在主函式推出之前把指標str2、str3和str4指向的堆記憶體釋放並把指標賦為NULL。每當決定不再使用堆記憶體時,應該把堆記憶體釋放,並把指標賦為NULL,這樣能避免記憶體洩漏以及產生野指標,是良好的程式設計習慣。 程式執行結果如下所示。 1 str1 == NULL? Yes 2 str2:GetMemory 2 3 str3:GetMemory 3 4 str3:GetMemory 4


2016-03-16
原文:(1)從靜態儲存區域分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在,例如全域性變數。 (2)在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。處理器的指令集中有關於棧記憶體的分配運算,因此效率很高,但是分配的記憶體容量有限。 (3)從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc或new申請任意多少的記憶體,程式設計師自己負責在何時用 free 或 delete 釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。


2016-03-16
原文:控制代碼在 Windows 程式設計中是一個很重要的概念,在許多地方都扮演著重要的角色。在Windows環境中,控制代碼是用來標識專案的,這些專案包括: 模組(module)。 任務(task)。 例項(instance)。 檔案(file)。 記憶體塊(block of memory)。 選單(menu)。 控制(control)。 字型(font)。 資源(resource),包括圖示(icon)、游標(cursor)、字串(string)等。 GDI物件(GDI object),包括點陣圖(bitmap),畫刷(brush)、元檔案(metafile),調色盤(palette)、畫筆(pen)、區域(region),以及裝置描述表(device context)。


2016-03-16
原文:Windows 是一個以虛擬記憶體為基礎的作業系統。在這種系統環境下,Windows 記憶體管理器經常在記憶體中來回移動物件,以此來滿足各種應用程式的記憶體需要。物件被移動意味著它的地址變化了。由於地址總是如此變化,所以Windows作業系統為各應用程式騰出一些記憶體儲地址,用來專門登記各應用物件在記憶體中的地址變化,而這地址(儲存單元的位置)本身是不變的。Windows 記憶體管理器在移動物件在記憶體中的位置後,把物件新的地址告知這個控制代碼地址來儲存。這樣我們只需記住這個控制代碼地址就可以間接地知道物件具體在記憶體中的哪個位置。這個地址是在物件裝載(Load)時由系統分配給的,當系統解除安裝時(Unload)又釋放給系統。 因此,Windows 程式中並不是用實體地址來標識一個記憶體塊、檔案、任務或動態裝入模組的,相反,WINDOWS API 給這些專案分配確定的控制代碼,並將控制代碼返回給應用程式,然後通過控制代碼來進行操作。


2016-03-16
原文:在Windows程式設計中會用到大量的控制代碼,比如HINSTANCE(例項控制代碼)、HBITMAP(點陣圖控制代碼)、HDC(裝置描述表控制代碼)、HICON(圖示控制代碼)等。這當中還有一個通用的控制代碼,就是HANDLE,比如下面的語句: 1 HINSTANCE hInstance; 2 HANDLE hInstance; 控制代碼地址(穩定)→記載著物件在記憶體中的地址→物件在記憶體中的地址(不穩定)→實際物件。但是,必須注意的是,程式每次重新啟動,系統不能保證分配給這個程式的控制代碼還是原來的那個控制代碼,而且絕大多數情況的確是不一樣的。


2016-03-16
原文:指標對應著一個數據在記憶體中的地址,得到了指標就可以自由地修改該資料。Windows 並不希望一般程式修改其內部資料結構,因為這樣太不安全。所以 Windows給每個使用GlobalAlloc等函式宣告的記憶體區域指定一個控制代碼,控制代碼是一種指向指標的指標。


2016-03-16
原文:控制代碼和指標都是地址,不同之處在於: (1)控制代碼所指的可以是一個很複雜的結構,並且很有可能是與系統有關的。比如說執行緒的控制代碼,它指向的就是一個類或者結構,它和系統有很密切的關係。當一個執行緒由於不可預料的原因而終止時,系統就可以返回它所佔用的資料,如 CPU、記憶體等。反過來想可以知道,這個控制代碼中的某一些項是與系統進行互動的。由於Windows系統是一個多工的系統,它隨時都可能要分配記憶體、回收記憶體、重組記憶體。 (2)指標也可以指向一個複雜的結構,但是通常是由使用者定義的,所以必需的工作都要使用者完成,特別是在刪除的時候。


2016-03-16
原文:在C/C++中沒有專門的字串變數,通常用一個字元陣列來存放一個字串。字串是以'\0'作為串的結束符。C/C++提供了豐富的字串處理函式,下面列出了幾個最常用的函式: 字串輸出函式puts; 字串輸入函式gets; 字串連線函式strcat; 字串複製函式strcpy; 測字串長度函式strlen。 字串是筆試以及面試的熱門考點,通過字串測試可以考查程式設計師的程式設計規範以及程式設計習慣。其中也包括了許多知識點,例如記憶體越界、指標與陣列操作等等。許多公司在面試時會要求應試者寫一段 strcpy 複製字串或字串子串操作的程式。本章列舉了一些與字串相關的面試題及其解析,有些題要求較高的程式設計技巧。


2016-03-16
原文:C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串。下面列舉了各函式的方法及其說明。 itoa():將整型值轉換為字串。 ltoa():將長整型值轉換為字串。 ultoa():將無符號長整型值轉換為字串。 gcvt():將浮點型數轉換為字串,取四捨五入。 ecvt():將雙精度浮點型值轉換為字串,轉換結果中不包含十進位制小數點。 fcvt():以指定位數為轉換精度,其餘同ecvt()。 還可以使用sprintf系列函式把數字轉換成字串,這種方式的速度比itoa()系列函式的速度慢。


2016-03-16
原文:如果不使用 atoi 或 sprintf 等庫函式,我們可以通過把整數的各位上的數字加'0'轉換成char 型別並存到字元陣列中。但要注意,需要採用字串逆序的方法。


2016-03-16
原文:atof():將字串轉換為雙精度浮點型值。 atoi():將字串轉換為整型值。 atol():將字串轉換為長整型值。 strtod():將字串轉換為雙精度浮點型值,並報告不能被轉換的所有剩餘數字。


2016-03-16
原文:strtol():將字串轉換為長整型值,並報告不能被轉換的所有剩餘數字。 strtoul():將字串轉換為無符號長整型值,並報告不能被轉換的所有剩餘數字。


2016-03-16
原文:已知strcpy函式的原型是: char * strcpy(char * strDest,const char * strSrc); (1)不呼叫庫函式,實現strcpy函式。 (2)解釋為什麼要返回char *。 【解析】 程式碼如下。 1 #include <stdio.h> 2 3 char * strcpy(char * strDest, const char * strSrc)


2016-03-16
原文://實現strSrc到strDest的複製 4 { 5   if ((strDest == NULL) || (strSrc == NULL)) //判斷引數strDest和strSrc的有效性 6   { 7    return NULL; 8   } 9   char *strDestCopy = strDest;    //儲存目標字串的首地址 10  while ((*strDest++ = *strSrc++)!='\0'); //把strSrc字串的內容複製到strDest下 11 12  return strDestCopy; 13 } 14


2016-03-16
原文:15 int getStrLen(const char *strSrc)    //實現獲取strSrc字串的長度 16 { 17  int len = 0; //儲存長度 18  while(*strSrc++ != '\0')    //迴圈直到遇見結束符'\0'為止 19  { 20    len++; 21  } 22 23  return len; 24 }; 25 26 int main()


2016-03-16
原文:27 { 28  char strSrc[] = "Hello World!";  //要被複制的源字串 29  char strDest[20];      //要複製到的目的字元陣列 30  int len = 0;       //儲存目的字元陣列中字串的長度 31 32  len = getStrLen(strcpy(strDest, strSrc)); //鏈式表示式,先複製後計算長度 33  printf("strDest: %s\n", strDest); 34  printf("Length of strDest: %d\n", len); 35 36  return 0; 37 }


2016-03-16
原文:實現memcpy函式


2016-03-16
原文:程式程式碼如下所示。 1 #include <stdio.h> 2 #include <assert.h> 3 4 void *memcpy2(void *memTo, const void *memFrom, size_t size) 5 { 6   assert((memTo != NULL) && (memFrom !=


2016-03-16
原文:NULL)); //memTo和memFrom必須有效 7   char *tempFrom = (char *)memFrom;   //儲存memFrom首地址 8   char *tempTo = (char *)memTo;    //儲存memTo首地址 9 10  while(size -- > 0)   //迴圈size次,複製memFrom的值到memTo中 11     *tempTo++ = *tempFrom++ ; 12 13  return memTo; 14 } 15 16 int main() 17 {


2016-03-16
原文:18  char strSrc[] = "Hello World!"; //將被複制的字元陣列 19  char strDest[20];     //目的字元陣列 20 21  memcpy2(strDest, strSrc, 4);  //複製strSrc的前4個字元到strDest中 22  strDest[4] = '\0';     //把strDest的第5個元素賦為結束符'\0' 23  printf("strDest: %s\n", strDest); 24 25  return 0; 26 }


2016-03-16
原文:strcpy與memcpy的區別


2016-03-16
原文:字串複製與記憶體複製之間的區別


2016-03-16
原文:主要有下面幾方面的區別。 複製的內容不同。strcpy只能複製字串,而memcpy可以複製任意內容,例如字元陣列、整型、結構體、類等。 複製的方法不同。strcpy 不需要指定長度,它是遇到字串結束符'\0'而結束的。memcpy則是根據其第三個引數決定複製的長度。 用途不同。通常在複製字串時用strcpy;而若需要複製其他型別資料,則一般用memcpy。


2016-03-18
原文:strcpy庫函式的實現細節


2016-03-18
原文:這個題目非常簡單。我們知道字串是以'\0'作為結束符的,所以只需要做一次遍歷就可以了。但是需要注意的是,要儘量把程式寫得簡單且效率高。看下面的示例程式碼: 1 #include <stdio.h> 2 #include <assert.h> 3 4 int strlen1(const char* src) 5 { 6   assert( NULL != src);  //src必須有效 7   int len = 0;     //儲存src的長度 8   while(*src++ != '\0')  //遇到結束符'\0'時退出迴圈 9    len++;     //每迴圈一次,len加1 10  return len; 11 }


2016-03-18
原文:13 int strlen2(const char* src) 14 { 15  assert( NULL != src);  //src必須有效 16  const char *temp = src;  //儲存src首地址 17  while(*src++ != '\0');  //遇到結束符'\0'時退出迴圈 18  return (src-temp-1);   //返回尾部指標與頭部指標之差,即長度 19 } 20 21 int main() 22 { 23  char p[] = "Hello World!"; 24  printf("strlen1 len: %d\n", strlen1(p)); //列印方法1得到的結果 25  printf("strlen2 len: %d\n", strlen2(p)); //列印方法2得到的結果 26 27  return 0; 28 }


2016-03-18
原文:trlen1和strlen2這兩個函式都可以用來計算字串長度。下面來比較它們的區別: strlen1用一個區域性變數len在遍歷的時候做自增,然後返回len。因此,每當while迴圈一次,就需要執行兩次自增操作。 strlen2用一個區域性變數temp記錄src遍歷前的位置。while迴圈一次只需要一次自增操作。最後返回指標之間的位置差。 顯然,strlen2比strlen1的效率要高,尤其是在字串較長的時候。下面是程式的輸出結果。 1 strlen1 len: 12 2 strlen2 len: 12


2016-03-18
原文:函式strcmp的實現細節


2016-03-18
原文:此題實際上就是做一個C/C++庫函式中的strcmp的實現。對於兩個字串str1和str2,若相等,則返回0,若str1大於str2,則返回1,若str1小於str2,則返回−1。 程式程式碼如下。 1 #include <iostream> 2 using namespace std; 3 4 int mystrcmp(const char *src, const char *dst) 5 { 6  int ret = 0 ; 7  while( !(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 8  {         //迴圈比較兩個字元是否相等 9    ++src;      //如果不等或者到了dst字串末尾,則 10   ++dst;     


2016-03-18
原文:退出迴圈 11  } 12  if ( ret < 0 )      //ret儲存著字元比較的結果 13    ret = -1 ; 14  else if ( ret > 0 ) 15    ret = 1 ; 16  return( ret ); 17 } 18 19 int main() 20 { 21  char str[10] = "1234567"; 22  char str1[10] = "1234567";   //str1 == str 23  char str2[10] = "12345678";  //str2 > str 24  char str3[10] = "1234566";   //str3 < str 25 26  int test1 = mystrcmp(str, str1); //測試str與str1比較 27  int test2 = mystrcmp(str, str2); //測試str與str2比較 28  int test3 = mystrcmp(str, str3); //測試str與str3比較


2016-03-18
原文:30  cout << "test1 = " << test1 << endl; 31  cout << "test2 = " << test2 << endl; 32  cout << "test3 = " << test3 << endl; 33 34  return 0;


2016-03-19
原文:char *get2String(long num)   //得到二進位制形


2016-03-19
原文:式的字串 6 { 7   int i=0; 8   char* buffer; 9   char