2012-08-06 38 views
5

我們目前正在通過一些非常舊的C++/CLI-代碼(舊語法.NET測試版)的挖掘,都感到有點驚訝地看到這樣的事情:爲什麼printf使用受管理的字符串?

System::String ^source("Test-String"); 
printf("%s", source); 

程序正確輸出

Test-String 

我們想知道,爲什麼可以將託管字符串源傳遞到printf - 更重要的是:爲什麼它可以工作?我不希望它是由編譯器一些便利特徵,因爲以下不工作:

System::String ^source("Test-String"); 
char pDest[256]; 
strcpy(pDest, source); 

這將產生一個(不知何故預期)編譯錯誤,指出System::String^不能轉換爲const char*。所以我唯一真正的解釋是,將託管引用傳遞給va_list會超過所有編譯器檢查,並欺騙本地代碼,使用指向託管堆的指針。由於System::String表示類似於char-陣列中的內存,printf可能工作。或者編譯器轉換爲pin_ptr並將其傳遞給printf

我不指望它會自動將String^編組到char*,因爲這會導致內存泄漏不良,而不會引用實際的內存地址。

我們知道這不是一個好的解決方案,而後來由Visual Studio-Versions引入的各種編組方法提供了一種更好的方法,但理解這裏實際發生的事情會非常有趣。

謝謝!

回答

4

我相信這是因爲編譯器把它變成這個IL:

call vararg int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) printf(int8 modopt([mscorlib]System.Runtime.CompilerServices.IsSignUnspecifiedByte) modopt([mscorlib]System.Runtime.CompilerServices.IsConst)*, ..., string) 

爲PInvoke的呼叫printf從而結束了,所以運行時被編組爲你是一個有點鬼鬼祟祟。您仍處於託管運行時,運行時將在需要時提供作爲服務的消息。


一些注意事項:

似乎clr!GenericPInvokeCalliHelper是做在x86 .NET 4 CLR工作站本次解除。

以下不起作用

這是因爲是直的,C++。它沒有機會通過編組,因爲它不是必需的。

+0

所以也許問題是,*爲什麼編譯器會將它轉換爲IL?從什麼時候開始'printf'是一個託管函數?爲什麼是P /調用它?爲什麼它不像其他本地C++函數那樣調用它? – 2012-08-06 23:19:26

+0

@CodyGray我懷疑是因爲編譯器認爲需要首先通過編組器才能看到需要的東西。我會稍微核實並寫一些更全面的內容。 – vcsjones 2012-08-06 23:24:30

+0

感謝迄今爲止的答案!對我來說,strcpy和sprintf之間關於託管字符串的唯一真正區別是sprintf採用變量arg-list而strcpy不能。也許這就是IL代碼的原因。 – Excelcius 2012-08-07 07:40:42