2014-02-26 314 views
6
CString s = "test"; 
std::string ss = "test"; 

char z[100]; 
sprintf(z, "%s", ss.c_str()); // z = "test" : OK 

char z2[100]; 
sprintf(z2, "%s", ss); // z2 = "(null)" : OK. undefined behavior is expected 

char z3[100]; 
sprintf(z3, "%s", s); // z3 = "test" : How is this possible ?! 

任何人都可以解釋CString如何與sprintf正常工作嗎?sprintf如何使用CString和std :: string

回答

7

它的工作原理是因爲CString類的第一個元素是指向char數組的指針。實際上CString中唯一的字段是指向字符串數組的指針。這個類使用一些技巧來隱藏內部數據(比如字符串長度,保留緩衝區大小等),然後分配一個大緩衝區,然後讓唯一的類指針指向char數組,以便到達那些內部數據字段,它將該指針移動到已知位置抵消。

你應該做的是調用s.GetBuffer(0);或(LPCTSTR);但使用它作爲

sprintf(z2, "%s", ss); 

被allowd由MFC創作者的設計,當然它工作在Windows下它可能會崩潰其他平臺。

[評論後編輯]

你的代碼將是如果不是C風格的強制轉型樣(LPCTSTR)s更安全,你會使用C++投:static_cast<LPCTSTR>(s);。但很快你會發現你的代碼在所有這些static_cast-s中變得很難看,特別是如果你的sprintf有很多參數的話。據我所知(在我看來),就設計而言,C++風格的轉換旨在讓您重新思考您的設計,而不是根本不使用轉換。在你的情況,而不是使用sprintf的,你應該使用std :: wstringstream(假設你使用UNICODE編譯):

#include<sstream> 

std::wostream & operator<< (std::wostream &out, CString const &s) { 
    out << s.GetString(); 
    return out; 
} 

int main(){ 
    CString s = _T("test"); 
    std::wstringstream ss; 
    ss << s; // no cast required, no UB here 
    std::wcout << ss.str(); 
    return 0; 
} 
+1

調用的正確方法是**'CString :: GetString()'**,**不** **'GetBuffer()'。還要注意_C風格的轉換是壞的:請使用** C++風格的轉換**:'static_cast (ss)'。 –

+0

更確切地說,在你的情況下,因爲你正在使用'sprintf()'(而不是像'_stprintf_s()')這樣的基於TCHAR的函數,所以應該使用'static_cast (ss)'。這樣,在Unicode構建(自VS2005以來一直是默認構造),其中'CString'實際上是'CStringW',轉換失敗並且代碼不能編譯,您可以修復它(而不是傳遞錯誤的參數到'sprintf()',並在運行時靜靜地引入一個bug)。 –

+0

'ss'是示例中的'std :: string',ITYM's.GetBuffer(0)'等等。否則就是'ss.c_str()'。 – MSalters

5

CString這種行爲似乎微軟官方支持(它依賴於實施細則CString,它似乎適用於像你引用的那種情況下工作,但將來可能會改變)。

注意MSDN documentation of CStringPCXSTR cast operator寫着:

// If the prototype isn't known or is a va_arg prototype, 
// you must invoke the cast operator explicitly. For example, 
// the va_arg part of a call to swprintf_s() needs the cast: 

swprintf_s(sz, 1024, L"I think that %s!\n", (PCWSTR)strSports); 

其實是投是不好的,因爲它是一個C樣式轉換。我會使用C++風格轉換static_cast<PCWSTR>(string)或僅使用CString::GetString()方法。

MSDN documentation page另一個讀取(重點煤礦):

在可變參數函數中使用的CString對象

某些C函數採用一個可變數目的參數。值得注意的 示例是printf_s。由於聲明這種函數的方式爲 ,因此編譯器無法確定參數的類型,並且 無法確定要對每個參數執行哪個轉換操作。因此,必須使用顯式類型強制轉換當你通過 一個CString對象是需要可變數量的 參數功能。要在可變參數函數中使用CString對象, 明確地將CString轉換爲LPCTSTR字符串,如以下示例中的 所示。

CString kindOfFruit = _T("bananas"); 
int howmany = 25; 
_tprintf_s(_T("You have %d %s\n"), howmany, (LPCTSTR)kindOfFruit);  

同樣,我更喜歡C++ - 風格static_cast<PCTSTR> MSDN文檔中使用的C樣式轉換。或者打電話CString::GetString()就可以了。


另請參閱此博文:Big Brother helps you

而另一個thread on StackOverflow in which this issue is discussed

+0

感謝您對劇組的詳細解釋。 – rmi

相關問題