8

I've come across some C code that make use of the reference/dereference (or whatever you choose to call them) operators, * and &, at the same time, like &*foo and *&bar. I'm puzzled by it. Is there any reason to do this?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
qsfzy
  • 554
  • 5
  • 17

3 Answers3

15

Yes, they can be used meaningfully: a valid use case is in macro definitions to check that the macro argument matches the requirements.

&*foo verifies that foo is a pointer type (possibly after an implicit conversion from an array or function type).

*&foo verifies that foo is an lvalue expression.

For example, admittedly an example so simple that it might be a bit of macro abuse:

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
}
2

The only meaningful reason to do so is to stop treating the expression as an lvalue.

Quoting C11, chapter §6.5.3.2

The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
0
  1. &*ptr contains a hint for the compiler that ptr != NULL (since otherwise *ptr would mean undefined behavior).

  2. *& may be simply a result of a maintenance change where an auxiliary pointer variable was eliminated:

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

    Straightforwardly replacing in the above code ip with &x.i results in code that can give birth to such a question on StackOverflow:

    struct {
        int i;
        double
    } x;
    
    void foo() {
        ...
        *&x.i = 1;  // <---
        ...
    }
    
  3. Similarly &* may be introduced by a refactoring change that replaced lvalue with a pointer.

    Before refactoring:

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

    After extracting the server setup code into a function of its own:

    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);
        ...
    }
    
Leon
  • 31,443
  • 4
  • 72
  • 97