变量是一个大小为1的数组吗?

Dan*_*our 15 c++ language-lawyer c++11 c++14 c++17

考虑一下:

int main(int, char **) {
  int variable = 21;
  int array[1] = {21};
  using ArrayOf1Int = int[1];
  (*reinterpret_cast<ArrayOf1Int *>(&variable))[0] = 42;
  *reinterpret_cast<int *>(&array) = 42;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我是否违反了严格的别名规则

或者,正如在这条引导我这个问题的评论中一样:变量是一个大小为1的数组吗?

请注意,我将此标记为语言律师问题.因此,我对-fno-strict-aliasing编译器的特定行为不感兴趣,而是在标准中所说的内容.另外我认为知道C++ 03,C++ 11,C++ 14和更新版本之间是否以及如何变化将会很有趣.

Die*_*ühl 9

显然,如果一个对象是一个大小为1的数组的变量,则可以使用对象初始化对大小为1的数组的引用:

int variable{21};
int (&array)[1] = variable; // illegal
Run Code Online (Sandbox Code Playgroud)

但是,初始化是非法的.相关条款是第1款所述的第4条[转换](标准转换):

标准转化是具有内置含义的隐式转化.第4条列举了全套此类转换.

这个子句太长了,不能在这里引用,但它没有任何关于将对象转换为任何大小的数组引用的说法.类似地,reinterpret_cast(5.2.10 [expr.reinterpret.cast]中的部分)没有说明涉及数组的任何行为,但在第1段中明确说明了这一排除:

... reinterpret_cast下面列出了可以明确使用的转换.无法使用明确执行其他转换reinterpret_cast.

我不认为有一个明确的声明,即一个对象不是一个对象的数组,但有足够的遗漏来隐式地使用该情况.标准关联对象和数组给出的保证是指向对象的指针表现为它们指向大小为1的数组(5.7 [expr.add]段落4):

出于这些运算符的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.

此语句的存在还意味着数组对象和非数组对象是不同的实体:如果它们被认为是相同的,那么这个语句就不必开始了.

关于标准的先前(或未来)版本:虽然不同条款中的确切单词可能已经改变,但总体情况没有改变:对象和数组总是不同的实体,到目前为止,我还不知道意图改变这一点.

  • 我认为这忽略了问题的重点:使用变量的语法与使用大小为1的数组的语法不同,但是OP试图获得类似的结果:变量是否具有与数组相同的属性,语法不谈. (4认同)

Bri*_*ian 7

reinterpret_cast只能在C++ 11及更高版本中出现可预测的行为,因此在C++ 11之前,两行都不能保证定义行为.我们将继续假设C++ 11或更高版本.

第一行

(*reinterpret_cast<decltype(&array)>(&variable))[0] = 42;
Run Code Online (Sandbox Code Playgroud)

在这一行,提领该reinterpret_cast收益率glvalue但不访问int通过glvalue对象.到int访问对象时,引用该数组的glvalue已经被衰减为指向该对象的指针(即a int*).

但是,人们可以"设计"一个看起来可能包含严格别名违规的情况,如下所示:

struct S {
    int a[1];
};
int variable = 42;
S s = reinterpret_cast<S&>(variable);
Run Code Online (Sandbox Code Playgroud)

这并没有违反严格别名,因为你被允许通过聚集或联合类型的子对象访问的对象.(此规则自C++ 98以来就已存在.)

第二行

*reinterpret_cast<decltype(&variable)>(&array) = 42;
Run Code Online (Sandbox Code Playgroud)

reinterpret_cast保证,得到指向数组,这是一个的第一子对象int的对象,因此通过分配给它int的指针是明确定义的.

  • @Brian:我不认为它更"严格",只是现有规则似乎适用:你通过类型为`S`的glvalue访问`int`类型的对象.这是被禁止的.那么为什么你认为这样可以呢?注意`variable`不是`S`类型对象的子对象.这已经是一个完整的对象了. (2认同)

Jer*_*fin 6

最近的一份草案说:

§[expr.unary.op]/3:

一元&运算符的结果是指向其操作数的指针.[...]出于指针运算(5.7)和比较(5.9,5.10)的目的,不是以这种方式获取地址的数组元素的对象被认为属于具有T类型的一个元素的数组.

我们在这里处理的类型都是真正的指针,但我们(最终)取消引用它们.因此,这可能不足以呈现定义的行为(但它是一个近距离调用).

至于版本之间的变化:该措辞在N4296(C++ 14和C++ 17之间的草案)中,而不是N4140或N3337(分别基本上是C++ 14和C++ 11).

C11标准fscanf_sfwscanf_s(§K.3.5.3.2/ 4)的语言模糊相似:

这些参数中的第一个与fscanf相同.该参数在参数列表中紧跟第二个参数,其类型为rsize_t,并给出该对的第一个参数指向的数组中的元素数.如果第一个参数指向标量对象,则认为它是一个元素的数组.