第一行的声明也是定义。(§3.1(2))
它创建了数组对象。(§1.8(1))
由于别名规则,可以通过多个lvalue访问对象。(§3.10(10))特别地,右侧的对象可以通过char指针合法地访问(别名)。
让我们看一下数组定义中的一个句子,然后消除“连续”的歧义。
“数组类型的对象包含一组非空的N个T类型子对象,这些子对象是连续分配的。”[dcl.array] §8.3.4。
澄清
我们从二元对称关系“连续”开始,这适用于char对象,这应该是显而易见的。(“iff”代表“如果且仅当”,集合和序列是数学上的概念,不是C++容器)如果您能链接到更好或更被认可的定义,请评论。
字符对象的序列x_1…x_N是连续的,当且仅当对于所有i=1…N-1,x_i和x_{i+1}在内存中连续。
一组字符对象M是连续的,当且仅当可以为其中的对象编号,例如x_1…x_N,使得序列(x_i)_i连续。也就是说,如果M是一个连续的、单射序列的图像,那么它是连续的。
两个字符对象的集合M_1,M_2是连续的,当且仅当存在M_1中的x_1和M_2中的x_2是相邻的。
字符对象集合的序列M_1…M_N是连续的,当且仅当M_i和M_{i+1}对于所有i=1…N-1是连续的。
一组字符对象集合是连续的,当且仅当它是字符对象集合的连续单射序列的图像。
现在应该应用哪个版本的“连续”?语言重载解析:
1)“连续”可能指“分配”。由于分配函数调用提供了一组可用的char对象的子集,因此这将调用char对象集合的变体。也就是说,所有N个子对象中出现的所有char对象都应该是连续的。
2)“连续”可能指“集合”。这将调用char对象集合的集合变体,每个子对象都被视为char对象的集合。
这是什么意思?首先,虽然作者将数组子对象编号为a [0] ... a [N-1],但他们选择不说关于子对象在内存中的顺序:他们使用了“集合”而不是“序列”。他们描述了分配是连续的,但他们没有说a [j]和a [j + 1]在内存中是连续的。此外,他们选择不写下涉及(char *)指针和sizeof()的直接公式。虽然看起来他们故意将连续性与排序问题分开,但§5.9(3)要求所有类型的数组子对象具有相同的排序方式。
如果指针指向同一数组的两个不同元素或其子对象,则具有较高下标的元素的指针比较大。
现在,组成数组子对象的字节是否符合上述引用中的子对象的定义?通过阅读§1.8(2)和完整对象或子对象?,答案是:对于不包含子对象且不是字符数组的数组,至少目前来说不是。因此,我们可能会找到一些示例,其中没有对数组元素施加特定的排序。
但是,暂时假设我们的数组子对象仅由字符填充。考虑“连续”的两种可能解释意味着什么?
1)我们有一组连续的字节,与一组有序的子对象重合。那么OP中的声明是无条件成立的。
2)我们有一系列连续的子对象,每个子对象可能单独不连续。这可以通过两种方式实现:要么子对象可能存在间隙,即它们包含两个距离大于sizeof(subobject)-1的char对象。要么子对象可能分布在不同的连续字节序列中。
在情况2)中,不能保证OP中的声明成立。
因此,清楚“连续”是什么意思非常重要。
最后,这里有一个实现的例子,其中§5.9没有对数组子对象施加明显的排序,因为数组子对象本身没有子对象。读者提出了担忧,认为这将与其他地方的标准相矛盾,但尚未证明明确的矛盾。
假设T是int,并且我们有一个特定的符合要求的实现,以朴素的方式表现得像预期一样,只有一个例外:
它按相反的内存顺序分配int数组,将数组的第一个元素放在对象的高内存地址端:
a[N-1], a[N-2], ... a[0]
替代
a[0], a[1], ... a[N-1]
这个实现满足任何合理的连续性要求,因此我们不必就“连续”达成单一解释就可以继续论证。
然后,如果 p 指向 a,将 p 映射到 &a[0](调用 [conv.array])会使指针跳转到 a 的高内存端附近。
由于数组算术必须与指针算术兼容,我们还需要
int * p= &intVariable;
(char*)(p+1) + sizeof(int) == (char*)p
和
int a[N];
(char*)(void*)&a[n] + n*sizeof(int)==(char*)(void*)&a[0], (0<=n<N)
然后,对于T=int,无法保证原帖中的声明是真实的。
编辑历史:删除并以修改后的形式重新引入了可能存在错误的快捷方式,这是由于未应用指针<关系规范的相关部分所致。尚未确定是否有正当理由,但关于连续性的主要论点仍然成立。
0<=n<=N
是有效的,这同样适用于标量。 - Bathshebap + 1
被定义了,但这并不意味着p + anything
也被定义了。公布的 C++14 标准文本被认为在这个领域有一些缺陷。一些已经被修复了(参见P0137),一些仍需要改进(例如CWG1701)。请记住,P0137澄清了a
和a [0]
不是指针可互换的。这不仅涉及布局,还涉及为允许优化而施加的限制。 - bogdana[b]
等同于*(a+b)
,这也是为什么它等同于b[a]
的原因。在 C++ 中,由于操作符重载的存在,这个原则已经有所退化,但它仍然对任何 C 风格的数组布局施加了非常严格的限制。而这种限制保证了你的等式必须成立。 - cmaster - reinstate monica