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

发布于 2024-12-12 07:14:03 字数 222 浏览 0 评论 0原文

void foo(const char *s);

相当于:

void foo(const char s[]);

是否有与以下两个类似的等价物?

void foo(char * const s);
void foo(const char * const s);
void foo(const char *s);

is equivalent to:

void foo(const char s[]);

Are there similar equivalents to the following two?

void foo(char * const s);
void foo(const char * const s);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

ι不睡觉的鱼゛ 2024-12-19 07:14:03

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

void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )

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

void f( const int a[] const );  // Invalid type definition

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

这在 C++03 标准的 §8.3.5/3 中进行了处理(可能在 C++11 中接近的地方)

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

请注意,由于编译器将执行该转换,因此最好遵循最小意外原则编写编译器将使用的实际类型:

void f( int a[10] ) { a[5] = 7; }

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

void f( int *a ) { a[5] = 7; }

可能会在代码审查中触发一些警报:我们是否保证在对 f 的所有调用中参数至少有 6 个元素大?为了以防万一,我们不应该也传递尺寸吗?

In C++, the compiler will automatically convert function parameters of type array of N elements of type T (where N can be unknown) into pointer to T. In the same transformation top level const qualifiers for the arguments are dropped:

void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )

That means that in the pointer case, the top level const qualifier is removed, and in the case of the array it is converted to pointer to. Now, on the other hand you cannot mix both, only because you cannot declare a constant array of N elements of type T, since arrays are always const. That means that you cannot write:

void f( const int a[] const );  // Invalid type definition

As the type of the parameter is invalid. If it was a valid type, then the conversions would apply, but because it is not, the compiler will reject the code before trying to perform the conversion.

This is treated in §8.3.5/3 of the C++03 standard (and probably somewhere close in C++11)

A single name can be used for several different functions in a single scope; this is function overloading (clause 13). All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type. The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any cv-qualifier modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types is the function’s parameter type list.

Note that since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise:

void f( int a[10] ) { a[5] = 7; }

The compiler is not going to check that the passed array has 10 elements, it reads the declaration as void f( int * ), and will gladly accept a call with an array of less elements or even no array at all (a pointer to a single int). Using a pointer in the actual code:

void f( int *a ) { a[5] = 7; }

Will likely trigger some alarms in a code review: are we guaranteed that in all calls to f the argument will be at least 6 elements big? Should we not pass also the size just in case?

渔村楼浪 2024-12-19 07:14:03

在 C89 中不能,但在 C99 中您可以将等价物声明为:

void foo(char s[const]);
void foo(const char s[const]);

You cannot in C89, but in C99 you can declare the equivalents as:

void foo(char s[const]);
void foo(const char s[const]);
请止步禁区 2024-12-19 07:14:03

这在某些情况下很有用:

class AA {
  void foo(char a[]);
  void foo(const char a[]);
};

void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }

在 C 中:

extern void foo(char a[]);
extern void fooc(const char a[]);


void foo(char* const a) { … }
void fooc(const char* const a) { … }

this will be useful in some cases:

class AA {
  void foo(char a[]);
  void foo(const char a[]);
};

void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }

and in C:

extern void foo(char a[]);
extern void fooc(const char a[]);


void foo(char* const a) { … }
void fooc(const char* const a) { … }
离鸿 2024-12-19 07:14:03

我认为指针可以为空,而数组参数不能为空(并且允许编译器在知道这一点的情况下进行优化;但是在一个简单的示例中,gcc-4.6 不会进行此类优化,即使使用 -O3 也是如此)。

我期望编译器会对下面的两个函数进行不同的优化。事实并非如此。我手头没有 C 标准来检查它是否可以删除下面 ss 中的测试。

int s (int *t)
{
  if (!t)
    return 0;
  return t[0] + t[1];
}


int ss (int t[])
{
  if (!t) // never false, since t is an array!!
    return 0;
  return t[0] + t[1];
}

I thought that a pointer can be null, while an array argument cannot be null (and that the compiler is permitted to optimize knowing that; however on a simple example gcc-4.6 don't do such an optimization, even with -O3).

I am expecting that the compiler would optimize differently the two functions below. It does not. I don't have my C standard at hand to check if it could remove the test in ss below.

int s (int *t)
{
  if (!t)
    return 0;
  return t[0] + t[1];
}


int ss (int t[])
{
  if (!t) // never false, since t is an array!!
    return 0;
  return t[0] + t[1];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文