将托管字符串封送到C#的char指针

4

我在使用C#中的Marshal类进行实验,对这个操作的结果有些困惑:

string someVal = "Hello There";
IntPtr ptS = Marshal.StringToHGlobalAnsi(someVal);
char* ptsPt = (char*)ptS.ToPointer();

在立即窗口查看ptsPt[0]的值为'效',我猜测这与StringToHGlobalAnsi方法将托管的字符视为8位值有关,而它们实际上是16位。但是我不太明白为什么会发生这种情况。 我知道可以通过将其更改为StringToHGlobalUni来解决此问题。但我不明白为什么会这样!祝好。

Unicode、UTF、ASCII、ANSI 格式的区别 - M.kazem Akhgary
尝试使用 byte* ptS(char)(ptS[0]) - Danny Varod
1个回答

5

这是因为在C#中,char是一个16位宽的类型。 StringToHGlobalAnsi将字符串转换为ANSI格式,即每个字符1字节。然后你看一下ptsPt[0],它被解释为包含前两个ANSI字符。

以下是原始字符串在内存中的样子:

00 48 00 65 00 6C 00 6C 00 6F 00 20 ...

这是因为C#字符串存储在UTF-16中,上面的内容是“Hello There”的UTF-16表示方式。 在调用StringToHGlobalAnsi后,会分配一块新的内存,其中包含这些字节:
48 65 6C 6C 6F 20 ...

(and incidentally, this means you should free it with Marshal.FreeHGlobal when you're done). (顺便提一下,当你使用完毕时,应该用Marshal.FreeHGlobal释放它。)
Then, when you get a char* to this, the first char pointed to comprises the bytes 48 65, which due to little endianness really means 0x6548, which stands for the character 效. 然后,当你获得一个指向它的char*时,第一个指向的char包括字节48 65,由于小端模式,它实际上表示的是0x6548,这代表着字符效。

很棒的答案,谢谢!我得到了25928,即0x6548的十进制表示效。但是关于字符串在内存中的样子,我有一个快速的问题 - 为什么C#托管字符需要额外的字节?48 00代表'H'。这是为了存储其他字符吗 - 就像我们之前看到的亚洲符号(抱歉,我不知道它来自哪种语言!)? - William
@William,初步的答案是肯定的,原因是为了扩展单个“char”值可以表示的字符数量。但最终,这只是Unicode v1.*时代认为65536种不同字符足够应付一切的遗留问题。 - Roman Starkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接