【问题标题】:What's the difference between var and &(*var)var 和 &(*var) 有什么区别
【发布时间】:2018-05-19 07:14:33
【问题描述】:

一些源代码使用符号&(*var),其中var 已经是一个指针,例如int *var = ...。

这两种符号有区别吗?

是var != &(*var) 吗?

示例:https://github.com/FreeFem/FreeFem-sources/blob/develop/src/medit/inout_popenbinaire.c#L40

【问题讨论】:

  • "notation &(*var) where var 已经是一个指针,就像int *var = ..." 这个比较我看不懂。我看不到“like”的&。
  • 在链接代码中,看起来这个人想对所有读取使用模式fread( (unsigned char *)&(OBJECT)...尽管他们认为这是一个好主意的确切原因是任何人的猜测。我会说这是一个糟糕的模式,因为它具有冗余(在 C89 中是有害的)演员表。该代码还有其他不好的东西,例如#define WrdSiz 4,不包括stdio.h 和while (!feof(stdin)),并且不检查fread 等的返回值。基本上这是一个充满错误和未定义行为的糟糕代码,你应该不要把它当作学习材料

标签: c language-lawyer


【解决方案1】:

&(*var) 在大多数情况下,在编译时的计算结果与var 相同,但请注意一些警告:

  • var 应该是一个有效的指针,尽管编译器没有理由生成代码来取消引用它。事实上,C11 指定 &*var 没有未定义的行为,即使 var 是空指针。
  • 正如 Pascal Cuoq 评论的那样,var 不应该是指向 void 的指针,但 C 标准明确允许它用于这种情况......确实令人震惊。
  • 如果var 是一个包含多个元素的数组,则var 和&*var 具有不同的类型,sizeof(var) 和sizeof(&(*var)) 具有不同的值:第一个是数组的大小,而第二个是指向其第一个元素的指针的大小。
  • 正如 Peter 所评论的,如果 var 是一个未初始化的指针,那么评估 var == &(*var) 会产生未定义的行为(因为评估 == 的任一侧都会产生未定义的行为 - 访问未初始化的变量给出了未定义的行为)。

您在问题中链接到的示例没有任何目的使用此结构:

#define WrdSiz 4

void getline_bin_float_vertex(int ddim, double *c, int *ref) {
    int i;
    float ff;

    for (i = 0; i < ddim; i++) {
        fread((unsigned char *)&(ff), WrdSiz, 1, stdin);
        c[i] = ff;
    }
    fread((unsigned char *)&(*ref), WrdSiz, 1, stdin);
}

程序员正在从重定向到标准输入的文件中读取一些浮点值和一个整数。代码笨拙且不可移植:

  • float 的大小默认为 4
  • int 的大小默认为相同的 4 个字节
  • c 必须是有效指针,除非 ddim 为 0
  • ref 必须是一个有效指针,因为 fread 将尝试在它指向的地址存储 4 个字节,除非流位于文件末尾
  • 文件结尾和读取错误会被静默忽略,并且可能出现未定义的行为。

可以这样简化和保护代码:

#define WrdSiz 4

/* read values from stdin, return non-zero in case of failure */
int getline_bin_float_vertex(int ddim, double *c, int *ref) {
    int i;
    float ff;

    assert(sizeof float == WrdSiz);
    assert(sizeof int == WrdSiz);
    assert(dim == 0 || c != NULL);
    assert(ref != NULL);

    for (i = 0; i < ddim; i++) {
        if (fread(&ff, sizeof ff, 1, stdin) != 1)
            return -1;
        c[i] = ff;
    }

    if (fread(ref, sizeof(*ref), 1, stdin) != 1)
        return -1;
    return 0;
}

此源代码的其他部分显示糟糕甚至无效的结构,您应该仅研究此包以了解不该做什么的示例。

【讨论】:

  • 标准says“如果操作数 [of &] 是一元 * 运算符的结果,则该运算符和 & 运算符都不会被计算,结果就像两者都被省略了一样,除了对运营商的限制仍然适用并且……”。在标准的上下文中,“约束”是指编译器必须在编译时警告和/或拒绝的东西。 “除了对运算符的约束仍然适用”部分意味着如果p 是指向 void 的指针,则应该拒绝(或至少警告)&amp;*p,例如......
  • 但是如果p 是一个指向int 的指针,它必须是一个有效的指针并不是标准定义的约束,所以没有理由认为&amp;*p 是当 p 是 (int*)0 时,不等于 p。
  • 如果var 是一个未初始化的指针,那么评估var == &amp;(*var) 会给出未定义的行为(因为评估== 的任一侧都会给出未定义的行为 - 访问未初始化变量的值会给出未定义的行为)。第三个项目符号中的点是因为如果var 是一个数组,那么var 和&amp;(*var) 具有不同的类型。
  • @PascalCuoq 当p 的类型为void * 时,C 中没有任何内容不允许*p(因此也没有任何内容不允许&amp;*p),是吗? C++中有这样的规则,但是C++与这个问题无关。
  • @hvd 你是对的,这根本不能作为约束的例子。 6.3.2.1:1 中有一句话“左值是一个表达式(对象类型不是 void),它可能指定一个对象”。这句话不在 Constraints 标题下。此外,即使p 具有类型void*,*p 不是左值,&amp;*p 是explicitly 允许:godbolt.org/g/7r4SRF
【解决方案2】:

&amp;(*var) 可能有 undefined behavior,而 var(指针变量)不包含旧版本 C 的有效地址。在这种情况下为 scared。但 C11 标准 §6.5.3.2 声明编译器必须在所有情况下将其视为等效(包括当 var 为 NULL 时)

所以当var 包含有效地址时,var == &amp;(*var) 为真。当它包含其他内容时(例如NULL),如果您使用不符合 C11 的非优化编译器,它可能会崩溃(segmentation fault)(但大多数编译器,即使是旧编译器,也不会在其中生成一些崩溃代码案例)

但是允许编译器(甚至是旧的 C99 编译器)(在as-if rule 下)将optimize &amp;(*var) 转换为var

【讨论】:

  • 那是一个非常不同的问题,C 标准禁止获取声明为 register 的变量的地址(今天,register 对此没有其他兴趣)
  • 从 C11 开始(至少),&amp;*E 在所有情况下都等同于 E(包括 E 是一个空指针)。见 6.5.3.2/3
  • 我不明白为什么。你天真地认为register 关键字与register allocation 有关。在 2018 年,register 关键字几乎没用了
  • 没有段错误,因为那里没有取消引用。
  • 我在github.com/FreeFem/FreeFem-sources/blob/develop/src/medit/… 中看到了这一点。在这种情况下,使用它是非常危险的。
【解决方案3】:

可能它是括号内的一个较长的表达式,但在调试期间已简化为这个并留在源代码中。没有任何意义

编辑 - github示例看起来完全符合我的预期。

【讨论】:

    猜你喜欢
    • 2019-09-21
    • 2018-12-07
    • 1970-01-01
    • 2013-06-28
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多