在C代码中使用*&或&*有什么原因吗?

8

我遇到了一些使用引用/解引用(或您选择称呼它们的任何内容)运算符*&同时进行的C代码,例如&*foo*&bar。我感到困惑。这样做有什么原因吗?


2
你能给我们展示一个案例吗(不是示例代码)? - Sourav Ghosh
它被用于简单的变量赋值中。 - qsfzy
3个回答

15

是的,它们可以有意义地使用:一个有效的用例是在宏定义中检查宏参数是否符合要求。

&*foo 验证 foo 是否为指针类型(可能是从数组或函数类型进行隐式转换后)。

*&foo 验证 foo 是否为左值表达式。

例如,尽管这个例子非常简单,但它可能会有点滥用宏:

void f(int *);
#define F(x) (f(&*(x)))

void g(int *);
#if A
#define G(x) (g(x), (x)=0)
#elif B
#define G(x) (g(*&(x)))
#endif

void h(int *p, int i) {
  f(p); // okay
  F(p); // still okay, does the same thing as f(p)
  f(i); // typically just a compiler warning
  F(i); // pretty much always a compiler error

  g(p); // okay
  G(p); // okay
  g(p+0); // okay
  G(p+0); // error if A because of the modification
          // should be an error if B even without modification
}

1
请您能否提供更具体的例子?这将有助于更好地理解您的回答。谢谢。 - Stanislav Pankevich
@StanislavPankevich 像这样吗? :) - user743382
非常有帮助,谢谢。你能否为*&foo添加一个例子以验证foo是左值表达式? - Stanislav Pankevich
@StanislavPankevich 我可以创建一个在技术上有效的示例,但我目前没有一个可以立即使用并具有任何好处的示例。我会尝试稍后添加一个示例。 - user743382
希望能有一个。谢谢! - Stanislav Pankevich

2
唯一有意义的原因是停止将表达式视为lvalue。 引用C11,第6.5.3.2章 块引用 运算符&返回其操作数的地址。 如果操作数的类型为"type",则结果的类型为"指向type的指针"。 如果操作数是一元*运算符的结果,则不评估该运算符或&运算符,并且结果就像两个运算符都被省略一样,除了运算符的约束仍然适用,且结果不是lvalue。

4
什么情况下你会想这样做?我从未对自己说过:“我真希望这不是个左值。” - yellowantphil
@yellowantphil 这取决于具体情况,但在进行相等性检查时,它可以帮助检测 === 问题。 - Sourav Ghosh
3
我从来没有见过有人写&*p1 == &*p2 来捕捉那个。你见过吗? - user743382
@hvd 不,我没有,但那仍然是可能的。回应你的问题,我最初就向OP要求了示例。 :) - Sourav Ghosh

0
  1. &*ptr 包含了一个提示,告诉编译器 ptr != NULL(否则 *ptr 将会导致未定义的行为)。

  2. *& 可能只是维护更改的结果,其中辅助指针变量被消除:

    struct {
        int i;
        double
    } x;
    
    void foo() {
        int* ip = &x.i;
        ...
        *ip = 1;
        ...
    }
    

    在上面的代码中直接用 &x.i 替换 ip,将会产生一个 StackOverflow 上的问题:

    struct {
        int i;
        double
    } x;
    
    void foo() {
        ...
        *&x.i = 1;  // <---
        ...
    }
    
  3. 同样地,&* 可能是通过重构更改引入的,该重构替换了 lvalue 为指针。

    重构之前:

    int main() {
        struct sockaddr_in serv_addr;
    
        ...
        bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
        ...
    }
    

    将服务器设置代码提取到自己的函数中后:

    void setup_server(struct sockaddr_in* serv_addr) {
        ...
        bind(sockfd, (struct sockaddr *) &*serv_addr, sizeof(*serv_addr));
        ...
    }
    
    int main() {
        struct sockaddr_in serv_addr;
    
        ...
        setup_server(&serv_addr);
        ...
    }
    

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