CppCoreGuidelines I.13 不要以单独指针传递数组
26 March 2022
I.13: Do not pass an array as a single pointer
理由
接口的参数如果是用数组指针+数组大小的方式传递,很容易出错。我们只能依靠其他途径获取指针所指的数组的大小。
例子
void copy_n(const T* p, T* q, int n); // copy from [p:p+n) to [q:q+n)
如果 q
的元素数少于 n
会怎么样?我们会覆盖一些无关的内存。如果 p
的长度小于 n
会怎么样?我们可能复制了一些无关的内存。任何一种情况,都导致了未定义的行为,很可能是很难对付的一种 bug。
void my_copy_n(const char* p, char* q, int n) { for (; n>0 ; n--) { *q = *p; p++; q++; } } int main() { char pp[] = "hello"; char qq[] = "hel"; cout << qq << endl; my_copy_n(pp, qq, 5); cout << qq << endl; char pp1[] = "hel"; char qq1[] = "hello"; cout << qq1 << endl; my_copy_n(pp1, qq1, 5); cout << qq1; return 0; }
hel helloello hello hel
替代方案
考虑显式地用 span
作为参数。
void copy(span<const T> r, span<T> r2); // copy r to r2
例子
void draw(Shape* p, int n); // poor interface; poor code Circle arr[10]; // ... draw(arr, 10);
给参数 n
的值如果是 10 可能会出错: C 语言里数组下标的不成文的习俗是
[0:n) 即包括 0 ,不包括 n。更坏的情况是,调用draw()
函数在编译时没有问题,数组隐式的转换成了指针(数组衰减),另一个隐式转换: Circle
转成了 Shape
。 draw()
函数无法逐个访问数组的元素,因为无从知道元素本身的大小( Shape
和 Circle
大小不一)。
替代方案
使用提供支持作用的类,确保元素数量可知,避免危险的隐式转换。
例子
void draw2(span<Circle>); Circle arr[10]; // ... draw2(span<Circle>(arr)); // deduce the number of elements draw2(arr); // deduce the element type and array size void draw3(span<Shape>); draw3(arr); // error: cannot convert Circle[10] to span<Shape>
draw2()
得到与draw()
同样数量的信息。但是不会再隐式转换 Circle
类型。
例外
使用 zstring
和 czstring
表示 C 风格的\0
结尾的字符串。这时候,要用
GSL 的std::string_view
或 span<char>
来避免错误的范围。
强化
- (简单)检查隐式的将数组类型转换成指针类型的地方,如果发现,发起警告。允许例外情况:
zstring
和czstring
指针。 - (简单)出现指针类型的数值计算的代码,发起警告。允许例外情况:
zstring
和czstring
指针。