var和&(*var)有什么区别?(涉及IT技术)

8

“*notation &(*var) where var is already a pointer, like int *var = ...*” 这个比较我不理解。我看不到“like”后面有任何&符号。 - alk
1
相关链接:https://dev59.com/PWsz5IYBdhLWcg3wuKa6 - alk
3
在链接的代码中,看起来这个人想要在所有读取操作中使用模式 fread( (unsigned char *)&(OBJECT),尽管为什么他们认为这是一个好主意是任何人都无法猜测的。我会说这是一种不好的模式,因为它具有冗余的(在C89中有害的)强制转换。该代码还有其他的问题,比如 #define WrdSiz 4、没有包含 stdio.h,以及 while (!feof(stdin)) 等等。基本上,这是一个充满错误和未定义行为的可怕代码,您不应将其视为学习材料。 - M.M
3个回答

10

&(*var)在大多数情况下与var的结果相同,但请注意以下一些限制:

  • var应该是一个有效的指针,尽管编译器不需要生成解引用它的代码。实际上,C11规范规定,即使var是一个空指针,&*var也不会出现未定义的行为。
  • 正如所评论的那样,var不应该是一个指向void的指针,但C标准明确允许这种情况...令人震惊。
  • 如果var是一个具有多个元素的数组,则var&*var具有不同的类型,而sizeof(var)sizeof(&(*var))具有不同的值:前者是数组的大小,后者是指向其第一个元素的指针的大小。
  • 如所评论的那样,如果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 个字节。
  • 除非 ddim 为 0,否则 c 必须是有效指针。
  • fread 将尝试在其指向的地址处存储 4 字节,除非流已到达文件末尾,否则 ref 必须是有效指针。
  • 文件结束和读取错误被默默忽略,可能导致未定义行为。

通过下面的方法可以简化代码并使其更加安全:

#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;
}

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


1
标准说明:“如果&的操作数是一元*运算符的结果,则不评估该运算符或&运算符,并且结果就好像两者都被省略了,除了运算符的约束仍然适用于...”。在标准的上下文中,“约束”指编译器必须在编译时警告和/或拒绝的事物。 “除了运算符的约束仍然适用”部分意味着,例如,如果p是指向void的指针,则应拒绝(或至少警告)&*p - Pascal Cuoq
1
但是,如果p是指向int的指针,则它必须是有效指针不是标准定义的约束条件,因此没有理由认为当p(int*)0时,&*p不等同于p - Pascal Cuoq
如果var是未初始化的指针,则评估var == &(*var)会产生未定义的行为(因为评估==的任一侧都会产生未定义的行为-访问未初始化变量的值就是导致未定义行为的原因)。第三个要点是由于,如果var是一个数组,则var&(*var)具有不同的类型。 - Peter
@PascalCuoq,C语言中没有任何规定禁止*p(因此也没有禁止&*p),当p的类型为void *时,是吗?在C++中有这样的规则,但C++与这个问题无关。 - user743382
@chqrlie 可能吧...我收不到很多电子邮件,刚刚收到了一封。 - Pascal Cuoq
显示剩余2条评论

5

&(*var)可能在旧版C语言中出现未定义行为,当var(一个指针变量)不包含有效地址时。在这种情况下要小心。但是C11标准§6.5.3.2规定编译器必须在所有情况下(包括varNULL的情况)将其处理为等效。

因此,当var包含有效地址时,var == &(*var)为真。当它包含其他内容(例如NULL)时,如果您使用非符合C11且非优化的编译器,则可能会崩溃(段错误)。但是大多数编译器,即使是旧版本,也不会生成一些导致崩溃的代码。

但是编译器(即使是旧的 C99 编译器)也允许在 as-if 规则 下将 &(*var) 优化为 var


这是一个非常不同的问题,C标准禁止获取声明为“register”的变量的地址(今天,“register”除了这个之外没有其他的兴趣)。 - Basile Starynkevitch
2
自C11以来(至少如此),&*E在所有情况下(包括E是空指针的情况下)等同于E。请参见6.5.3.2/3。 - M.M
1
我不明白为什么。你天真地认为register关键字与寄存器分配有关。在2018年,register关键字几乎没有用处。 - Basile Starynkevitch
没有解引用,因此不会出现段错误。 - 0___________
我看到在https://github.com/FreeFem/FreeFem-sources/blob/develop/src/medit/inout_popenbinaire.c中。在这种情况下,使用那个是非常危险的。 - SGarnotel
@chqrlie,在我的评论中,E是一个指针。显然,&*3也不等同于3,依此类推。 :) - M.M

0

可能在括号内原本是一个更长的表达式,但在调试过程中已经被简化为这个,并留在源代码中。它没有任何意义。

编辑 - Github 的示例看起来正如我所预期的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接