知识点:
由于数组和指针这部分知识比较深奥,本菜鸟决定将C和C++的指针部分的知识点全归到C语言部分来整理,所以这里就只总结一些题目。
部分题目:
-
习题4.2
下列数组的值是什么?
string sa[10];
int ia[10];
int main()
{
string sa2[10];
int ia2[10];
}
【解答】
sa 和sa2 为元素类型为string 的数组,自动调用string 类的默认构造函数将各元素初始化为空字符串;ia 为在函数体外定义的内置数组,各元素初始化为0;ia2 为在函数体内定义的内置数组,各元素未初始化,其值不确定。
-
习题4.4
如何初始化数组的一部分或全部元素?
【解答】
定义数组时可使用初始化列表(用花括号括住的一组以逗号分隔的元素初值)来初始化数组的部分或全部元素。如果是初始化全部元素,可以省略定义数组时方括号中给出的数组维数值。如果指定了数组维数,则初始化列表提供的元素个数不能超过维数值。如果数组维数大于列出的元素初值个数,则只初始化前面的数组元素,剩下的其他元素,若是内置类型则初始化为0,若是类类型则调用该类的默认构造函数进行初始化。字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化,也可以用一个字符串字面值进行初始化。
-
习题4.5
列出使用数组而不是vector 的缺点。
【解答】
与vector 类型相比,数组具有如下缺点:数组的长度是固定的,而且数组不提供获取其容量大小的size 操作,也不提供自动添加元素的push_back 操作。因此,程序员无法在程序运行时知道一个给定数组的长度,而且如果需要更改数组的长度,程序员只能创建一个更大的新数组,然后把原数组的所有元素复制到新数组的存储空间中去。与使用vector 类型的程序相比,使用内置数组的程序更容易出错且难以调试。
-
习题4.10
下面提供了两种指针声明的形式,解释宁愿使用第一种形式的原因:
int *ip; // good practice
int* ip; // legal but misleading
【解答】
第一种形式强调了ip 是一个指针,这种形式在阅读时不易引起误解,尤其是当一个语句中同时定义了多个变量时。
-
习题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.16
下列程序段实现什么功能?
int i = 42, j = 1024;
int *p1 = &i, *p2 = &j;
*p2 = *p1 * * p2;
*p1 *= *p1;
【解答】
该程序段使得i 被赋值为42 的平方,j 被赋值为42 与1024 的乘积。
-
习题4.17
已知p1 和p2 指向同一个数组中的元素,下面语句实现什么功能?
p1 += p2 – p1;
当p1 和p2 具有什么值时这个语句是非法的?
【解答】
此语句使得p1 也指向p2 原来所指向的元素。原则上说,只要p1 和p2 的类型相同,则该语句始终是合法的。只有当p1 和p2 不是同类型指针时,该语句才不合法(不能进行-操作)。但是,如果p1 和p2 不是指向同一个数组中的元素,则这个语句的执行结果可能是错误的。因为-操作的结果类型ptrdiff_t 只能保证足以存放同一数组中两个指针之间的差距。如果p1 和p2 不是指向同一个数组中的元素,则-操作的结果有可能超出ptrdiff_t 类型的表示范围而产生溢出,从而该语句的执行结果不能保证p1 指向p2 原来所指向的元素(甚至不能保证p1 为有效指针)。
-
习题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 指针,但该指针没有初始化。
-
习题4.20
下列哪些初始化是合法的?为什么?
(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 更为安全。