假設get_size 是一個沒有參數並返回int 值的函數,下列哪些定義是非法的? 爲什麼?
unsigned buf_size = 1024
(a) int ia[buf_size]; (b) int ia[get_size()]; (c) int ia[4*7-14]; (d) char st[11] = "fundamental"
【解答】
(a)非法,buf_size 是一個變量,不能用於定義數組的維數(維長度)。(b)非法,get_size()是函數調用,不是常量表達式,不能用於定義數組的維數 (維長度)。
(d)非法,存放字符串"fundamental"的數組必須有12 個元素,st 只有11 個元 素。
習題4.2
列數組的值是什麼?
string sa[10];
int ia[10]; int main(){ string sa2[10]; int ia2[10]; }
【解答】
sa 和sa2 爲元素類型爲string 的數組,自動調用string 類的默認構造函數將
各元素初始化爲空字符串;ia 爲在函數體外定義的內置數組,各元素初始化爲
0;ia2 爲在函數體內定義的內置數組,各元素未初始化,其值不確定。
習題4.3
下列哪些定義是錯誤的?
(a) int ia[7] = {0, 1, 1, 2, 3, 5, 8};
(b) vector<int> ivec = {0, 1, 1, 2, 3, 5, 8}; (c) int ia2[] = ia; (d) int ia3[] = ivec;
【解答】
(b)錯誤。vector 對象不能用這種方式進行初始化。 (c)錯誤。不能用一個數組來初始化另一個數組。 (d)錯誤。不能用vector 對象來初始化數組。
習題4.4
如何初始化數組的一部分或全部元素?
【解答】
定義數組時可使用初始化列表(用花括號括住的一組以逗號分隔的元素初
值)來初始化數組的部分或全部元素。如果是初始化全部元素,可以省略定義
數組時方括號中給出的數組維數值。如果指定了數組維數,則初始化列表提供
的元素個數不能超過維數值。如果數組維數大於列出的元素初值個數,則只初
始化前面的數組元素,剩下的其他元素,若是內置類型則初始化爲0,若是類類
型則調用該類的默認構造函數進行初始化。字符數組既可以用一組由花括號括
起來、逗號隔開的字符字面值進行初始化,也可以用一個字符串字面值進行初 始化。
習題4.5
列出使用數組而不是vector 的缺點。
【解答】
與vector 類型相比,數組具有如下缺點:數組的長度是固定的,而且數組
不提供獲取其容量大小的size 操作,也不提供自動添加元素的push_back 操作。
因此,程序員無法在程序運行時知道一個給定數組的長度,而且如果需要更改
數組的長度,程序員只能創建一個更大的新數組,然後把原數組的所有元素復
制到新數組的存儲空間中去。與使用vector 類型的程序相比,使用內置數組的 程序更容易出錯且難以調試。
習題4.6
下面的程序段企圖將下標值賦給數組的每個元素,其中在下標操作上有一些錯 誤,請指出這些錯誤。
const size_t array_size = 10 int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix) ia[ix] = ix
【解答】
該程序段的錯誤是:數組下標使用越界。
根據數組ia 的定義,該數組的下標值應該是0~9(即array_size-1),而不是從
1 到array_size,因此其中的for 語句出錯,可更正如下: for (size_t ix = 0; ix < array_size; ++ix) ia[ix] = ix
習題4.10
下面提供了兩種指針聲明的形式,解釋寧願使用第一種形式的原因:
int *ip; // good practice
int* ip; // legal but misleading
【解答】
第一種形式強調了ip 是一個指針,這種形式在閱讀時不易引起誤解,尤其
是當一個語句中同時定義了多個變量時
題4.11
解釋下列聲明語句,並指出哪些是非法的,爲什麼?
(a) int* ip;
(b) string s, *sp = 0; (c) int i; double* dp = &i; (d) int* ip, ip2;
(e) const int i = 0, *p = i; (f) string *p = NULL; 【解答】
(a)合法。定義了一個指向int 型對象的指針ip。
(b)合法。定義了string 對象s 和指向string 型對象的指針sp,sp 初始化爲0 值。
(c)非法。dp 爲指向double 型對象的指針,不能用int 型對象i 的地址進行初 始化。
(d)合法。定義了int 對象ip2 和指向int 型對象的指針ip。 (e)合法。定義了const int 型對象i 和指向const int 型對象的指針p,i 初
始化爲0,p 初始化爲0。
(f)合法。定義了指向string 型對象的指針p,並將其初始化爲0 值。
習題4.12
已知一指針p,你可以確定該指針是否指向一個有效的對象嗎?如果可以,如何
確定?如果不可以,請說明原因。
【解答】
無法確定某指針是否指向一個有效對象。因爲,在C++語言中,無法檢測指
針是否未被初始化,也無法區分一個地址是有效地址,還是由指針所分配的存
儲空間中存放的不確定值的二進制位形成的地址。
習題4.13
下列代碼中,爲什麼第一個指針的初始化是合法的,而第二個則不合法? int i = 42; void *p = &i; long *lp = &i;
【解答】
具有void*類型的指針可以保存任意類型對象的地址,因此p 的初始化是合
法的;而指向long 型對象的指針不能用int 型對象的地址來初始化,因此lp 的初始化不合法。
習題4.15
解釋指針和引用的主要區別。
【解答】
使用引用(reference)和指針(pointer)都可間接訪問另一個值,但它們之
間存在兩個重要區別:(1)引用總是指向某個確定對象(事實上,引用就是該對
象的別名),定義引用時沒有進行初始化會出現編譯錯誤;(2) 賦值行爲上存
在差異:給引用賦值修改的是該引用所關聯的對象的值,而不是使該引用與另
一個對象關聯。引用一經初始化,就始終指向同一個特定對象。給指針賦值修
改的是指針對象本身,也就是使該指針指向另一對象,指針在不同時刻可指向
不同的對象(只要保證類型匹配)。
習題4.19
解釋下列5 個定義的含義,指出其中哪些定義是非法的:
(a) int i; (b) const int ic; (c) const int *pic; (d) int *const cpi; (e) const int *const cpic;
【解答】
(a) 合法:定義了int 型對象i。
(b) 非法:定義const 對象時必須進行初始化,但ic 沒有初始化。
(c) 合法:定義了指向int 型const 對象的指針pic。 (d) 非法:因爲cpi 被定義爲指向int 型對象的const 指針,但該指針沒有初 始化。
(e) 非法:因爲cpic 被定義爲指向int 型const 對象的const 指針,但該指針 沒有初始化。
列哪些初始化是合法的?爲什麼?
(a) int i = -1; (b) const int ic = i (c) const int *pic = ⁣ (d) int *const cpi = ⁣ (e) const int *const cpic = ⁣
【解答】
(a) 合法:定義了一個int 型對象i,並用int 型字面值-1 對其進行初始化。
(b) 合法:定義了一個int 型const 對象ic,並用int 型對象對其進行初始 化。
(c) 合法:定義了一個指向int 型const 對象的指針pic,並用ic 的地址對 其進行初始化。
(d) 不合法:cpi 是一個指向int 型對象的const 指針,不能用const int
型對象ic 的地址對其進行初始化。
(e) 合法:定義了一個指向int 型const 對象的const 指針cpic,並用ic 的地址對其進行初始化。
題4.22
解釋下列兩個while 循環的差別:
const char *cp = "hello"; int cnt;
while (cp) { ++cnt; ++cp; } while (*cp) { ++cnt; ++cp; }
【解答】
兩個while 循環的差別爲:前者的循環結束條件是cp 爲0 值(即指針cp 爲0
值);後者的循環結束條件是cp 所指向的字符爲0 值(即cp 所指向的字符爲
字符串結束符null(即'\0'))。因此後者能正確地計算出字符串"hello"中有
效字符的數目(放在cnt 中),而前者的執行是不確定的。 注意,題目中的代碼還有一個小問題,即cnt 沒有初始化爲0 值。
習題4.24
解釋strcpy 和strncpy 的差別在哪裏,各自的優缺點是什麼?
【解答】
strcpy 和strncpy 的差別在於:前者複製整個指定的字符串,後者只複製指定 字符串中指定數目的字符。
strcpy 比較簡單,而使用strncpy 可以適當地控制複製字符的數目,因此比 strcpy 更爲安全。