第四章數組和指針
Arrays and Points
vector和array類似的地方就是它們都是同一對象類型的集合。不同點是array是固定長度的。iterator之于vector就如同指針之于array。
使用指針的一個重要原因是vector不能提供所需的訪問速度。
數組類型不能是引用(reference)
4.1 Array
關于Array初始化:
l 使用class默認的構造函數初始化每個單元。
l Character Array即可以用字符數組來初始化也可以用string來初始化,注意如果用string,那么自動在末尾追加了null作為結束符,這樣數組的長度+1.
下標操作時,要使用數據類型是:size_t,就像在vector中使用size_type。
for (size_t ix = 0; ix != array_size; ++ix)
ia[ix] = ix;
|
4.2 Pointers
什么是指針
Pointers are iterators for arrays.指針是用于數組的迭代器。
pointer holds the address of another object:。具體來說,指針保存的是另一個對象的地址。
string *sp = &s; // sp holds the address of s
|
*sp里面*代表sp是一個指針。
Best practice:指針初始化時,如果不能指向具體的地址,就設置為0,這樣在程序中就可以檢測出指針沒有指向一個對象(object)。
指針初始化和賦值操作的約束
指針初始化和賦值
只有4類值可以初始化指針和指針賦值:
1.值為0的常量表達式。不能把任何的int賦值給指針,即使這個int的值是0,但是可以把值是0的const 或者數值0賦值給指針。NULL定義為0,所以也可以使用。NULL叫做預處理變量。
2.類型匹配的對象的地址
3.另一對象末的下一地址
4.同類型的其它有效的指針
void*指針,這是一個特殊類型的指針,它能夠是任何對象的地址。它就是說明它的值是一個指針,但是指針所指向的對象的類型是不可知的。
因此void*指針只能執行通用的操作:
l 指針比較
l 傳遞給function或者作為function的返回值。
l 賦值給另一個void*指針
Operations on Pointer(指針操作)
注意*在這里是一個operator。dereference a pointer。解引用操作(dereference operator)返回指定對象的左值lvalue。因此可以進行賦值操作。
引用和指針的區別:
1. 引用總是指向對象object。引用必須初始化,但指針是可以僅僅定義,而無需初始化的。
2. 賦值:引用的賦值會改變引用所綁定的對象的值;引用是不能重新綁定新的對象的。一旦初始化,引用總是指向同一個對象。
引用:
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival這里ri和ri2還是指向2個不同的地址單元
|
指針:
int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2; // pi now points to ival2,pi和pi2都指向了同一個地址單元
|
指向指針的指針**ppi,代碼:
int ival=1024;
int *pi = &ival;
int **ppi=π
int *pi2 =*ppi; //或者 int *pi2 = pi;
|
對ppi解引用2次,就可以得到ival的值了。
1. 使用指針訪問數組單元
當使用數組的名字時,這個名字就自動轉換為指向數組第一個單元的指針。
int ia[] = {0,2,4,6,8};
int *ip = ia; // ip points to ia[0] 定義ip是指針,賦值為ia
|
指針的算數運算會產生一個新指針。
兩指針相減得到的數據類型是ptrdiff_t。它是一個
int last = *(ia + 4); // ok: initializes last to 8, the value of ia[4]
|
如何獲得一個數組的結尾?
const size_t arr_size = 5;
int arr[arr_size] = {1,2,3,4,5};
int *p = arr; // ok: p points to arr[0]
int *p2 = p + arr_size; // ok: p2 points one past the end of arr
|
p2就是指向了數組的結尾。
指向const對象的指針和const指針
1. 指向const對象的指針
如果指針指向的是一個const對象,那么肯定不希望它能夠修改這個const對象的值。那么這樣的指針如何定義?
const double *cptr; //cptr是指向const double類型的指針
|
對于這個定義的理解是這樣子的:
A. 這里的限定詞(qualifier)-const是限定cptr指向的數據類型必須是const double,而不是限定cptr本身。
B. cptr本身不是const
C. 不能做的是用cptr去修改它指向的對象的值
但是也可以把指向const對象的指針賦值為非const對象的地址。(A pointer to a const object can be assigned the address of a nonconst object)
int val=10;
const int *cptr = &val;
val=30; //這樣寫是對的
*cptr = 30; //這樣寫會死人的L
|
不過我想,這只不過是代碼上的文字游戲而已,如果程序這樣寫,也許會死人的。混亂!
這種指針的用途是作為方法的參數,這可以保證這個參數不會在方法中被修改。(哎,要不怎么說是大師呢,佩服!)
2. const指針
const指針的值是不可以修改的,但是const指針指向的對象的值是可以修改的。
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer
*curErr = 0; // ok: reset value of the object to which curErr is bound
|
3. 指針和typedef
復習:
typedef 可以用來定義類型的同義詞。wages就是double的同義詞
typedef double wages; // wages is a synonym for double
|
(以下的這段很饒人啊)
typedef string *pstring; //pstring是string的同義詞;并且pstring是指針類型
const pstring cstr; //這是指向string的const指針,而不是指向const string的指針
|
第一繞:pstring類型是指向string類型的指針類型。
第二繞:const是用來修飾pstring類型的,const pstring就是const的string指針
第三繞:這不存在簡單的文字替換的游戲:const pstring cstr;等于const string* cstr;所以就是指向const string的指針。錯誤!
4.3 C風格字符串
不建議在C++中使用C風格的字符串,原因是它有“many many”的安全問題。
1. 什么是C風格的字符串?
它不是一種類型,而是以空字符 null 結束的字符數組。
定義:
char ca2[] = {'C', '+', '+', '"0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically
const char *cp = "C++"; // null terminator added automatically
char *cp2 = ca2; // points to first element of a null-terminated char array
|
遍歷字符串
const char *cp = "some value";
while (*cp) {
// do something to *cp
++cp;
}
|
2. 字符串函數
C標準庫提供了一系列的字符串函數,但是這些函數都不檢查參數的合法性(限制條件)。這就是Lippman不建議在C++中使用C風格的字符串的原因。
主要的限制:
1. 傳遞給這些標準庫函數的指針必須是非空的。
2. 每個指針必須是以null結尾的。
3. 數組的長度要足夠大
strlen(s),返回的是s的長度,但是這里并不包含null,因此數組的實際長度是strlen(s)+1。
3. 使用動態數組
為什么要使用動態數組?
通常是因為在編譯時無法知道數組的維數,所以才需要動態創建該數組。
size_t n = get_size(); // get_size returns number of elements needed
int* p = new int[n];
|
如果是普通的定義數組,以上的定義是根本無法實現的,因為在編譯期是不能確定數組的大小的。只能這樣定義:
因此就可以得出結論:動態數組主要是用于當數組的尺寸不能在編譯期確定的情況。
數組釋放:
4. 疑問代碼(P139):
我認為在const size_t len = strlen(pc +1); 這行存在筆誤,應該是
const size_t len = strlen(pc )+1;
否則copy后得到的結果pc2不是C風格的字符串,因為最后一個單元的內容是’g’而不是null。
const char *pc = "a very long literal string";
const size_t len = strlen(pc +1); // space to
// performance test on string allocation and copy
for (size_t ix = 0; ix != 1; ++ix) {
char *pc2 = new char[len + 1]; // allocate the space
strcpy(pc2, pc); // do the copy
cout << "pc2=" << pc2 << endl;
cout << "pc2[25]=" << (pc2+25) << endl;
delete [] pc2; // free the memory
}
|
4.4多維數組 Multidimensioned Arrays
C++實際是沒有多維數組的,
int ia[3][4]
可以理解為數字大小是3,每個單元element又是一個大小是4的int數組。或者叫做3行3列。
1. 指針和多維數組
這段代碼就如同“回”字的N多種寫法一樣:
int ia[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
//*ip是一個指針變量,它指向的是長度是4的int數組。
int (*ip)[4]=ia;
//*ipo是一個指針數組
int *ipo[4] ;
ipo[2]=ia[2]; //對指針數組的單元賦值
//*ipt就是一個最普通的指針,對指針變量賦值
int *ipt=ia[2];
//
int (*ipn)[4]=ia;
ipn=&ia[2]; //也可以寫為ia+2
cout << "(*(ip+2))[0]=" << (*(ip+2))[0] << endl;
cout << "ipo[2][0]=" << ipo[2][0] << endl;
cout << "ipt[0]=" << ipt[0] << endl;
cout << "(*ipn)[0]=" << (*ipn)[0] << endl;
|
打印結果是一樣的,都是‘8’。
2. 以下2種定義的區別(圓括號是必不可少的)
指針數組:
int *ip[4]; // array of pointers to int
|
如果從內向外閱讀 ip 的聲明,則可理解為:*ip 是 int[4] 類型——即 ip 是一個指向含有 4 個元素的數組的指針。
int (*ip)[4]; // pointer to an array of 4 ints
|
進一步的理解:
int (*ip)[4]=ia;
ip=&ia[2]; //也可以寫為ia+2
|
剛開始,我覺得應該寫成ip=ia[2];結果得到報錯信息:
error: cannot convert `int[4]' to `int (*)[4]' in assignment
|
因為ia[2]就是一個數組啊,再想如果這樣寫ip=ia[2];這就相當于
int ia[4]= {0,1,2,3};
int *ip=ia;
ip=ia[2];
|
當然是錯誤的。