用长度标记数组的最大问题不是存储该长度所需的空间,也不是如何存储长度的问题(对于短数组使用一个额外的字节通常不会令人反感,也不会使用四个长数组需要额外的字节,但即使是短数组也可能需要使用四个字节)。一个更大的问题是给定的代码如下:
void ClearTwoElements(int *ptr)
{
ptr[-2] = 0;
ptr[2] = 0;
}
void blah(void)
{
static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
ClearTwoElements(foo+2);
ClearTwoElements(foo+7);
ClearTwoElements(foo+1);
ClearTwoElements(foo+8);
}
代码能够接受第一个调用ClearTwoElements
而拒绝第二个调用的唯一方法是使该ClearTwoElements
方法接收足以知道每种情况下foo
除知道哪一部分之外还接收到对数组部分的引用的信息。通常,这将使传递指针参数的成本增加一倍。此外,如果每个数组前面都有一个指向末尾的地址的指针(最有效的验证格式),则优化后的代码ClearTwoElements
可能会变成:
void ClearTwoElements(int *ptr)
{
int* array_end = ARRAY_END(ptr);
if ((array_end - ARRAY_BASE(ptr)) < 10 ||
(ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||
(array_end - 4) < ADDRESS(ptr)))
trap();
*(ADDRESS(ptr) - 4) = 0;
*(ADDRESS(ptr) + 4) = 0;
}
注意,方法调用者通常可以完全合法地将指针传递给数组的开头,或者将最后一个元素传递给方法。仅当该方法尝试访问传入数组之外的元素时,此类指针才会引起任何麻烦。因此,被调用方法必须首先确保数组足够大,以使用于验证其参数的指针算术本身不会超出范围,然后进行一些指针计算以验证参数。这种验证所花费的时间可能会超过进行任何实际工作所花费的成本。此外,如果编写并调用该方法,则可能会更有效:
void ClearTwoElements(int arr[], int index)
{
arr[index-2] = 0;
arr[index+2] = 0;
}
void blah(void)
{
static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
ClearTwoElements(foo,2);
ClearTwoElements(foo,7);
ClearTwoElements(foo,1);
ClearTwoElements(foo,8);
}
一种类型的概念很不错,它结合了一些东西来识别一个对象和一个东西来识别一个对象。但是,如果不需要执行验证,则C样式的指针会更快。