2013-12-17 36 views
0

我有以下代碼(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未顯示。

有人可以解釋爲什麼發生這種情況/如何解決它?

編輯:我正在測試一個函數,可以作爲一個獲取/設置功能。我對此很新,而且我失敗了。我只是無法弄清楚這裏到底發生了什麼。

編輯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」嘗試讀取的數據?

+0

驚喜是它的作品。將指針返回到超出範圍的變量是未定義的行爲。 –

+0

發生這種情況是因爲像n.ApplicationVersion(「10.50.136」)這樣的調用是爲了設置版本而設計的 - 你期望'this-> applicationVersion.major = nav-> major; this-> applicationVersion.minor = nav-> minor; this-> applicationVersion.build = nav-> build;'做什麼?如何破解或需要修復?如果你不想設置它,不要打電話。 –

+0

@RetiredNinja:哪個函數/變量的那個? –

回答

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++標準並不要求所有自動變量都存在的「堆棧」統一概念,但大多數常見的實現都是這樣工作的)。

因此,修改堆棧的後續函數也會修改指向C風格的字符串致電toString()。例如,對n.ApplicationVersion()的後續調用可能會損壞str。只要字符串改爲「0.0.0」以外的任何內容,您的第二個消息框就不會顯示,並且以這種方式損壞堆棧並不會花太多時間。根據您的後續編輯


:使你的串級轎車將有些工作中的一員。對toString的任何調用都會重寫此字符串,從而影響保存指向此緩衝區的指針的所有呼叫者。

不過,這比將字符串保存在堆棧上安全得多。此外,只要toString寫入該緩衝區,您就可以定義該字符串何時有效的規則。

+0

值得注意的是,這也可能發生在成員變量上,因爲調用者只是複製指針而不是實際複製字符串(就像@ Mikhail的答案指出的那樣)。 –

+0

我有點困惑,因爲第二次調用toString()不是導致問題的原因。這是第一個MessageBox之後的調用。 –

+0

的確如此。在這種特殊情況下,我認爲中間的'strcmp'因爲字符串被垃圾篡改而失敗。但是你是正確的:如果你想返回一個持久化的字符串,你必須返回那些沒有超出範圍的存儲,並且不會被後面的方法調用修改。 –

1

LPCSTR不是字符串,它只是指向char數組的指針。當你設置新版本時,char數組本身正在改變。這就是爲什麼你立即觀察test2變量的變化。如果您希望不更改此字符串,請將其複製並保存在內部緩衝區中。

相關問題