参数声明中的静态数组索引:一个不错且鲜为人知的C语言特性

C语言的创造者们一定都热衷于让关键字的数目尽可能的少.今天将向你展示另一个可使用C99中static关键字地方.

也许你已经看到过在数组的参数声明中包括数组的长度:
void foo(int myArray[10]);
这样的函数仍可接收整数指针int *,但是长度[10]可以对阅读代码的人们可当作文档,传达着这相函数预期一个长度为10个整数的数组的信息.

除此之外,你还可以在括号之中加上static关键字[1]:
void bar(int myArray[static 10]);

这会告诉编译器:函数会假定传进来的实参至少有10个元素.(值得注意的是这就会把NULL排除在外!)
这样做有二个目的:
1.编译器在优化代码时能够用到这些信息[2]
2.当编译一个带有上述声明的bar时,如果传递下面三种实参[3]进去, 编译器能够警告调用者:
   a. 传递NULL
bar(NULL);
warning: null passed to a callee which requires a non-null argument [-Wnonnull]
    bar(NULL);
    ^   ~~~~

   b. 传递一个(比10)更小的数组
int a[9];
bar(a);
warning: array argument is too small; contains 9 elements, callee requires at least 10 [-Warray-bounds]
    bar(a);
    ^   ~


   c. 传递一个(比10)更大的数组

int b[11];
bar(b);

[no output]

这在当你确信函数能接收的数组的长度时很有用, 因为它既可以为阅读代码的人提供文档信息,又能让编译器帮忙捕获错误.
[1]. 也可以在括号中使用const关键字. 这会让指向myArray的指针变成指针常量.
[2].不确实编译器是否真的会优化代码, 因为寄希望于程序员们都留心编译警告信息是十分不靠谱的.
[3].使用的编译器: Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn).
原文地址:A nice, little known C feature: Static array indices in parameter declarations

看过这篇文章,但去查了C语言的参考手册<C A Reference Manual>,发现C99中在函数参数声明时,的确允许在数组的括号中加上限制符. 以下译自<C A Reference Manual> Chap 9, 9.3 FORMAL PARAMETER DECLARATION, P296

"C99扩展了声明形式参数的语句. 一个用于修饰数组的限制符列表允许出现在数组声明者的最上层[]括号中.数组限制符(类型限制符)const, volatile和restrict将数组和指针类型等同对待.也就是说,这样的参数声明:
T A[qualifier-list e]
与以下声明等价:
T *qualifier-list e
例子:
给出这些C99中的声明:
extern int f(int x[const 10]);
extern int g(const y[10]);

那么在f函数中参数x会被看成是int * const类型(这是一个指向整型的指针常量), 而在g函数中参数y被看成它有const int *类型(也就是一个指向整型常量的指针).
译注:指向整型的指针常量,是指针的值无法改变,它指向一个可变的整型变量,也即无法改变p的值,但是*p的值是可以改的. 而后面的常量指针,是指向一个整型常量的指针,也就是p可以改变,但是*p是无法改变的.
在C99中,数组限制符static也允许出现在数组的括号之中.它对C的实现(编译器)是一个优化提示,断言实际的数组参数不能是NULL并且在进入函数体时要有所声明的类型和长度.如果没有这个限制符,NULL指针就可以当作实参作为数组传进来,就会使编译器很难知道它是安全的,比如,当在进入函数时要预取一个输入数组参数的内容时.
另外,对于原型(不能是函数定义)中的C99形式数组参数声明,其长度可以用星号(*)替代,这意味着实参将会是一个可变长数组.在原型声明中作为数组大小的任何非常量表达式与星号是等价.函数定义时必须提供一个非常量的表达式作为数组大小."

其实这里面提到的目的都是让编译器在编译时帮忙探测更多的错误.但是貌似大多数编译并不支持对函数声明数组参数的修饰,比如以上的例子在gcc中就完全没有效果,gcc不会给出任何提示.上面的建议只有static的修饰有实用意义,那就是帮忙排除NULL实参.其他的都仅有学术研究价值.
其实,在实际使用过程中,对于数组的参数,都必须同时给出另外一个参数来传达数组的长度,就像这样:
void foo(int a[], int len);
这是提倡的,也是大量普遍使用的方式.

你可能感兴趣的:(参数声明中的静态数组索引:一个不错且鲜为人知的C语言特性)