数组类型规范中的 const / static / volatile?

const / static / volatile in array type specification?

以下函数签名在 C 中定义了什么?!

#include <stdio.h>
#include <string.h>

void f(int a[const volatile static 2])
{
    (void)a;
}

int main() {
    int b[1];
    f(b);
}

https://godbolt.org/z/6qPxaM1vM

这里没看懂const / volatile / static的意思,不过好像能编译,所以我猜是有意思吧?

谢谢

数组类型参数自动调整为指针类型。 因此一个参数声明为:

int A[3]

转换为:

int *A

但是,使用数组表示法没有直观的地方可以添加限定符变量 a 本身(不是 a 指向的数据)。 因此 C 标准允许将这些说明符放在方括号之间。

因此:

void f(int a[const volatile restrict 2])

实际上是:

void (int * const volatile restrict a)

大小(上例中的2)通常被忽略。使用 static 关键字时除外。它为编译器提供了一个提示,地址a + 0a + size - 1处的至少个元素是有效的。 这个理论上的提示应该通过简化向量化来改进优化。但是,据我所知,主要编译器会忽略它。

您的代码:

int b[1];
f(b);

正在触发 UB 因为只有地址 b + 0 处的元素有效,而提示需要 b+0b+1 有效。更好的 compilers/sanitizers 应该检测到。

static 对于自我记录和错误检测也很有用,例如告诉指针指向的至少 n 个元素必须有效:

void fun(int n, int arr[static n])

甚至告诉指针永远不会 NULL:

void fun(int ptr[static 1]);

此外,语法 int buf[static n] 是一个很好的视觉提示,表明某些东西实际上 不是 数组。它有助于避免在尝试使用 sizeof buf 语法获取“数组”的大小时出现常见错误。


编辑

如评论中所述,“提示”一词可能有点误导 因为可以解释为违反“提示”不是 一个错误,尽管它可能会导致一些非最优性(如性能下降)。 实际上,这是一项要求,违反会导致未定义的行为。

这是 C99 中引入的一个比较有用的功能。来自 C17 6.7.6.3/7:

A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

在这种情况下,限定符 constvolatile 的意思是数组衰减为 int* const volatile a 类型的指针。由于此限定类型对函数而言是局部的,因此对调用者意义不大,因为传递的指针是通过左值转换分配的,并且仍然可以是非限定指针类型。

static 稍微有点用处,因为它可以让编译器进行一些编译时大小检查,尽管实际上主流编译器(目前)似乎只能检查指针不为空。例如 f(0) on clang 给出:

warning: null passed to a callee that requires a non-null argument [-Wnonnull]

奇怪的是,f(0) 在 gcc 11.1 及更高版本上说:

warning: argument 1 to 'int[static 8]' is null where non-null expected [-Wnonnull]"

不知道 8 是什么来历,我猜这是一个 typo/minor 编译器错误(衰减的指针有 8 个字节大)。