C++ uninitialized local variable

Solution 1:

You are making the mistake that many make when it comes to functions that require pointer arguments.

When a function requires a pointer as an argument, it doesn't mean you blindly declare a pointer and pass it to the function. What the function is asking for is an address-of an existing, valid, entity.

DWORD major, minor, build;
GetOSVersion(&major, &minor, &build);

The DWORD's above are valid, and all that is done is pass the address of these variables to the function.

The other mistake that is related to this (not a bug, as it will give the desired results, but still a "mistake") is to declare a pointer, have it point to somewhere valid, and then pass it to the function. In other words:

PDWORD major, minor, build;
major = new DWORD;
minor = new DWORD;
build = new DWORD;
GetOSVersion(major, minor, build);
delete major;
delete minor;
delete build;

or

PDWORD pmajor, pminor, pbuild;
DWORD major, minor, build;
pmajor = &major;
pminor = &pminor;
pbuild = &build;
GetOSVersion(pmajor, pminor, pbuild);

I have seen code written in this manner. This indicates that the programmer does not have a clear understanding of what it means when a function requires a pointer as an argument. The programmer erroneously believes that a pointer must be declared, have it point somewhere valid, and then pass this pointer.

Yes, you get the results without crashing, but it is a waste of time to call the allocator (new / delete), or if not calling the allocator, create unnecessary pointer variables which unnecessarily obfuscates the code.

So the easiest way is the first example above. Just declare non-pointer types, and just pass the address.

Solution 2:

the problem is there:

DWORD major;
DWORD minor;
DWORD build;
GetOSVersion(&major, &minor, &build);

Fix:

VOID GetOSVersion(PDWORD major, PDWORD minor, PDWORD build)
{
    OSVERSIONINFO osver = {};
    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&osver);
    if(major)
    *major = osver.dwMajorVersion;
    if(minor)
    *minor = osver.dwMinorVersion;
    if(build)
    *build = osver.dwBuildNumber;
}

DWORD major = 0;
DWORD minor = 0;
DWORD build = 0;
GetOSVersion(&major, &minor, &build);

PDWORD is pointer to DWORD. All three parameters are output parameters. In C/C++, it's a common usage: if you want to return more then one value from a function, you need pass pointer (or reference too in case of c++) to a variable:

int var = 0;
if(some_function_that_can_fail_and_write_result(&var))
 ;//do something

In your case you are passing uninitialized pointer to a function by value. It's the same as:

void foo(int parameter);
// ...
int a;
foo(a);

You have a lot of ways:

Pass uninitialized pointer by reference:

VOID GetOSVersion(PDWORD& major, PDWORD&, PDWORD&)
{
//...
major = new DWORD(osver.dwMajorVersion);

}
// usage:
PDWORD major;
GetOSVersion(major, ....);

//...
delete major;

Pass all parameters by reference:

VOID GetOSVersion(DWORD& major, DWORD&, DWORD&)
{
//...
major = osver.dwMajorVersion;

}
// usage:
DWORD major = 0;
GetOSVersion(major, ....);

Use your version of GetOSVersion(), but with the fix in this answer on the beginning

Solution 3:

You probably meant to have your variable declarations and to call your function like this

DWORD major;
DWORD minor;
DWORD build;
GetOSVersion(&major, &minor, &build);

You use pointers to reference the output parameters, thus these need to point them to valid memory addresses. You can refer to those variables to get a valid pointer using the 'address-of' (&) operator as shown above.


With c++ you can use by reference parameters, which will make things a bit clearer

VOID GetOSVersion(DWORD& major, DWORD& minor, DWORD& build) {
    OSVERSIONINFO osver;
    ZeroMemory(&osver, sizeof(OSVERSIONINFO));
    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&osver);
    // Note there's no check needed if the pointers are valid!
    major = osver.dwMajorVersion;
    minor = osver.dwMinorVersion;
    build = osver.dwBuildNumber;
}

DWORD major;
DWORD minor;
DWORD build;
GetOSVersion(major, minor, build);

No need to call the new() allocator (and bother with correct dynamic memory allocation management) with any of the above samples in 1st place.