我们正在深入研究一些非常古老的C ++ / CLI代码(旧语法.NET Beta),看到类似的东西有点惊讶:
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引入的各种编组方法提供了一种更好的方法,但理解这里实际发生的事情会非常有趣。
谢谢!
答案 0 :(得分: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)
最终作为对printf
的pinvoke调用,因此运行时通过为您编组它有点偷偷摸摸。您仍处于托管运行时,运行时将在需要时将marhsalling作为服务提供。
一些注意事项:
似乎clr!GenericPInvokeCalliHelper
正在x86 .NET 4 Workstation CLR上进行此操作。
以下不起作用
那是因为那是直接的C ++。它没有机会通过编组,因为它不需要。