C語言最富有迷幻色彩的部分當屬指針部分,無論是指針的定義還是指針的意義都可算是C語言中最復雜的內容。指針不但提供給了程序員直接操作硬件部分的操作接口,還提供給了程序員更多靈活的用法。C++繼承這一高效的機制,同時引入了另一個與指針相似但不相同的機制: 引用。
一、引用
簡單的來說,引用就是變量的別名(alias), 通過別名我們可以操作引用代表的變量。 定義一個引用的語法如下所示:
變量類型 &引用標識符 = 變量名。
Exp:
int iVar=10; int &iRef = iVar; iRef = 20 ; cout<<iVar<<endl;
這段程序執行的結果就是輸出: 20 ;
程序通過引用 iRef 改變了變量iVar的值。
要點:
1、在定義引用的同事必須初始化,指出引用代表的是哪一個變量,而且這種“指向關系”不能改變。
2、引用只是對象的另一個名字,可以通過對象的原標識符訪問對象,也可以通過對象的引用訪問對象。
3、在一個語句定義多個引用的時候,每個引用標識符(引用名)的前面必須都加上&符號,否則就是錯誤。
1、const引用
const引用是指向const對象的引用, 不能通過const引用改變原對象的值。如下所示:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <bitset> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::string; 10 using std::vector; 11 using std::bitset; 12 13 int main() 14 { 15 const int iVar=10; 16 const int &iRef = iVar; 17 iRef = 20; 18 cout<<iVar<<endl; 19 20 return 0; 21 }
上面的程序編譯的結果如下所示:
[root@localhost cpp_src]# g++ test.cpp test.cpp: In function ‘int main()’: test.cpp:17: 錯誤:assignment of read-only reference ‘iRef’
可以發現在第17行,試圖對一個指向const對象的const引用賦值,結果編譯報錯。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <bitset> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::string; 10 using std::vector; 11 using std::bitset; 12 13 int main() 14 { 15 const int iVar=10; 16 const int &iRef = iVar; 17 iRef = 20; 18 19 int &iRef1 = iVar; 20 cout<<iVar<<endl; 21 22 return 0; 23 }
程序編譯結果如下:
[root@localhost cpp_src]# g++ test.cpp test.cpp: In function ‘int main()’: test.cpp:17: 錯誤:assignment of read-only reference ‘iRef’ test.cpp:19: 錯誤:將類型為 ‘int&’ 的引用初始化為類型為 ‘const int’ 的表達式無效
我們發現在程序編譯的時候第19行也報錯啦,報錯的類型是: 將 類型int &的引用初始化類型const int的表達式無效。
2、字面值引用
可以定義const引用代表字面值。實例如下:
int main() { int const &iRef = 100; const string &strRef = "volcanol"; cout << iRef <<endl; cout << strRef <<endl; return 0; }
程序的執行結果如下:
[root@localhost cpp_src]# g++ test.cpp [root@localhost cpp_src]# ./a.out 100 volcanol
上面的實例注意一點: 要對字面值定義別名引用,則必須將別名引用定義為const型的,否則將出現編譯錯誤。
二、指針
指針是什麼,有的地方說是指針是一個地址。這裡我們不對指針的復雜用法進行討論,如果想了解指針的復雜用法可以產考我在園子裡的另外一篇隨筆,鏈接地址
為:http://www.cnblogs.com/volcanol/archive/2011/06/05/2073042.html
1、指針的定義
在C++中定義指針,很簡單,在定義的變量的時候,在變量的前面加上一個 * 就表示要定義一個指針變量。語法如下:
指針要指向的數據類型 * 指針變量名;
Exp:
int *pInt; 定義了一個指向整型變量的指針變量pInt;
string *pStr; 定義了一個指向string類型的對象的指針pStr;
vector<int> *pVectorInt; 定義一個指向vector<int> 容器的指針。
bitset<5> *pBitset5; 定義一個指向bitset<5>類型的對象的指針。
2、指針變量賦值和初始化
指針變量在使用前必須有一個確定的指向,否則就會造成一個游離的指針,操作的游離指針會得到一個意想不到的的結果。通過取得一個變量的地址然後賦值給
指針變量或者初始化指針變量使指針變量有一個確定的指向。 通過操作符 & 取得一個變量/對象的地址或者(指針)。
指針變量初始化:
int iVar = 10; int *pInt = &iVar;
指針變量賦值:
int iVar = 10; int *pInt1; int *pInt2; pInt1 = &iVar; pInt2 = pInt1;
3、指針的引用
通過解引用操作符 * 可以引用指針指向的變量。
int iVar = 20; int *pInt = NULL; pInt = &iVar; cout<< * pInt<<endl;
Exp:
int main() { int iVar = 100; int *pInt = &iVar; cout<<(*pInt)<<endl; string strVar = "volcanol"; string *pStr = &strVar; cout<<(*pStr)<<endl; vector<int> vInt(1); vector<int> *pVecInt=&vInt; cout<<(*pVecInt)[0]<<endl; bitset<5> bitVar(5); bitset<5> *pBitset5 = &bitVar; cout<< (*pBitset5) <<endl; return 0; }
程序的執行結果如下所示:
[root@localhost cpp_src]# g++ test.cpp [root@localhost cpp_src]# ./a.out 100 volcanol 0 00101
要點:
在定義指針變量的時候,必須在每個指針變量的前面都加上 * ,否則定義的就是一個非指針變量。
int *pInt1,pInt2; //pInt1 為指針變量, pInt2為整型變量。
在定義指針變量的時候,有兩種風格的格式: int *pInt 和 int* pInt; 這兩種格式沒有對錯之分,兩種格式C++都是接受的,只是在理解的時候可能會引起
誤解。為了避免誤解,在一個程序裡面,最好選取一種格式一直保持下去。
4、指針的指針
指針變量也是一種對象,同樣可以給指針變量定義一個指向它的指針,就是指針的指針。定義語法如下:
指針的指針變量指向的對象類型 **指針的指針變量標識符;
Exp:
int iVar = 10 ; int *pInt = &iVar; int **ppInt = &pInt;
如上就定義了一個指向整型指針變量的指針變量ppInt; ppInt指向的對象的類型為 int* 類型的對象。
int main() { int iVar = 100; int *pInt = &iVar; int **ppInt = &pInt; cout <<"iVar ="<< iVar<<endl; cout <<"int *pInt = &iVar,then *pInt ="<<*pInt<<endl; cout <<"int **ppInt = &pInt,then *ppInt="<<*ppInt; cout <<";and then **ppInt="<<**ppInt<<endl; return 0; }
程序的執行結果如下所示:
[root@localhost cpp_src]# g++ test.cpp [root@localhost cpp_src]# ./a.out iVar =100 int *pInt = &iVar,then *pInt =100 int **ppInt = &pInt,then *ppInt=0xbfb949f8;and then **ppInt=100
5、通過指針訪問數組元素
這裡需要說明一個細節: 某一個數組的數組名是一個常量,而且數組名表示的是數組的第一個元素的首地址,同時數組元素在內存中是連續存放的。
正是因為數組具有上述的特點,才能方便的通過指針來訪問數組的元素。
通過指針訪問數組元素的例子如下:
int main() { int iArray[5] = {1,2,3,4,5}; int *pInt = iArray; cout << *pInt << endl; // 1 cout << pInt[0]<<endl; // 1 cout << *++pInt<<endl; // 2 cout << *pInt++<<endl; // 2 cout << *pInt<<endl ; // 3 return
}
程序的執行結果如下所示:
[root@localhost cpp_src]# ./a.out 1 1 2 2 3
不但可以通過++運算符來改變指針的指向,指針還支持加整數和減整數運算,同時支持兩個指針的減法運算。
int main() { int iArray[5] = {1,2,3,4,5}; int *pInt1 = iArray; int *pInt2= &iArray[4]; cout <<*(pInt1 + 2)<<endl; // 3 cout <<*(pInt2 - 1)<<endl; // 4 cout << pInt2 - pInt1 <<endl; return 0; }
程序的執行結果如下:
[root@localhost cpp_src]# g++ test.cpp [root@localhost cpp_src]# ./a.out 3 4 4
要點:
可以發現這個地方 pInt2 - pInt1 的結果是4, 這個結果與C語言的輸出是存在差別的。這一點要非常注意,在指針與數組結合使用的過程中,兩個指針相減
是經常見到的操作,因此這個地方需要注意。
通過上面的實例,我們可知利用指針可以很方便的訪問數組的元素,因此我們可以通過指針遍歷整個數組。
int main() { int iArray[5] = {1,2,3,4,5}; for(int *pBegin=iArray,*pEnd=iArray+5; pBegin != pEnd; ++pBegin) cout<<*pBegin<<endl; return 0; }
程序的執行結果如下所示:
[root@localhost cpp_src]# g++ test.cpp [root@localhost cpp_src]# ./a.out 1 2 3 4 5
指針和數組之間的定義還包括* 和 [] 符號同時在定義中出現的情況,
Exp:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <bitset> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::string; 10 using std::vector; 11 using std::bitset; 12 13 int main() 14 { 15 int iArray_1[5] = {1,2,3,4,5}; 16 int iArray_2[3] = {1}; 17 int *pInt1[5] ={iArray_1, iArray_2}; 18 int (*pInt2)[5] = iArray_1; //error 19 pInt2 = iArray_2; //error 20 21 22 return 0; 23 }
上面的代碼中, 我標出了兩處錯誤,錯誤的原因是, pInt2 是一個二維的指針,而iArray_1 和 iArray_2 都是int * 類型的指針, 如果將程序修改一下就可以
得到如下的結果。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <bitset> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::string; 10 using std::vector; 11 using std::bitset; 12 13 int main() 14 { 15 //int iArray_1[5] = {1,2,3,4,5}; 16 //int iArray_2[3]= {1}; 17 //int *pInt1[5] ={iArray_1, iArray_2}; 18 //int (*pInt2)[5] = iArray_1; //error 19 //pInt2 = iArray_2; //error 20 21 int iArray_1[5]={1,2,3,4,5}; 22 int iArray_2[3][5]={{1}}; 23 int iArray_3[5][3]={{2}}; 24 int (*pInt)[5] = iArray_2; 25 pInt=iArray_3; //error 26 27 28 return 0; 29 }
上面的代碼中,我們可以知道 25行的語法是錯誤的,錯誤的原因是二維數組的第二維的指針長度不一致。通過上面的例子我們可以知道,* 和 [] 在一起定義指針變量
的時候,需要注意 * 和 [] 符號的優先級,同事需要知道加上括號後,定義的時候[] 的維度的擴展。這個地方是C語言當中經常會使用的,而且是屬於較復雜的用法,因
此需要因此特別的重視。
6、 指針 和 const限定符/修飾符
指針和const的結合使用沒有太多的說頭,主要是注意const修飾的 *p 還是 p, 只要分清楚修飾對象的不同就很好理解。
int main() { int iVar1 = 10; int iVar2 = 20; const int *pInt1 = &iVar1; int const *pInt2 = &iVar1; int * const pInt3 = &iVar1; const int * const pInt4 = &iVar1; int const * const pInt5 = &iVar2; return 0; }
關於const限定符需要知道的就是上面的各個定義的意義,只要知道 const是修飾 *pInt 還是修飾pInt就可以准確的分辨各個定義的意義,具體可以關注我前面
給出的關於C語言趣事相關的鏈接文章。
這裡還有一個需要注意的地方,就是對於const對象如何定義指向其的指針,下面是一個例子:
int main() { const int iVar = 10; //int *pInt1 = &iVar; //error int const *pInt1 = &iVar; const int *pInt2 = &iVar; return 0; }
這裡要注意加了注釋部分錯誤的原因。這裡就不解釋了,這個與const對象與引用的關系是一樣的。
7、指針和typedef的使用
在C語言中進程會做這樣的預處理指令。
#define PINT int*
這樣定義宏以後,就可以通過這個宏來定義指針變量,如下所示:
#define PINT int* int iVar = 0; PINT pInt = &iVar;
這樣是可以通過的,但是這樣會存在一個漏洞,如果同時定義兩個指針變量的話,就會出現錯誤。
#define PINT int* int iVar1 = 0; int iVar2 = 0; PINT pInt1 = &iVar1, pInt2 = &iVar2;
很顯然上面的代碼存在漏洞, 第二個變量 pInt2 不是指針變量,而是一個整型的變量, 好在這樣的錯誤編譯器在編譯的時候會檢查出來,這裡需要引起注意。
我們可以利用typedef機制來規避上述的風險, typedef 的作用就是為數據類型取一個別名,尤其在數據類型比較長時是一個非常有效的機制, typedef的語法
如下:
typedef 數據類型 數據類型別名;
例如:
typedef int* PINT;
這就為 int* 這種類型定義了一個新的別名 PINT,在使用的時候PINT就表示 int*。
Exp:
typedef int* PINT; int iVar1 = 0; int iVar2 = 0; PINT pInt1 = &iVar1, pInt2 = &iVar2;
上面的代碼定義了兩個整型變量 iVar1、iVar2, 同時定義了兩個指針變量pInt1 和 pInt2;
要點:
通過上面兩個例子,就可以清楚 typedef和#define 之間的差別。
注意typedef是語句,因此後面必須有個分號結尾。 這個點是經常容易忘記的,好在編譯器一般可以檢測出這樣的錯誤。
typedef和指針的結合還有一個值得注意的地方,就是 typedef 、const和指針同時出現。
typedef int* PINT const PINT pInt; //error
這裡定義的指針對象pInt是const指針對象, 這個指針對象在定義的時候必須初始化。因此要注意上面的這個錯誤。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <bitset> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::string; 10 using std::vector; 11 using std::bitset; 12 13 int main() 14 { 15 typedef int* PINT; 16 const PINT pInt; 17 18 return 0; 19 }
程序編譯的結果如下所示:
[root@localhost cpp_src]# g++ test.cpp test.cpp: In function ‘int main()’: test.cpp:16: 錯誤:未初始化的常量 ‘pInt’
將程序改成下面的形式則正確:
#include <iostream> #include <string> #include <vector> #include <bitset> using std::cin; using std::cout; using std::endl; using std::string; using std::vector; using std::bitset; int main() { typedef int* PINT; //const PINT pInt; int iVar = 0; const PINT pInt = &iVar; //初始化const指針 return 0; }
當然還可以定義更加復雜的數據類型,這裡就不再進行描述,後面如果碰到會進行相關的描述。
指針的操作基本上就是這些,在C++語言中,大部分的人傾向於不使用指針, 但是指針確實是一種非常高效的機制,但是如果能把指針用好,則會對
程序的性能的提升具有很好的提高作用。
關於指針和引用暫時就說到這, 接下來將要對C語言風格和C++風格的字符串進行一番討論, 待續......
HACKER的話,C語言稍微學習下就好了。
網絡多接觸下,比如,網絡的拓撲結構,網絡安全,計算機操作系統,socket通信(c/c++裡面也有不少這方面的范例),還有就是windows命令行以及linux的命令,再者可以深造一下ASM(匯編),現在的話,php也是很不錯的,做HACKER還是要掌握一下的。
想深入學習c的話,推薦《C++ PRIMER》,數據結構的話,可以了解一下。我是常年開發c++應用的,所以比較懶了,數據結構和算法都直接用C++的STD庫了,自己也省事兒,當然你也能拿STD庫來參考學習。
指針還是很有用,也是要用心學習的。做的時間久了,可以用用boost庫,當然現在也可以看看,主要看那些前輩們怎麼實現一些復雜的功能的。
既然是電腦編程,那麼VC6.0還是不錯的。做幾個小游戲練練手。然後再理解一下《軟件工程》,面試個工作,然後就可以了。
單純的C語言一般應用到unix和單片機中的比較多,windows中感覺很少了,除非一些歷史遺留的傳統項目。
所以還是向C++或Java或。。。發展吧。