3

Maybe I confused people with my example. I was trying to understand a part of the code and simplyfied it. Here is part of the original code (simplyfied again... :)) (see original post below).

uint16_t hal_nrf_read_multibyte_reg(uint8_t *pbuf)
{
    uint8_t memtype;

    memtype = *(uint8_t*)(&pbuf);

    if (memtype == 0x00U)
    {
        uint8_t data *buf = (uint8_t data *)pbuf;
        DOTHIS
    }

    if (memtype == 0x01U)
    {
        uint8_t xdata *buf = (uint8_t data *)pbuf;
        DOTHAT
    }

    if (memtype == 0xFEU)
    {
        uint8_t pdata *buf = (uint8_t data *)pbuf;
        DOSOMETHING
    }

    return SOMETHING;
}

void main()
{
    uint8_t payload[3];

    hal_nrf_read_multibyte_reg(payload);

    while(1) { }
}

So I was wondering, why do they cast pbuf which already is of uint8_t. But I think I've got my answer now.

------------ OLD POST -------------

I'm exploring Nordic Semiconductors nRF24LE1.

If I have the following test code.

void tempF(int *test)
{
    int varA;
    int varB;
    int varC;

    varA = *(int*)(&test);   // The way it is done in the source code
    varB = *(&test);
    varC = test;

    printf("A: %x\n", varA);
    printf("B: %x\n", varB);
    printf("C: %x\n", varC);
    printf("C1: %x\n", test);

    if (test == 0x00)
        printf("equals 0x00");
}

int main(void) {
    int myArray[3];

    tempF(myArray);
    return 0;
}

The printfs all give the same reply. What is the reason for doing it "varA-style"? Examples where it is necessary?

If I use the way in varA I don't get the warning "Warning C260: '=': pointer truncation.

RedSmolf
  • 355
  • 3
  • 11

4 Answers4

6

Your three samples are all basically converting a pointer into an int. Technically, this requires a cast in your cases B and C, and your compiler ought to warn you about that. For example:

int varC = (int) test;

With the cast, that is completely valid, but without, not. Nevertheless, your compiler probably produces the same code with or without.

In your example code, however, the type of the expression &test is int **. Casting an expression of that type to int * and dereferencing the result, as is done to assign a value to varA, is intended to have the effect of reinterpreting the bytes of test as those of an int, as with a C++ reinterpret_cast. This does not necessarily produce the same value as converting test directly to an int, as is done to assign a value to varC. They are especially prone to differ if the size of a pointer is not the same as the size of an int on the target system, but they are not required to produce the same result even if the sizes are the same.

On the other hand, applying the * operator directly to the result of the & operator has no net effect, so the value computed for varB will reliably be the same as that computed for varC.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • @ryyker compiles for me with warnings (and with including `stdio.h`) using gcc 4.5.1, but it compiles and runs – Eric Renouf Mar 29 '16 at 14:12
  • 1
    @rykker, as I responded also on your answer, the code compiles for me in GCC 4.4.7 with `gcc -std=c99`, provided that I prepend `#include `. The compiler warns about the implicit pointer conversions in the assignments to `varB` and `varC`, which I also pointed out in my answer. The expression assigned to `varA`, which is the main point, looks fine to me, and GCC has no complaint with it, even with all warnings turned on. What do you think is wrong with it? – John Bollinger Mar 29 '16 at 14:12
  • @JohnBollinger - Yes, thank you I saw your comment below. _What do you think is wrong with it?_. Even with `stdio.h` added and C99 extensions turned on, my compiler fails to compile the original OP code. Is it possible then that GCC compiler is using extensions that are not yet adopted into standard? – ryyker Mar 29 '16 at 14:16
  • @rykker, GCC does not necessarily guarantee to reject programs that rely on extensions to the standard, even with, say, `-std=c99`, so it is possible that it is relying on an extension to compile the program. I still don't see what extension that would be in this case, however, and you have not answered about what *your* compiler says is wrong with it. – John Bollinger Mar 29 '16 at 14:22
  • @rykker, inasmuch as you referred to C99 features as "C99 extensions", I should point out also that the only problem that `gcc -ansi` or the equivalent `gcc -std=c89` have with the code is the C99-style comment. – John Bollinger Mar 29 '16 at 14:25
  • @JohnBollinger - This notation: `int varA = *(int*)(&test);` (where `test` in this case is also an int) is new to me, and interesting. Your explanation is good... _reinterpreting the bytes of test as those of an int_. The _reinterpret_ part is good, and I get why that might be useful, but in this specific example, the argument is already an `int`. What value is there then in _reinterpreting_ it to a new instance of variable that is also an `int`? (I have experimented a little this morning going from one type to another, it all works, but why do it at all if same type?) – ryyker Mar 30 '16 at 14:51
  • By the way, the same technique showed up _[here](http://stackoverflow.com/a/18903944/645128)_ as well. :) – ryyker Mar 30 '16 at 14:55
  • @rykker, no, in this case the argument as presented by the OP is a *pointer* to an `int`. It is your supposition that he meant it to be an `int` instead, and I don't see any support for that. He is reinterpreting the bytes of a pointer as the bytes of an `int`. – John Bollinger Mar 30 '16 at 14:55
  • Oh, I see that, my mistake. Is it valid then to also to apply this technique in a general way... i.e. `typea var = *(typea *)&c;` where c is of `typeb` as long as `sizeof(typeb) <= sizeof(typea)`? (my experiments show that at least it appears to work.) – ryyker Mar 30 '16 at 15:01
  • 1
    @ryyker converting an object pointer to any other object pointer type is allowed, but it can produce undefined behavior (see C2011 6.3.2.3/7). Additionally, without the size constraint you gave, dereferencing such a converted pointer can involve an attempt to access memory that extends beyond the boundary of an object, which also produces UB. And the value one attempts to access that way can be a trap representation. Also, doing anything at all with the result of such a conversion breaks the strict aliasing rule. Overall, I guess that's a highly-qualified "yes". – John Bollinger Mar 30 '16 at 15:21
1

The problem is that any pointer type need not be of same size as an int. The compiler truies to warn you about that fact.

Using (int *)(&test) casts the address of test to be a pointer to int. Dereferencing this yields an int that happily can be assigned to an int variable. It may still be truncated if pointers need more bits than an int can hold, but you convinvced the compiler that you do know what you are doing and it happens by purpose.

rpy
  • 3,953
  • 2
  • 20
  • 31
1

Given that your example varibles are actually int:

int varA;
int varB;
int varC;

Without using the GCC compiler versions 4.4.7 or newer and using stdio.h as noted in comments, the code does not compile, the second two of your statements will error out because of illegal types 'int' and 'pointer to int'

varA = *(int*)(&test);   // The way it is done in the source code
varB = *(&test);//error
varC = test;    //error

If they were int *

int *varA;
int *varB;
int *varC;

Then the first statement: varA = *(int*)(&test); would error out.

The only way the assignment statements will compile is with the variables declared as follows:

int varA;
int *varB;
int *varC;

varA = *(int*)(&test);   // The way it is done in the source code
varB = *(&test);
varC = test;
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 1
    I have a feeling `varA` and others are actually `int*` in OPs actual code. – vgru Mar 29 '16 at 13:46
  • 1
    @Groo, I'm inclined to think not. The point appears to be to convert the pointer value into an `int`. You will see that approach to conversions in a wide variety of C codes. In those cases it is meant to have the same effect that a C++ `reinterpret_cast` does, which is different from the effect of a C cast. – John Bollinger Mar 29 '16 at 13:51
  • @JohnBollinger: you're right, I wanted to delete my comment after looking at the code again, but now I may as well leave it here if others get the same idea. – vgru Mar 29 '16 at 13:55
  • @JohnBollinger - My compiler errors out the way OP code is written, are you able to compile? – ryyker Mar 29 '16 at 13:59
  • 1
    @rykker, if I add `#include ` then the OP's code compiles for me with GCC 4.4.7, but I do get warnings about the assignments to `varB` and `varC`. – John Bollinger Mar 29 '16 at 14:01
0

varA = *(int*)(&test); means interpret the bitwise representation of test as an int, then store it in var.

The cast, (int *), indicates that the bitwise representation of test should be interpreted as an int, while the * operator interprets it as an int.

This is identical to memcpy(&varA, &test, sizeof varA);, if sizeof (int) == sizeof (int *).

nalzok
  • 14,965
  • 21
  • 72
  • 139