8

Some source code use the notation &(*var) where var is already a pointer, like int *var = ....

Is there a difference between this two notations ?

Is var != &(*var) ?

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

alk
  • 69,737
  • 10
  • 105
  • 255
SGarnotel
  • 83
  • 5
  • "*notation `&(*var)` where `var` is already a pointer, like `int *var = ...`*" this comparison I do not understand. I see no `&` right to the "*like*". – alk May 19 '18 at 07:27
  • 1
    Related: https://stackoverflow.com/q/7698476/694576 – alk May 19 '18 at 07:31
  • 3
    In the linked code it looks like the person wanted to use the pattern `fread( (unsigned char *)&(OBJECT)` for all of the reads... although exactly why they thought that was a good idea is anybody's guess. I would say it is a bad pattern for featuring a redundant (and harmful in C89) cast. The code also has other bad things like `#define WrdSiz 4`, and not including `stdio.h`, and `while (!feof(stdin))`, and not checking return value of `fread` etc. Basically this is awful code rife with bugs and undefined behaviour, and you shouldn't treat it as learning material – M.M May 19 '18 at 07:52

3 Answers3

10

&(*var) evaluates to the same as var in most circumstances, at compile time, but note some caveats:

  • var should be a valid pointer, although there is not reason for the compiler to generate code to dereference it. Indeed C11 specifies that there is no undefined behavior for &*var even if var is a null pointer.
  • as Pascal Cuoq commented, var should not be a pointer to void, but the C Standard explicitly allows it for this very case... Shocking indeed.
  • if var is an array, with more than one element, var and &*var have a different type and sizeof(var) and sizeof(&(*var)) have a different value: the first is the size of the array while the second is the size of a pointer to the its first element.
  • As commented by Peter, if var is an uninitialised pointer, then evaluating var == &(*var) gives undefined behaviour (since evaluating either side of the == gives undefined behaviour - accessing the value of an uninitialised variable is what gives the undefined behaviour).

The example you link to in the question uses this construction for no purpose whatsoever:

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

The programmer is reading a number of floating point values and an integer from a file redirected into the standard input. The code is awkward and non-portable:

  • the size of float is silently assumed to be 4
  • the size of int is silently assumend to be the same 4 bytes
  • c must be a valid pointer, unless ddim is 0
  • ref must be a valid pointer as fread will try and store 4 bytes at the address it points to, unless the stream is at end of file
  • end of file and read errors are silently ignored and undefined behavior is possible.

The code can be simplified and protected this way:

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

Other portions of this source code show poor or even invalid constructions, you should study this package only for examples of what not to do.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    The standard [says](http://port70.net/~nsz/c/c11/n1570.html#6.5.3.2p3) “If the operand [of &] 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 …”. In the context of the standard, “constraints” means things that the compiler has to warn about and/or reject at compile-time. The “except that the constraints on the operators still apply” part means that `&*p` should be rejected (or at least warned about) if `p` is a pointer to void, for instance… – Pascal Cuoq May 19 '18 at 07:36
  • 1
    But if `p` is a pointer to `int`, it having to be a valid pointer is not a constraint as the standard defines them, so there is no reason to think that `&*p` is not equivalent to `p` when p is, say, `(int*)0`. – Pascal Cuoq May 19 '18 at 07:37
  • If `var` is an uninitialised pointer, then evaluating `var == &(*var)` gives undefined behaviour (since evaluating either side of the `==` gives undefined behaviour - accessing the value of an uninitialised variable is what gives the undefined behaviour). The point in the third bullet is a consequence of, if `var` is an array, then `var` and and `&(*var)` have different types. – Peter May 19 '18 at 08:13
  • @PascalCuoq There is nothing in C that disallows `*p` (and hence nothing that disallows `&*p`) when `p` has type `void *`, is there? There is such a rule in C++, but C++ is not relevant to this question. –  May 19 '18 at 09:42
  • @hvd You are correct, this does not work at all as an example of a constraint. There is the sentence “An lvalue is an expression (with an object type other than void) that potentially designates an object” in [6.3.2.1:1](https://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p1). This sentence is not under a **Constraints** heading. In addition, even if, when `p` has type `void*`, `*p` is not an lvalue, `&*p` is [explicitly](https://port70.net/~nsz/c/c11/n1570.html#6.5.3.2p1) allowed: https://godbolt.org/g/7r4SRF – Pascal Cuoq May 19 '18 at 14:11
  • @hvd I am shocked by this (for me) discovery. I expected C++'s behavior. – Pascal Cuoq May 19 '18 at 14:19
  • @chqrlie Probably… I don't get that many e-mails, and I just received one. – Pascal Cuoq May 19 '18 at 14:45
5

&(*var) might have had undefined behavior when var (a pointer variable) does not contain a valid address for old versions of C. Be scared in that case. But C11 standard §6.5.3.2 states that the compiler has to handle it as equivalent in all cases (including when var is NULL)

So var == &(*var) is true when var contains a valid address. When it contains something else (e.g. NULL), it might crash (segmentation fault) if you use a non-C11 conforming non-optimizing compiler (but most compilers, even old ones, won't generate some crashing code in that case)

But the compiler (even an old C99 one) is allowed (under as-if rule) to optimize &(*var) into var

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • That is a very different question, and the C standard forbids taking the address of a variable declared `register` (today, `register` has no other interest than this one) – Basile Starynkevitch May 19 '18 at 07:21
  • 2
    Since C11 (at least), `&*E` is equivalent to `E` in all cases (including `E` being a null pointer). See 6.5.3.2/3 – M.M May 19 '18 at 07:22
  • 1
    I don't see why. You are naive in believing that the `register` keyword is related to [register allocation](https://en.wikipedia.org/wiki/Register_allocation). In 2018 the `register` keywoird is nearly useless – Basile Starynkevitch May 19 '18 at 07:23
  • No segfault as there is no dereference there. – 0___________ May 19 '18 at 07:27
  • I see that in https://github.com/FreeFem/FreeFem-sources/blob/develop/src/medit/inout_popenbinaire.c. In this case, it is very dangerous to use that. – SGarnotel May 19 '18 at 07:27
  • @chqrlie in my comment `E` is a pointer . Obviously `&*3` is not equivalent to `3` either, etc. :) – M.M May 19 '18 at 07:28
0

Probably it was a longer expression inside the parentheses but during the debugging has been reduced to this one and left in the source code . It does not make any sense

Edit - the github example it looks exactly as I expected.

0___________
  • 60,014
  • 4
  • 34
  • 74