C#中从字符串获取char*指针

40

在C#中,是否可以获得string变量的char*形式?

我需要将路径字符串转换为char*以便于使用一些本地Win32函数...

7个回答

62

你肯定可以这样做:

string str = "my string";

unsafe 
{
    fixed (char* p = str)
    {               
        // do some work
    }
}

有一个操作符(char*)绑定到字符串对象中。 然而,输出格式可能与底层的C或其他格式不兼容... 不过这是解析字符串的相当好的方法。 希望对任何阅读此帖子的人有所帮助。


2
这提供了一个指向System.Char元素的指针,它对应于Win32的WCHAR*,但问题要求char * - Tom Blodget
2
@TomBlodget charSystem.Char 的类型别名。 - Drew Noakes
1
@DrewNoakes 确实。我在问题中将 char 解读为本地字符。现在意图对我来说不是很清晰。 - Tom Blodget
当将p传递给本地函数时,这会导致访问冲突。 - hongxu

12
您可以将 StringBuilder 作为 char* 传递。
请访问http://pinvoke.net,以查看该函数的签名是否已经存在。

3
我会授予这个答案,因为你基本上回答了我的问题,但似乎至少对于我所做的事情,只需要使用fixed(char* s = string){...}。 - Kris
28
我尝试使用SecureString(char,int length)方法,但是SecureString(sb, sb.Length)和SecureString((char)sb, sb.Length)都无法正常工作。提示无法将System.Text.StringBuilder转换为char*。 - Despertar
我尝试了这个,但是我得到了“无法将'System.Text.StringBuilder'转换为'char *”。 - Rye bread

9

以下代码段不需要任何不安全的上下文环境(是的,它展示了有关stringGetHashCode方法实现的内部细节,并显示了它与Java中的差异,因为在C#中,hashcode的值没有被缓存,通常表明C#字符串并不像你所学到的那样是绝对不可变的,不能处理Fon Neiman):

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

5
说某个回答是完全错误的是不正确的,这个回答是正确的、高效的,并且在不安全的上下文中没有太多主观性。该回答正是满足问题要求的。通过GCHandle.alloc固定字符串指针不会导致内存泄漏,除非您在底层上下文中以后使用指针时强加了不当的用法。在C#中,整个指针/内存概念被认为是低级/高级话题,如果您使用指针,则意味着您理解整个概念并基本知道自己在做什么。 - Lu4
4
与引用的问题和答案相关,该观点完全正确,但与上述问题无关,没有提到任何人将要存储指针。如果您想要存储在C#中创建的字符串以供以后使用,那么显然您需要在C中复制它,因为这将涉及在C中分配该字符串,从而允许C稍后进行释放。因此,通常意味着您不能永久固定指针,也不应该在C#中创建一个字符串的副本并将其存储在C中,因为C将无法在以后对其进行解除分配。 - Lu4

5
这取决于你想要做什么。当你通过PInvoke调用Win32函数时,你应该只需传递String变量;框架会为你进行所有的封送操作。如果你需要更复杂的操作,请查看Marshal类的Marshal.StringToHGlobalAnsi和其他方法。

4

结合之前两条回答,这取决于您参数的方向。

如果函数只需要一个输入字符串,即const char *,则可以使用System.String(或简单的string)类型的参数。

如果函数填充一个字符串,即char * buffer, int bufferSize,则可以传递System.Text.StringBuilder

在这两种情况下,(自动)封送将为您执行必要的转换。


谢谢您的见解,但我在下面的帖子评论中有一些额外的备注... - Kris
1
谢谢您解释了仅接受字符串和构建字符串的函数之间的区别。每个人都在不断地宣传 StringBuilder,但如果函数不会修改字符串,那似乎并不正确。 - mpen

1

您可以使用Encoding.ASCII.GetBytes从字符串获取byte[]数组。 在C#中,可能可以使用fixed语句将其转换为char*。(这会固定分配的内存,不允许gc移动它-然后您可以创建指向它的指针)。

所以我的答案是肯定的,只要您成功将byte *转换为char *即可。 (在C / C ++中,这不是问题,但是我对C#不太确定)

附言>如果我找到一篇关于此的文章的书签,我将稍后发布一些代码。 我知道我有它的位置..


1
你还应该考虑使用 Encoding.UTF8,因为它可以保留所有的 Unicode 字符。 - asveikau
1
或者,如果要传递给本地Win32函数,最好使用Encoding.Unicode(以及C端的PWSTR)... - asveikau
有趣的是你提到了“fixed”。在研究一个使用反射器发现在线上与Win32实体进行交互的程序集时,我看到了一个接受字符串参数的方法。这个字符串的第一件事就是被 fixed(char* path = (char*)strargument){...}。最终,Char会被分配给一个win32结构(WINTRUST_FILE_INFO.LPCWSTR 参数)。为了教育目的,我自己试着这样做,但却无法成功(编译器说“不能将字符串转换为char”)。我不明白为什么会出现这个编译器错误,原始代码是否直接将字符串赋值给了该结构? - Kris
让我走上正确轨道的东西是:http://msdn.microsoft.com/en-us/library/f58wzh21%28VS.80%29.aspx - Kris
-1:您不需要转换为byte[]。请参考Guillau Pelletier的答案。 - joniba
显示剩余2条评论

0

就像其他帖子指出的那样,fixed (char* p = str)并不真正兼容本地格式,而StringBuilder也并不真正起作用。这是对我有用的:

fixed (byte* p = Encoding.ASCII.GetBytes(str))
{
    native_call((char*)p);
}

p末尾没有\0。那么,本地可能会访问冲突或读取长度大于byte[] .Length。 - Trương Quốc Khánh

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