《C++primer》讀書筆記---數組和指針

數組

1.1 數組

1.1.1數組的初始化和定義
數組的維數必須是大於等於1的常量表達式定義。此常量表達式只能用整型字面值常量、枚舉常量或者用常量表達式初始化的整型const對象。非const變量以及就要到運行階段才能確定其值的const變量都不能用於定義數組的維數。
在函數體外定義的內置數組,其元素初始化爲0;
在函數體內定義的內置數組,其元素無初始化;
如果爲類類型則自動調用構造函數初始化,如果無構造函數則必須顯示的初始化。

特殊字符初始化:
char ca1[] = {'C', '+', '+'}; // no null 正常初始化後結尾不會自動加'\0'空字符
char ca2[] = {'C', '+', '+', '\0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically使用字符串字面值初始化後自動+‘\0’,此時size爲4‘;

不能對數組對象進行直接複製或者賦值
int ia2[](ia); // error: cannot initialize one array with another
ia3 = ia; // error: cannot assign one array to another

1.1.2數組操作
數組在遍歷和賦值時可以使用下標操作訪問,下標的正確類型爲size_t

1.2.1指針的初始化和賦值操作的約束
對指針進行初始化或者賦值可以使用如下:
1>0值常量表達式    例如,在編譯時可獲得0值的整型const對象或者字面值常量0
2>類型匹配的對象的地址
3>另一個對象之後的下一個地址
4>同一類型另一個有效的指針
int ival;
int zero = 0;
const int c_ival = 0;
int *pi = ival; // error: pi initialized from int value of ival
pi = zero; // error: pi assigned int value of zero
pi = c_ival; // ok: c_ival is a const with compile-time value of 0
pi = 0; // ok: directly initialize to literal constant 0
把int變量賦值給指針是非法的,儘管int型變量值可能爲0:
int *pi = NULL; // ok: equivalent to int *pi = 0;

1.2.2void*指針
void*指針可以保存任何類型對象的地址:
double d1 = 3.14;
double *d = &d1;
void *v = d;
void*表明改制真與已低至相關,但不清楚此地址上的對象的類型,不可以使用void*指針操作它指向的對象。

1.2.3指針操作
指針提供簡介操作器所指對象的功能,類似於對迭代器進行解引用 操作一樣,對指針解引用可以訪問他所指向的對象。

1.2.4指針和引用的比較
區別:
1>引用總是指向某個對象:定義引用時沒有初始化時錯誤的。
2>給引用賦值修改的是該引用所關聯的對象的值,而並不是是引用於另一個對相關聯。
3>引用一經初始化就始終指向同一個特定對象。
例如:
int ival = 1024, ival2 = 2048;
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival
ri = ri2使得ival的值等於ival2的值2048;

1.2.5指針和數組
類似對數組的下標訪問操作,通過指針的算術操作也可以達到同樣的效果(此時指針類似vector裏的迭代器)。
int ia[] = {0,2,4,6,8};
int *ip = ia; // ip points to ia[0]
ip = &ia[4]; // ip points to last element in ia
int *ip2 = ip + 4; // ok: ip2 points to ia[4], the last element in ia
通常在數組指針上加上(或減去)一個整型數值n,等效於獲得一個新指針,該指針指向原指針指向元素之後(或之前)的第n個元素。(注意:不要越界)

只要兩個指針指向同一個數組或者有一個指向該數組末端的下一個單元,C++支持對這兩個指針做減法操作:
ptrdiff_t n = ip2 - ip;
結果是4,兩個指針指向元素的間隔。結果是標準庫類型ptrdiff_t類似size_t但ptrdiff_t是signed,因爲連個指針間的差距可能是負數。
 
在一個指針上加一個整數仍然是一個指針,並且支持直接解引用:int last = *(ia + 4);等價於ia[4];


下標和指針:
使用下標訪問數組,實際是使用下標訪問指針
int ia[] = {0, 2, 4, 6, 8};
int i = a[0];
int *p = &ia[2];
int j = p[1]; // j = ia[3];  相當於*(p + 1);
int k = p[-2]; // k = ia[0]; 相當於*(p -  2);

計算數組超出末端指針
nt iarr[] = {0, 2, 4, 6, 8};
int *p = arr;
int *p2 = p + 4; //允許計算,但不許解引用和賦值。
超出末端指針常用於數組的遍歷,判斷是否已到數組尾:
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };

for (int *pbegin = int_arr, *pend = int_arr + arr_sz; pbegin != pend; ++pbegin)
cout << *pbegin << ' '; // print the current element
類似標準庫容器的迭代器,指針與數組一起使用時,指針其實是數組的迭代器

1.2 指針和const限定符

1.2.1 指向const對象的指針
const double pi = 3.14;  (可以不進行初始化)
double pi2 = 3.14;
const double *cptr;
double *cptr2;
特性:
1>不可以使用指向const對象的指針對對象進行修改; 例如:*cptr = 3.142;  //error !
2>C++強制要求指向const對象的指針必須具有const特性; 例如:constdouble *cptr;
3>指向const對象的指針本身不是const,可以賦值; 例如:cptr = cptr2;
4>const對象不能賦值給普通或者非const對象的指針 例如:double *cptr3 = &pi;   //error !
5>不能使用void *保存const對象地址,必須使用const void*
6>允許把非const對象地址保存到const對象的指針 例如:const double *cptr = & pi2;  
     此時不能通過該指針對pi2的指針進行修改,但可以通過普通指針修改   例如: double *cptr3 = &pi2;  *cptr3  = 3.1432;
指向const對象的指針,所指對象不一定爲cosnt對象(因爲可以指向普通非const對象),可以理解爲“自以爲指向cosnt的指針
實際使用中,指向const的指針常用作函數的形參,以確保傳遞給函數的實際對象在函數中不被修改;

1.2.2 const指針
const指針---本身的值不能修改:
int errNum = 0;
int *const curerr = &errNumb;
curerr = curerr; //即使是賦值本身也不行
const指針所指向的對象能否修改取決於對象本身,如果對象本身是const則不能修改,否則可以通過*curerr = ···修改;

1.2.3指針和typedef
typedef string *pstring;
const pstring cstr;     //等同於 string *const cstr
const double db == double const db; // const修飾符可以放在類型的前面或者後面,作用都是使其修飾的變量成爲const;
typedef string *pstring  使得*string 成爲一個類型(指針類型的string --> pstring),此時const 位於pstring前或者後均可,都表示聲明一個const (string*)類型變量。

2 C風格字符串 --- 以NULL結束的字符數組:
char ca1[ ] = {'c', '+', '+'};  //沒有使用NULL字符結尾,並非C風格字符串
char ca2[ ] = {‘c’,  '+', '+', '\0'};
char ca3[ ] = "c++"; //自帶NULL字符結尾
const char *cp = “c++”; //自帶NULL字符結尾
char *cp1 = cp1; //指向數組首元素,但不是C-Style字符串
char *cp2 = ca2; //指向數組首元素,以NULL字符結尾

2.1C風格字符串的標準庫函數

strlen(s) Returns the length of s, not counting the null.
strcmp(s1, s2) Compares s1 and s2 for equality. Returns 0 if s1 == s2,
positive value if s1 > s2, negative value if s1 < s2.
strcat(s1, s2) Appends s2 to s1. Returns s1.
strcpy(s1, s2) Copies s2 into s1. Returns s1.
strncat(s1, s2,n) Appends n characters from s2 onto s1. Returns s1.
strncpy(s1, s2, n) Copies n characters from s2 into s1. Returns s1.

C++提供關係操作符對C風格字符串進行比較,但意義不同:
if (cp1 < cp2) // compares addresses, not the values pointed to

C風格標準庫函數僅適用於以null結尾的C_Style字符串,亂用會出錯:
char ca[] = {'C', '+', '+'}; // not null-terminated
cout << strlen(ca) << endl; // disaster: ca isn't null-terminated


使用strn函數處理C風格字符串相對更安全,但儘可能使用C++標準庫類型string

解釋下列兩個

while 

循環的差別:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解釋下列兩個

while 

循環的差別:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解釋下列兩個

while 

循環的差別:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

解釋下列兩個

while 

循環的差別:

 

const char *cp = "hello"; 

int cnt; 

while (cp) { ++cnt; ++cp; } 

while (*cp) { ++cnt; ++cp; }

const char *cp = "hello";
int cnt;
while (cp) {++cnt, ++cp};		//結束條件爲cp指針爲 0 ;死循環
while(*cp) {++cnt, ++cp};		//結束條件爲cp所指向的值爲0,即末尾\0,可以正確計算出字符串長度。

2.2創建動態數組
初始化:
動態分配數組時,如果元素具有類類型,將使用該類的默認構造函數實現初始化,如果數組元素師內置類型則無初始化。
string *psa = new string[10]; // array of 10 empty strings
int *pia = new int[10]; // array of 10 uninitialized ints
可以使用數組長度後面的一對空圓括號對數組元素做值初始化,不能像數組那樣使用初始化列表爲數組提供各不相同的初始值,只能將元素初始化爲元素類型的默認值
int *pia2 = new int[10]()   //把十個int初始化爲0;
const 對象的動態數組
在堆中創建動態數組就必須對成員初始化,內置類型必須在後邊帶“()”,類類型必須提供默構造函數。已創建的const常量元素不允許修改,因此const動態數組無用處。
	const int *pci_bad = new const int[100];		// error: uninitialized const array
	const int *pci_ok = new const int[100]();		// ok: value-initialized const array
	const string *pcs = new const string[100];	<span style="white-space:pre">	</span>// ok: array of 100 empty strings

允許動態分配空數組
在不知道數組長度的時候,我們編寫如下代碼:
size_t n = get_size(); // get_size returns number of elements needed
int* p = new int[n];
for (int* q = p; q != p + n; ++q)
;
如果get_size()返回 0 ,代碼將仍然執行,C++語序使用new動態創建長度爲0的數組,new返回有效的非零指針,但不能對其解引用。

動態數組空間的釋放

delete [ ]  pia;

2.3 混合使用標準庫類string和C風格字符串

可以使用字符串字面值初始化string對象;
可以使用C風格字符串對string對象初始化或者賦值;
string類型加操作可以使其中一個操作數爲C風格字符串,也可將C風格字符串用作複合賦值操作的右操作數;
反之則不成立:

string提供一個名爲c_str的函數:
const char *str = st2.c_str();
c_str函數返回C風格字符串,即:指向字符數組首地址的指針,該數組廚房了與string對象相同的內容,並且以null結束。
c_str返回的數組並不保證一定有效,接下來如果對st2操作可能會改變st2的值,使返回的數組失效。如果程序需要持續訪問該數據,則應該賦值c_str返回的數組
	string st2 = "hello";
	const char *str = st2.c_str();
	cout<<str<<endl;<span style="white-space:pre">	</span>//輸出結果hello
	st2 = "world";
	cout<<str<<endl;<span style="white-space:pre">	</span>//輸出結果world
參考:http://blog.csdn.net/baizhengbiao/article/details/7341666

2.4 用數組初始化vector對象
使用數組初始化vector必須指出用於初始化式的第一個第一個元素及數組最後一個元素的下一個位置的地址:
const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};  <pre name="code" class="cpp">// ivec has 6 elements: each a copy of the corresponding element in int_arr<span style="font-family: Arial, Helvetica, sans-serif;">		</span><span style="font-family: Arial, Helvetica, sans-serif;">				</span>
vector<int> ivec(int_arr, int_arr + arr_size);

傳遞給ivec的兩個指針標出了vector初值的範圍,第二個指針指向被複制的最後一個元素之後的地址空間。被標出的元素範圍可以使數組的子集:
vector< int >(int_arr + 1, int_arr + 4 );//複製3個元素
2.5 多維數組
C++中無多維數組,通常所指爲數組的數組:  int ia[3][4]  三個含有4個元素的int 數組
多維數組的初始化:
<pre name="code" class="cpp">int ia[3][4] = { 	/* 3 elements, each element is an array of size 4 */
{0, 1, 2, 3} , 		/* initializers for row indexed by 0 */
{4, 5, 6, 7} , 		/* initializers for row indexed by 1 */
{8, 9, 10, 11} 		/* initializers for row indexed by 2 */
};
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};		//以上兩種方法等效
int ia[3][4] = {{ 0 } , { 4 } , { 8 } };			//只初始化每行的第一個元素
int ia[3][4] = {0, 3, 6, 9};				//初始化第0行的元素,其他未初始化


多維數組訪問時需要提供行下標和列下標,如果只提供一個下標如ia[2],將只獲得ia數組最後一行,即內層數組本身,並非數組中任意一元素。

多爲數組是數組的數組,所以由多爲數組轉換成的指針類型應該是指向第一個內層數組的指針:
int  ia[3][4]; // array of size 3, each element is an array of ints of size 4
int (*ip)[4] = ia;  //ip points to an array of 4 ints;
ip  = &ia[2];        //ia[2] is an array of 4 ints;
定義之像數組的指針與如何定義數組本身類似:首先聲明元素類型,後接(數組)變量名字和維數。*p可以理解爲:int[4]4類型 --- 即ip是一個指向有4個元素的數組的指針。
圓括號必不可少:
int *ip[4]; // array of pointers to int<span style="white-space:pre">		</span>指針的數組
int (*ip)[4]; // pointer to an array of 4 ints<span style="white-space:pre">	</span>數組的指針
使用typedef可以簡化指向多爲數組的指針
typedef int int _array[4];
int_array *ip = ia;
使用typedef類型輸出ia的元素:
for(int_array *p = ia; p != ia + 3;++p)
{
for(int *q = *p; q != *p + 4; ++q)
{
cout<<*q<<endl;
}
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章