从时间和空间的角度来讲,数组是访问内存中连续对象的最佳结构。然而,它同时也是非常底层的数据结构,不当地使用它常常会导致大量潜在的错误。而且,基本上在所有需要用到数组的地方,我们都有更好的替代品。我所说的“更好”是指更易于读写、不易导致错误,以及同等效率。
和数组如影随形的两个基本问题是:
- 数组不知道其自身的长度
- 稍有风吹草动,数组的名字就会转换成指向其首元素的指针
思考以下一些例子:
void f(int a[], int s)
{
// 处理 a;a 的长度是 s
for (int i = 0; i < s; ++i) a[i] = i;
}
int arr1[20];
int arr2[10];
void g()
{
f(arr1,20);
f(arr2,20);
}
第二个函数调用会玷污不属于 arr2 的内存。通常,程序员都不会传递错误的长度给函数 f,但传递参数是个额外的负担,而且不时都会有些人犯错(传递了错误的长度)。我更喜欢使用标准库里的 vector,这样写出来的程序更加简单明了:
void f(vector< int >& v)
{
// 处理 v
for (int i = 0; i < v.size(); ++i) v[i] = i;
}
vector< int > v1(20);
vector< int > v2(10);
void g()
{
f(v1);
f(v2);
}
因为数组不知道其自身的长度,所以不能直接进行数组赋值:
void f(int a[], int b[], int size)
{
a = b; // 并非数组赋值
memcpy(a,b,size); // a = b
// ...
}
同样,我更喜欢使用 vector:
void g(vector< int >& a, vector< int >& b, int size)
{
a = b;
// ...
}
vector 的另一个好处是,memcpy() 不能正确处理带有复制构造函数的元素,例如 string:
void f(string a[], string b[], int size)
{
a = b; // 并非数组赋值
memcpy(a,b,size); // 灾难
// ...
}
void g(vector< string >& a, vector< string >& b, int size)
{
a = b;
// ...
}
数组的大小在编译时就已固定:
const int S = 10;
void f(int s)
{
int a1[s]; // 错误
int a2[S]; // ok
// 若想增加 a2 的长度,必须改用 malloc() 从堆中分配数组空间,
// 然后使用 realloc() 改变分配到的空间的大小
// ...
}
作为对比:
const int S = 10;
void g(int s)
{
vector< int > v1(s); // ok
vector< int > v2(S); // ok
v2.resize(v2.size()*2);
// ...
}
C99 允许可变长的局部数组,但 VLA(变长数组,variable-length array)也有其独特的问题。
在 C 和 C++ 中,数组名“退化”为指针的方式是基本常识。然而,数组退化和继承“互动”时,是非常不妙的。例如:
class Base { void fct(); /* ... */ };
class Derived { /* ... */ };
void f(Base* p, int sz)
{
for (int i=0; i < sz; ++i) p[i].fct();
}
Base ab[20];
Derived ad[20];
void g()
{
f(ab,20);
f(ad,20); // 灾难!
}
在后一个函数调用里,Derived[] 被认为是 Base[],以至于当 sizeof(Derived)!=sizeof(Base) 时,余下的代码不再能正常工作。如果我们使用 vector 的话,在编译时就能捕捉到这个错误:
void f(vector< Base >& v)
{
for (int i=0; i < v.size(); ++i) v[i].fct();
}
vector< Base > ab(20);
vector< Derived > ad(20);
void g()
{
f(ab);
f(ad); // 错误:不能将 vector< Derived > 转换成 vector< Base >
}
我发现大量 C 和 C++ 初学者的程序错误都和(错误地)使用数组有关。
原文地址:http://www.research.att.com/~bs/bs_faq2.html#arrays
本文版权归 蚂蚁的 C/C++ 标准编程 以及 作者 Bjarne Stroustrup 翻译:antigloss 共同所有,转载请注明原作者和出处。谢谢。