【问题标题】:Does `->` implies unreferencing?`->` 是否意味着不引用?
【发布时间】:2014-08-15 07:50:42
【问题描述】:

我正在使用下面的代码来查找某些属性的对齐属性。我知道存储 NULL 指针是定义的行为,指针操作也是定义的行为,只有取消引用 NULL(和其他无效值)指针才会调用未定义的行为。我的问题很简单:-> 是否意味着取消引用指针(从而导致下面代码中的未定义行为)?

#include <iostream>

void f(void *p)
{
  std::cout << p << std::endl;
}

struct X
{
  int a;
  int b;
};

struct Y
{
  int a[2];
  int b;
};

int main()
{
  X *x = NULL;
  Y *y = NULL;
  f(&x->b);
  f(&y->b);
}

【问题讨论】:

  • 在我看来&amp;x(和&amp;y)和-&gt; 都会导致未定义的行为。一个是因为您要查询NULL 的地址,另一个是因为您要取消引用NULL 指针。
  • 你实际上有两个问题。 &amp;xx-&gt;b 一样都是 UB,所以这种表达方式会导致各种问题。
  • @wolfpack88:其实&amp;的优先级比-&gt;低,然后再评估:en.wikipedia.org/wiki/…
  • @park-young-bae:这里没有运算符重载(我的代码已经有了这个保证)。
  • @wolfPack88 即使表达式是(&amp;x)-&gt;b&amp;x 并没有采用NULL 的地址(无论如何这没有意义),你正在采用变量@987654336 的地址@。在这种情况下,代码不会编译,因为X* 没有任何名为b 的成员。 vinipsmaker:从 §5.2.5/2 - 表达式 E1->E2 转换为等效形式 (*(E1)).E2。所以你的代码有未定义的行为。

标签: c++ pointers alignment


【解决方案1】:

-&gt; 运算符是*. 运算符的组合。对于

X *x = (struct *)malloc(sizeof(struct));
f(&x->b);

它在x 上执行间接定位它指向的结构,然后选择结构的b 成员。类似于调用

 f( &(*x).b ); // which is same as f( & ( (*x).b ) );

由于在您的情况下 xNULL 指针,因此取消引用 NULL 指针会调用未定义的行为:
C++11:8.3.2 引用 (p4):

注意:特别是,空引用不能存在于定义良好的程序中,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这导致未定义的行为

【讨论】:

    【解决方案2】:

    有问题的代码不是未定义的行为(尽管它有点不合常规)。该代码本质上是在对f() 的调用中执行穷人的offsetof(),您将在其中将b 的结构偏移量传递给该函数。 NULL 指针不会被取消引用,因为您正在计算x-&gt;b 的地址。您没有通过 NULL 指针加载 x-&gt;b 的内容。

    为什么上面的代码(相当于offsetof)不是未定义的行为,C++ spec (draft 2012-01-16),第 427 页指出:

    offsetof(type, member-designator) 接受本国际标准中的一组受限类型参数。如果 type 不是标准布局类(第 9 条),则结果未定义。

    上面代码中的用法是定义的,因为它使用标准布局结构类型(struct Xstruct Y)。

    【讨论】:

    • 它不需要是 UB 的取消引用(即使我相信它是取消引用,从语言的角度来看)。只有创建一个比数组末尾的一个元素更靠前的指针才是UB:这意味着对空指针的任何操作都是UB。
    • zneak,你能引用那个 UB 吗?
    • @zneak,鉴于 stddef.h 中的 offsetof() 宏在上面的代码中使用了相同的技术(基于 NULL 指针计算地址),我会说这不是未定义的行为(即标准 C 库不会通过提供上述宏来鼓励未定义的行为)。 offsetof() 的常见定义如下所示:#define offsetof(TYPE, MEMBER) ((size_t) &amp;((TYPE *)0)-&gt;MEMBER)
    • 我应该补充一点,标准 C++ 还提供了 offsetof() 宏。
    • @troydj,关于使用空解引用实现的 offsetof 宏的有效性已经有 quite a bit of discussion。 GCC 现在在 C++ 中使用 __builtin 来处理它,因为类型可以重载地址运算符,在这种情况下,这绝对是 UB。
    猜你喜欢
    • 2018-07-25
    • 2015-06-21
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-14
    • 2019-03-10
    相关资源
    最近更新 更多