C++ 负数组索引
这是我的结构:C++ 负数组索引,c++,c,arrays,indexing,C++,C,Arrays,Indexing,这是我的结构: struct Node { struct Node* data; struct Node* links[4]; } 假设没有填充,Node->links[-1]是否保证指向Node::data?我会说是,但为什么不声明为 struct Node { struct Node* links[5]; } 我想是的。但是标准并不能保证没有填充物 编辑:但该标准还规定,如果“数组下标超出范围,即使使用给定下标显然可以访问某个对象”,则行为是未定义的;这是未定义的
struct Node {
struct Node* data;
struct Node* links[4];
}
假设没有填充,
Node->links[-1]
是否保证指向Node::data
?我会说是,但为什么不声明为
struct Node {
struct Node* links[5];
}
我想是的。但是标准并不能保证没有填充物
编辑:但该标准还规定,如果“数组下标超出范围,即使使用给定下标显然可以访问某个对象”,则行为是未定义的;这是未定义的行为:
- 编译器相关的结构填充
- 标准仅定义0和长度(包括)之间的数组索引
- 可能的严格别名冲突
实际上,很有可能最终指向
数据
,但任何试图访问它的尝试都会导致UB。一个联合
可能会对您有所帮助。大概是这样的:
struct Node {
union {
Node *data;
struct {
Node *lnk[5];
} links;
}
}
几乎是的
是的,它几乎肯定会指向数据
,但它可能并不完全是一种保证
实际上,你确实得到了一个保证,即x[-1]
与*(x-1)
是同一件事,因此,根据你在x-1
的地址上写的内容,你确实有一种保证。(不要忘记,当从指针中减去时,减量是由项目的大小决定的。)
尽管其他人基于严格的别名而反对,但在这种情况下,类型是相同的,因此详细的优化不应删除您的预期行为。这是实现定义的,取决于实现如何填充结构和数组。但是,只要它以相同的方式填充结构和数组,它就应该可以工作。数组下标是根据指针算法定义的,C99标准对指针算法有如下规定: 如果指针操作数和 结果指向 相同的数组对象,或一个过去的 数组对象的最后一个元素, 该评估不应产生任何结果 过流;否则,该行为是错误的 未定义 因此,严格地说,访问
节点->链接[-1]
(即使只是获取节点->链接[-1]
的地址)是未定义的行为。因此,您无法保证Node->links[-1]
将获得Node::data
但正如许多评论员所提到的,它几乎总是有效的。我仍然认为应该避免那些糟糕的编程实践。如果不是因为技术上的原因,那么因为它要求对
struct Node
的修改很容易导致编译器无法帮助您解决的bug。当有人在数据
和链接
之间添加一些内容时,事情会神秘地中断。因为在我的情况下,我不能这样做。[1]我的问题是假设没有填充,你可以设置编译器生成没有填充。[2]那么关于标准,你能告诉我它说的是哪一行吗?因为我的理解是针对数组a[]
,所以索引i
被定义为*(a+i)
,这与a[i]
或i[a]
的*(i+a)
相同。[3] 什么是严格别名冲突?有关指针算法,请参阅C99第6.5.6节第8段。我对严格别名的理解是错误的,它不适用于这种情况(因此我将编辑我的答案)。严格来说,这会导致实现定义的行为,而不是未定义的行为——实现依赖性是由于结构填充。标准根据指针添加定义数组索引。指针加法的结果只有在它产生分配对象内的地址或紧接它之后的地址时才被定义。@Oli Charlesworth:向我展示任何一个C编译器,如果这个东西不起作用的话。如果他将指向此结构的重叠指针传递给具有严格参数的函数,您关于严格别名的评论可能适用,但我认为这一点根本不相似。请参阅我的答案中的注释,了解为什么这是未定义的,而非实现定义的。+1了解为什么这是糟糕的编程实践(imhoundefined behavior
是一个简短的解释,标准中的几个ub案例只是为了考虑真正的外来硬件,在实践中并不重要)。这在实践中可能会起作用,但请注意,写入一个enum成员,然后从另一个enum成员读取会导致未定义的行为。