简体   繁体   中英

C++ uninitialized local variable

I have a function:

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

And I wanted to invoke it like this:

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

I get an error: uninitialized local variable: for all the three arguments. In my head it went like: I declare major, minor, build, and they get filled in the function. Space is already allocated for them during the first three lines of the invoking code.

I am surely missing something here. Could somebody please explain this for me?

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.

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

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.

Those are pointers. They are not pointing at any memory you have allocated. They do not get "filled" in the function, they get used to access (uninitialized) memory.

You are probably not getting an error but a warning (but you might have configured your complier to treat warnings as errors).

If executed, your program will segfault because you are writing to the memory pointed to by them but as they are uninitialized they contain invalid/random addresses.

Possible solution

PDWORD major = new DWORD;
PDWORD minor = new DWORD;
PDWORD build = new DWORD;

assuming PDWORD is defined to be *DWORD .

Do not forget the deletes!

edit: Actually its much more sensible to allocate these on the stack – see user2451677's answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM