0

I came across some code where a pointer was used on the same line of its declaration. This is the essential SSCCE for it:

#include "stdafx.h"
#include <iostream>

struct C 
{
    uint32_t a;
};

int main() {
    C* pC = (C*) malloc(sizeof(*pC));     // <---- ???
    pC->a = 42;

    std::cout << pC << std::endl;
    std::cout << pC->a << std::endl;

    free(pC);
}

When I try to do something similar with a uint32 (insert before the free()):

uint32_t a = a + pC->a;
std::cout << a << std::endl;

Then either nothing is printed for this statement, or while debugging a random value is stored in a and VS2015 gives me a runtime warning. Errorlevel after execution is 3. I know this can't work.

Why can I use the pointer? Is it even legal? Why isn't the compiler complaining about such statements? Is the statement split into multiple statements behind the scenes?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Timmos
  • 3,215
  • 2
  • 32
  • 40
  • 2
    The pointer's value is not being used in the SSCE, only its type is being used -- `sizeof(*pC)` is the same thing as `sizeof(C)`. Using something's *value* and using its *type* are very different. – David Schwartz Apr 06 '17 at 14:05
  • 1
    `malloc` and `free`? Are programming C or C++??? – JHBonarius Apr 06 '17 at 14:07
  • C++. As I said, this is not my own code, I only stripped it down to the essential parts to get an SSCCE. I know I should be using new/delete. Also, can the downvoter explain why this wouldn't be a good question? – Timmos Apr 06 '17 at 14:09
  • @DavidSchwartz I understand, the point here is that `pC` is being declared and initialized on that particular line of code, and `pC` is immediately referred as well. I have a Java background, a similar construction would not be possible in Java, hence my question. – Timmos Apr 06 '17 at 14:11
  • 1
    @Timmos It's not being referred really. Only its type is being used. It doesn't have a value yet, but it does have a type. – David Schwartz Apr 06 '17 at 14:16

1 Answers1

3

uint32_t a = a + pC->a; gives you bad results because you are using the value of a before it is initialized which is undefined behavior. For more on this see Why is 'int i = i;' legal?

C* pC = (C*) malloc(sizeof(*pC)); on the other hand is not using the value of pC in the initialization. sizeof, when applied to a named variable, gives you the size of its type. This is an unevaluated operand which means it is not evaluated. So we are not using the value of *pC but instead we are getting the size of what the pointer points to which is C.

One reason you would want to do this is if you changed the type of pC you do not have to change the sizeof part. If you had

C* pC = (C*) malloc(sizeof(C));

Then you need to remember to change sizeof(C) part when changing the type of pC.


Of course all of this can be avoided using a std::unique_ptr like

auto pC = std::make_unique<C>();
                           ^ here is the only place you have to specify the type
Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Very concise yet useful answer with a nice use case. Would polymorphism also be a good use case for prefering `*pC` over `C`? – Timmos Apr 06 '17 at 14:40
  • @Timmos Probably not since you normally have a base class pointer and dereferencing that just gives you the base, the the derived type you want to create. – NathanOliver Apr 06 '17 at 14:44
  • I just found out that sizeof is a compile time operator, so no, polymorphism would not work, because the compiler only sees the base type. – Timmos Apr 06 '17 at 14:59