将非数组变量的地址传递给声明为 Type ptr[static 1] 的函数参数是否有效?

Is it valid to pass the address of a non-array variable to a function parameter declared as `Type ptr[static 1]`?

如前所述here, and 一个函数(在c99或更新版本中)以这种方式定义

void func(int ptr[static 1]){
    //do something with ptr, knowing that ptr != NULL
}

有一个指向 int 类型指针的参数 (ptr),编译器可以假定该函数永远不会以 null 作为参数调用。 (例如,如果使用空指针调用 func,编译器可以优化空指针检查或发出警告 - 是的,我知道,编译器不需要 来执行任何操作...)

C17 节 6.7.6.3 Function declarators (including prototypes) 第 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.

在上述定义的情况下,ptr 的值必须提供对至少具有 1 个元素的数组的第一个元素的访问。因此很明显,参数永远不会为空。

我在徘徊的是,使用不属于数组的一部分的 int 地址调用这样的函数是否有效。例如。这是(给定上面 func 的定义)在技术上有效还是未定义的行为:

int var = 5;
func(&var); 

我知道这实际上永远不会成为问题,因为据我所知,没有编译器会区分指向 int 数组成员的指针和指向局部 int 变量的指针。但是考虑到 c 中的指针(至少从标准的角度来看)can be 不仅仅是一些具有特殊编译时类型的整数,如果标准中有某些部分,我会徘徊,这使得它有效。

我怀疑它实际上无效,因为第 6.5.6 Additive operators 节第 8 段包含:

[...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. [...]

对我来说,这听起来好像对于任何指向数组元素的指针,添加 1 是一个有效的操作,而将 1 添加到指针是 UB指向一个常规变量。这意味着,指向数组元素的指针和指向普通变量的指针之间确实存在差异,这将使 UB 以上的代码片段...

6.5.6 Additive operators 节第 7 段包含:

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

由于该段落以“为了这些运算符的目的”开头,我怀疑在其他情况下可能会有差异?


tl;博士;

标准中是否有某些部分规定,指向 T 类型的常规变量的指针与指向长度为 1 的数组(输入 T[1])?

从表面上看,我认为你说得有道理。我们并没有真正将指针传递给数组的第一个元素。如果我们在真空中考虑标准,这可能是 UB。

除了您在 6.5.6 中引用的段落之外,标准中没有任何段落将单个对象等同于一个元素的数组。不应该,因为两者是不同的。当出现在大多数表达式中时,数组(甚至一个元素)被隐式转换为指针。这显然不是 属性 大多数对象类型所拥有的。

[]static关键字的定义提到传递的指针必须指向至少包含一定数量元素的数组的初始元素。你引用的措辞还有一个问题,那

int a[2];
func(a + 1);

显然传递的指针不是指向数组的 first 元素。如果我们对 6.7.6.3p7 进行字面解释,那也是 UB。

撇开 static 关键字不谈,当函数接受指向对象的指针时,该对象是否是数组(任意大小)的成员仅在一个上下文中很重要:指针算法。

在没有指针运算的情况下,使用指针访问数组元素或独立对象时,在行为上没有明显区别。

我认为 6.7.6.3p7 背后的意图考虑了指针算法。因此,所提到的语义与尝试对传递给函数的指针进行指针运算密切相关。

static 1 的使用只是作为有用的习语自然而然地出现,也许并不是一开始的意图。虽然规范性文本可能会稍作更正,但我认为其背后的意图很明确。 不是标准未定义的行为。

标准的作者几乎肯定打算质量实现将对待指向非数组对象的指针的 value 的方式与对待 value of a pointer to a point of a point of a array object of length 1. 如果只是说指向非数组对象的指针等同于指向数组的指针,那可能被误解为适用于产生指针值的所有表达式。这可能会导致问题,例如char a[1],*p=a;,因为表达式 ap 都产生具有相同值的 char* 类型的指针,但是 sizeof psizeof a 可能会产生不同的值。

在编写标准之前,该语言已被广泛使用,程序依赖这种行为的情况并不少见。因此,应该期望实现真正努力以与已发布的基本原理文档中记录的标准委员会的意图一致的方式行事,以有意义地处理此类代码,而不管对标准的迂腐阅读是否需要它。然而,不做出此类努力的实现不应被信任以有意义地处理此类代码。