【问题标题】:Arithmetic with array of pointers casted to `void **`将指针数组转换为“void **”的算术运算
【发布时间】:2021-02-14 09:20:16
【问题描述】:

我知道 C 标准承诺指向任何对象的指针可以往返于 void * 并返回。换句话说,

T obj;
assert(&obj == (T *) (void *) &obj);

现在,我想知道指针算法。它是如何遵循的/它在哪里说的

T *arr[2] = {0};
assert(arr[1] == (T *) (((void **)arr)[1]))

有效吗?这还能保证吗?

我有一种预感,答案是“不能保证”,甚至是“这是 UB”。原因是,据我“所知”,void * 的表示不必等于其他指针的表示。例如,如果 void 指针的大小比其他指针大,void ** 上的指针运算可能无法工作。所以除非上述是合法的,我想知道如果我想用泛型代码对指针数组进行操作,我应该在实践中做什么。

【问题讨论】:

  • C 标准中没有任何内容可以保证这将起作用。正如您所注意到的,没有任何东西可以保证 void *T * 具有相同的表示或大小。但是,即使他们这样做了,使用 void * 类型的左值访问数组的元素 T * 也违反了 C 2018 6.5 7 中的别名规则。该行为未由标准定义,因此assert 不能保证失败,即使在某种意义上,左右操作数具有相同的值。
  • 这个话题是discussed here。它回答了你的问题吗?
  • 如果您知道您的程序将在 C 实现中执行,其中使用的指针类型具有相同的大小和表示形式,那么使用指针的定义方法是使用unsigned char * 指针从数组中取出字节,将字节复制到void *,然后使用强制转换将void * 转换为所需的类型。

标签: c


【解决方案1】:

我有一种预感,答案是“无法保证”甚至“这 是UB”。原因是,据我“所知”, void * 不需要等于其他指针的表示。为了 例如,如果 void 指针的大小比其他指针大, void ** 上的指针运算可能无法正常工作。

你是对的,你的推理是对的,至少在实际意义上。从标准的角度来看,有点不清楚哪种方式可以证明评估表达式 ((void **)arr)[1] 会产生未定义的行为。我倾向于这样做:表达式等效于*(((void **)arr) + 1),并且由于(void **)arr 实际上并未指向void * 的数组,因此指针添加会产生未定义的行为。但还有一个事实是 *(((void **)arr) + 1) 的类型是 void *,并且根据严格的别名规则,如果它实际上没有引用 void *,则评估会产生未定义的行为。

我想知道如果我想用泛型代码对指针数组进行操作,我应该怎么做。

C 没有表达泛型指针数组类型的机制。特别是void *arr[DIMENSION] 形式的数组和void ** 类型的指针不是通用的。但是,您可以使用这些特定类型的数组和指针,并使用从任意其他(对象)指针类型转换而来的指针加载它们。

根据您要执行的具体操作,可能还有其他选择。

【讨论】:

    猜你喜欢
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 2018-06-10
    相关资源
    最近更新 更多