2013-02-05 65 views
2

變量爭論函數如何將對象傳遞給struct/class? 例如:將對象傳遞給var arg函數

CString a_csName; 
CString a_csAge(_T("20")); 
a_csName.Format(_T("My Age is : %s"),a_csAge); 

這裏CString::Format是一個printf風格可變arguement函數,它接受這個CString對象。這怎麼可能?

+0

使用可變參數模板的CString :: Format? CString是否只包含一個屬性,它是一個指向char的指針? –

+0

void __cdecl CString :: Format(_In_ _Printf_format_string_ PCXSTR pszFormat,...); – surega

+0

http://stackoverflow.com/questions/205529/c-c-passing-variable-number-of-arguments-around –

回答

4

經過MFC代碼的一些研究和調試發現下面,希望它有助於誰面臨着名的靜態代碼分析器錯誤「將struct'CStringT'傳遞給省略號」,這實際上也是我懷疑的根源。

http://www.gimpel.com/html/bugs/bug437.htm

Format函數是一個可變參數函數,取決於存在於第一參量的格式說明符。第一個參數總是一個char *。

它分析格式說明符(%s,%d,%i ...),並根據找到的格式說明符的索引讀取var_arg數組,並直接轉換爲char * if%s或int if% d被指定。

因此,如果一個CString被指定並且相應的格式說明符是%s,那麼對CString對象進行char *的直接強制轉換嘗試。

CString a_csName; 
CString a_csAge(_T("20")); 
a_csName.Format(_T("My Age is : %s"),a_csAge); 
wcout<<a_csName; 

將打印我的年齡是20

CString a_csName; 
CString a_csAge(_T("20")); 
a_csName.Format(_T("My Age is : %d"),a_csAge); 
wcout<<a_csName; 

將打印我的年齡是052134

所以有這背後沒有任何情報。只是一個直接演員。因此,我們通過POD或用戶定義的數據結構,它沒有什麼差別。

+0

你爲什麼回答你自己的問題? –

+1

我研究了一下,調試了一下,發現了這個。因此思想會分享它。 – surega

4

來源:http://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx

  • 您可以爲const char *與LPCTSTR函數參數自由替代CString的對象。

大概CString是沒有虛函數表的一類,並用僅char*類型的屬性。這意味着sizeof(CString) == sizeof(const char*),並且如果您將一個CString重新解析爲const char*,那麼您將得到一個工作const char*

編譯器不應該接受將結構傳遞給函數的可變參數部分。但如果我沒有錯,在GCC中,當你將一個結構傳遞給可變參數時,它的內存被複制(即不使用拷貝構造函數)併發出警告。我猜MSVC在那裏做的是一樣的,然後,Format方法只是假設給定的數據是const char*

讓我給你一個替代的例子。假設你有一個沒有vtable的類,只有成員是int。如果你把它傳遞給一個可變參數,編譯器會複製這個對象的內容,這只是一個int。在函數實現中,你(不知何故)知道你收到了一個int。然後你查詢參數,詢問int。因爲在內存級別上,你的班級與int沒什麼區別,所以事情會「很好」。該函數將訪問該類的int屬性。

+0

那麼這是否意味着Format會接受任何只有const char *成員的結構?還是有一些CString對象的特殊處理? – surega

+0

在代碼(而不是編譯器)級別有CString處理*特殊*處理對象在內存中的佈局非常仔細地管理以實現這種效果。一般來說,你不應該通過可變參數傳遞類對象。如果您絕對*必須*執行此操作,請將指針傳遞給該對象。 –

+0

@surega編輯說明更好 –

2

我最近不得不清除這個Lint錯誤,並偶然發現這個線程。

雖然這是一個有趣的調查正在發生,避免lint警告的最簡單的方式轉換是通過使用CString的方法Get.String()作爲傳遞CString的指針,而不是CString的結構如下

a_csName.Format(_T("My Age is : %d"), a_csAge.GetString()); 
0

讓我開始以不同的方式回答這個問題。

struct Value 
{ 
    int nValue; 
    float fOtherValue; 
}; 

int main() 
{ 
    Value theValue; 
    theValue.nValue = 220; 
    theValue.fOtherValue = 3.14f; 

    printf("Value: %d", theValue); 
} 

此代碼將打印220沒有任何問題。但是,如果你theValue後通過第二個參數,它不會:

printf("Values: %d, %f", theValue, theValue.fOtherValue); 

因爲第一可變參數theValue不符合%d參數指定的大小。因此,不會顯示3.14(可能不會)。我們不要談論如何printf,堆棧上的參數,va_start和類似的東西的工作。

同樣,當我們設計一個String類是這樣的:

struct String 
{ 
    char* pData; 

public: 
    String() 
    { 
     pData = new char[40]; 
     strcpy_s(pData, 40, "Sample"); 
    } 
}; 

而且使用這種方式:

String str; 
printf("%s", str); 

它肯定會工作。

但是參數和調用棧損壞呢?如果一個類的大小(如Value)大於格式(%d)所指定的數據大小(4爲Value)會怎麼樣?那麼,在這種情況下,所設計的類需要確保給定類的大小與printf函數使用的參數大小相同。

我不會提及它的細節,但CString使用內部類CStringData。後一類包含字符串,長度,參考計數等。CString(實際CSimpleStringT)使用此類,但只保留指針char/wchar_t,由CStringData管理。

很大程度上,CString實現爲String類提到(只要數據和sizeof去)。