【问题标题】:C++ uninitialized local variableC++ 未初始化的局部变量
【发布时间】:2014-06-29 00:37:56
【问题描述】:

我有一个函数:

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

我想像这样调用它:

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

我收到一个错误:未初始化的局部变量:对于所有三个参数。在我的脑海中它是这样的:我声明了主要、次要、构建,然后它们被填充到函数中。在调用代码的前三行中已经为它们分配了空间。

我肯定在这里遗漏了一些东西。有人可以帮我解释一下吗?

【问题讨论】:

  • 您将所有三个变量major、minor 和build 作为对函数的非引用传递,而无需初始化它们。由于该函数处理这些变量的值,因此这没有多大意义。

标签: c++


【解决方案1】:

在涉及需要指针参数的函数时,您犯了许多人所犯的错误。

当一个函数需要一个指针作为参数时,这并不意味着你盲目地声明一个指针并将它传递给函数。该函数要求的是 address-of 一个现有的、有效的实体。

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

上面的DWORD是有效的,所做的只是将这些变量的地址传递给函数。

与此相关的另一个错误(不是错误,因为它会给出期望的结果,但仍然是“错误”)是声明一个指针,让它指向某个有效的地方,然后将它传递给功能。换句话说:

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

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

我见过以这种方式编写的代码。这表明程序员对函数需要指针作为参数时的含义没有清晰的理解。程序员错误地认为必须声明一个指针必须,让它指向一个有效的地方,然后传递这个指针。

是的,您可以在没有崩溃的情况下获得结果,但是调用分配器(new / delete)是浪费时间,或者如果不调用分配器,则会创建不必要的指针变量,从而不必要地混淆代码。

所以最简单的方法是上面的第一个示例。只需声明非指针类型,然后传递地址即可。

【讨论】:

  • +1 如果有的话,我认为这个答案最好地解释了 OP 的可能误解是什么!
【解决方案2】:

问题出在:

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

修复:

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 是指向 DWORD 的指针。这三个参数都是输出参数。在 C/C++ 中,这是一种常见用法:如果要从函数返回多个值,则需要将指针(或在 c++ 的情况下也是引用)传递给变量:

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

在您的情况下,您将未初始化的指针按值传递给函数。同理:

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

你有很多方法:

通过引用传递未初始化的指针:

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

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

//...
delete major;

通过引用传递所有参数:

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

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

使用您的 GetOSVersion() 版本,但在开始时使用此答案中的修复

【讨论】:

  • 通过展示一个如何正确初始化这些变量的简单示例,您的答案可以得到很大改善。
  • 是的,我试过了,编译器说:“Cannot convert parameter 1 from PDWORD to PDWORD”
  • @user3718333 这真的是您从编译器得到的错误吗?这似乎很奇怪。你能粘贴确切的代码吗?
  • @David Schwartz 这就是我在调用评论的第一个灰色框中的代码示例时得到的。代码就是那个+函数
  • @user2451677 你正在走向更糟糕的方向。这里不需要指针引用和new()
【解决方案3】:

你可能想要有你的变量声明并像这样调用你的函数

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

您使用指针来引用输出参数,因此这些需要将它们指向有效的内存地址。如上所示,您可以使用 'address-of' (&) 运算符引用这些变量以获取有效指针。


在c++中,你可以通过引用参数来使用,这会让事情变得更清晰

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

无需调用new() 分配器(并为正确的动态内存分配管理而烦恼),上面的任何示例都排在第一位。

【讨论】:

    【解决方案4】:

    那些是指针。它们没有指向您分配的任何内存。它们不会在函数中“填充”,它们习惯于访问(未初始化的)内存。

    【讨论】:

      【解决方案5】:

      您可能收到的不是错误而是警告(但您可能已将编译器配置为将警告视为错误)。

      如果执行,您的程序将出现段错误,因为您正在写入它们指向的内存,但由于它们未初始化,它们包含无效/随机地址。

      可能的解决方案

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

      假设PDWORD 被定义为*DWORD

      不要忘记删除!

      编辑:实际上在堆栈上分配这些更明智 - 请参阅 user2451677 的答案。

      【讨论】:

      • 谢谢@wonce。我在发布之前尝试将它们分配为新的 PDWORD,现在我明白了。真的是一个完整的答案,非常感谢!
      • -1 不,不,不,这是非常糟糕的建议!当然不应该被接受!三思而后行!
      • 这是一个糟糕的解决方案。它给人的印象是指针参数意味着您必须实际声明指针。您不必要地调用分配器。
      猜你喜欢
      • 2012-10-03
      • 2022-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多