(C ++)调用函数时变量值的变化

时间:2013-12-17 05:52:29

标签: c++

我有以下代码

NCore.h

#ifndef _NCORE_H_
#define _NCORE_H_

#include <Windows.h>
#include <cstdio>

namespace Neat
{
    class NCore
    {
    private:
        // Structure Definitions
        struct NApplicationVersion
        {
            int major = 0;
            int minor = 0;
            int build = 0;
            LPCSTR toString();
        };

        // Application Variables
        LPCSTR applicationName;
        NApplicationVersion applicationVersion;

    protected:


    public:
        NCore();

        LPCSTR ApplicationName(LPCSTR _applicationName = NULL);
        NApplicationVersion ApplicationVersion(LPCSTR _applicationVersion = NULL);
    };
}

#endif

NCore.cpp

#include "NCore.h"

Neat::NCore::NCore()
{
    this->applicationName = NULL;
}

LPCSTR Neat::NCore::NApplicationVersion::toString()
{
    char str[16];
    memset(&str, 0, sizeof(str));
    sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
    return str;
}

LPCSTR Neat::NCore::ApplicationName(LPCSTR _applicationName)
{
    if (_applicationName)
        this->applicationName = _applicationName;
    return this->applicationName;
}

Neat::NCore::NApplicationVersion Neat::NCore::ApplicationVersion(LPCSTR _applicationVersion)
{
    if (_applicationVersion)
    {
            //I know this isn't needed. I was just testing something.
        Neat::NCore::NApplicationVersion *nav = (Neat::NCore::NApplicationVersion *)malloc(sizeof(Neat::NCore::NApplicationVersion));
        sscanf_s(_applicationVersion, "%i.%i.%i", &nav->major, &nav->minor, &nav->build);
        this->applicationVersion.major = nav->major;
        this->applicationVersion.minor = nav->minor;
        this->applicationVersion.build = nav->build;
            free(nav);
    }
    return this->applicationVersion;
}

的main.cpp

#include <Windows.h>

#include "NCore.h"

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT iCmdShow)
{
    Neat::NCore n;
    n.ApplicationName("test");
    LPCSTR test = n.ApplicationName();

    LPCSTR test2 = n.ApplicationVersion().toString();
    if (strcmp(test2, "0.0.0") == 0)
    {
        MessageBox(NULL, "", "", MB_OK);
    }
    n.ApplicationVersion("10.50.136");
    if (strcmp(test2, "0.0.0") == 0)
    {
        MessageBox(NULL, "", "", MB_OK);
    }
    LPCSTR test3 = n.ApplicationVersion().toString();
    if (test3 == "10.50.136")
        {
            MessageBox(NULL, "", "", MB_OK);
    }

    while (true);
    return 0;
}

我的问题是test2初始化为“0.0.0”并显示第一个MessageBox。但在我调用ApplicationVersion(“10.50.136”)后,它将test2更改为“10.50.136”,并且未显示第二个MessageBox。

有人可以解释为什么会发生这种情况/如何解决?

编辑:我正在测试一个既可以作为get / set函数运行的函数。我对此很新,我通过失败来学习。我无法弄清楚到底出了什么问题。

编辑2:我改变了代码如下......

NCore.h

struct NApplicationVersion
{
    int major = 0;
    int minor = 0;
    int build = 0;
    char asString[16];
    LPCSTR toString();
};

NCore.cpp

LPCSTR Neat::NCore::NApplicationVersion::toString()
{
    memset(this->asString, 0, 15);
    sprintf_s(this->asString, 16, "%i.%i.%i", this->major, this->minor, this->build);
    return this->asString;
}

这可行吗?

根据我的理解,我把变量“str”放在堆栈上。这导致它在内存中没有设置位置(?),当其他调用改变了堆栈时,它们也改变了指针“test2”试图读取的数据?

2 个答案:

答案 0 :(得分:2)

实际上,这很容易。 toString通过返回一个本地分配给函数的数组(str)并使用return超出范围来调用未定义的行为:

LPCSTR Neat::NCore::NApplicationVersion::toString()
{
    char str[16];
    memset(&str, 0, sizeof(str));
    sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
    return str;
}

在大多数常见的C ++实现中,str将在堆栈中。 (C ++标准不需要统一的“堆栈”概念,其中所有自动变量都存在,但大多数常见的实现都是这样的。)

因此,修改堆栈的后续函数也将修改调用toString()所指向的C样式字符串。例如,对n.ApplicationVersion()的后续调用可能会废弃str。只要字符串更改为“0.0.0”以外的任何内容,您的第二个消息框就不会显示,并且以这种方式破坏堆栈也不会花费太多。


根据您的后续编辑:使字符串成为您的类的成员将稍微工作。对toString的任何调用都将重写此字符串,从而影响保存指向此缓冲区的指针的所有调用方。

但是,这肯定比在堆栈上保存字符串更安全。此外,只要toString只写入此缓冲区,就可以明确定义该字符串何时有效的规则。

答案 1 :(得分:1)

LPCSTR不是字符串,它只是指向char数组的指针。设置新版本时,char数组本身正在发生变化。这就是你立即观察test2变量的变化的原因。如果您希望不更改此字符串,请将其复制并保存在内部缓冲区中。