我可以传递伪装成数组的常量指针吗?

fre*_*low 12 c c++ arrays pointers const

void foo(const char *s);
Run Code Online (Sandbox Code Playgroud)

相当于:

void foo(const char s[]);
Run Code Online (Sandbox Code Playgroud)

是否有类似的以下两个?

void foo(char * const s);
void foo(const char * const s);
Run Code Online (Sandbox Code Playgroud)

Dav*_*eas 14

在C++中,编译器会自动将类型为T的N个元素的类型数组(N可以是未知的)的函数参数转换为指向T的指针.在相同的转换const中,参数的顶级限定符将被删除:

void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )
Run Code Online (Sandbox Code Playgroud)

这意味着在指针的情况下,顶级const限定符被删除,而在数组的情况下,它被转换为指针.现在,另一方面,你不能混合两者,只是因为你不能声明类型为T的N个元素的常量数组,因为数组总是const.这意味着你不能写:

void f( const int a[] const );  // Invalid type definition
Run Code Online (Sandbox Code Playgroud)

由于参数的类型无效.如果它是有效类型,则转换将适用,但由于它不适用,编译器将在尝试执行转换之前拒绝该代码.

这在C++ 03标准的§8.3.5/ 3中处理(可能在C++ 11中很接近)

单个名称可用于单个范围内的多个不同功能; 这是函数重载(第13条).具有给定参数列表的函数的所有声明应完全同意返回值的类型以及参数的数量和类型; 省略号的存在与否被认为是函数类型的一部分.使用以下规则确定函数的类型.每个参数的类型由其自己的decl-specifier-seq和声明符确定.在确定每个参数的类型之后,将"T数组"或"函数返回T"类型的任何参数分别调整为"指向T的指针"或"指向函数返回T的指针".在生成参数类型列表之后,会对这些类型进行多次转换以确定函数类型.将删除修改参数类型的任何cv限定符.[示例:类型void(*)(const int)变为void(*)(int)-end示例]此类cv限定符仅影响函数体内参数的定义; 它们不会影响功能类型.如果存储类说明符修改参数类型,则删除说明符.[示例:register char*变为char*-end示例]此类存储类说明符仅影响函数体内参数的定义; 它们不会影响功能类型.生成的已转换参数类型列表是函数的参数类型列表.

请注意,由于编译器将执行该转换,因此最好按照最少惊喜的原则编写将由编译器使用的实际类型:

void f( int a[10] ) { a[5] = 7; }
Run Code Online (Sandbox Code Playgroud)

编译器不会检查传递的数组是否有10个元素,它将声明作为读取void f( int * ),并且很乐意接受带有较少元素的数组的调用,甚至根本没有数组(指向单个int的指针).在实际代码中使用指针:

void f( int *a ) { a[5] = 7; }
Run Code Online (Sandbox Code Playgroud)

可能会在代码审查中触发一些警报:我们是否保证在f参数的所有调用中至少有6个元素?我们是否也不能通过大小以防万一?

  • 因为编译器将执行该转换,所以+1最好是编写将由编译器使用的实际类型,遵循最小惊喜原则*.好点.此后的文字也很棒. (2认同)

caf*_*caf 13

你不能在C89,但在C99你可以声明等价物:

void foo(char s[const]);
void foo(const char s[const]);
Run Code Online (Sandbox Code Playgroud)