我们目前正在挖掘一些非常旧的C++/CLI代码(旧语法.NET测试版),看到这样的内容有点惊讶:
System::String ^source("Test-String"); printf("%s", source);
程序正确输出
Test-String
我们想知道,为什么可以将托管字符串源传递给 printf -更重要的是: 为什么它有效 ?我不希望它是编译器的一些便利功能,因为以下功能不起作用:
printf
System::String ^source("Test-String"); char pDest[256]; strcpy(pDest, source);
这会产生一个(某种程度上预期的)编译错误,说明 System::String^ 无法转换为 const char* 。因此,我唯一真正的解释是,向va_list传递托管引用超过了所有编译器检查,并诱使本机代码使用指向托管堆的指针。自从 System::String 表示为类似于 char -存储器中的阵列, 输出函数 可能会起作用。或者编译器转换为 pin_ptr 并将其传递给 输出函数 。
System::String^
const char*
System::String
char
输出函数
pin_ptr
我不希望它自动整理 String^ 到 char* ,因为这将导致在没有任何实际内存地址参考的情况下发生错误的内存泄漏。
String^
char*
我们知道这不是一个好的解决方案,而且后来的Visual Studio版本引入的各种编组方法提供了一种更好的方法,但了解这里实际发生的事情会非常有趣。
谢谢
我相信这是因为编译器正在将其转换为以下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 ,所以运行时为您编组它有点狡猾。您仍然处于托管运行时中,运行时将在需要时提供marhsalling作为服务。
一些注意事项:
看起来 clr!GenericPInvokeCalliHelper 正在x86.NET 4工作站CLR上执行此提升操作。
clr!GenericPInvokeCalliHelper
以下内容不起作用
这是因为它是直接的C++。它没有机会通过编组,因为不需要它。