Marshal.AllocHGlobal正在分配比预期更多的内存

时间:2019-05-25 18:17:31

标签: c# c++ memory-management

我正在使用Marshal.AllocHGlobal c#方法为IntPtr分配内存,然后复制包含的字节[]:

byte[] clientIpAddr = System.Text.Encoding.ASCII.GetBytes("127.0.0.1")
IntPtr client_ipAddr_p = IntPtr.Zero;
ipAddress_p = Marshal.AllocHGlobal(clientIpAddr.Length);
Marshal.Copy(clientIpAddr, 0, ipAddress_p, clientIpAddr.Length);

然后,我将指针传递给C ++外部库,该库接收我的变量,如下所示:

int open_socket(char *clientIpAddr_p)
{
    <open a socket>
}

根据AllocHGlobal的定义,它最终可以保留比预期更多的内存(我可以保证,因为我现在正遭受痛苦)。问题是我需要IntPtr来具有我所要求的大小,因为以后需要精确的IP来打开套接字。

是否有更好的分配外部内存的方法来确保char *拥有我的正确IP?

1 个答案:

答案 0 :(得分:4)

Marshal.AllocHGlobal 分配比指定数量更多的内存不是问题。您的代码遭受另一个不同且很可能致命的问题。

open_socket(char *clientIpAddr_p)需要一个指向C样式null-terminated string(也称为NUL终止的字符串,零终止的字符串或ASCIIZ)的指针。但是,您的C#代码创建一个缓冲区,并用字符串字符/字节填充该缓冲区,而不会以空字符结尾。在内存(从指针给定的地址开始)中搜索缺少的空字符时,这很可能会使 open_socket 函数崩溃或出现其他异常情况。

可能的解决方法非常简单:为缓冲区分配clientIpAddr.Length + 1,以解决空字符的终止问题。必须明确设置零终止符(一个空字节),因为 Marshal.AllocHGlobal 不会 not 初始化/用空字节填充分配的缓冲区。由于我们正在处理ASCII / ANSI字符串,因此空字符只是一个值为零的字节。

byte[] clientIpAddr = System.Text.Encoding.ASCII.GetBytes("127.0.0.1")
IntPtr client_ipAddr_p = Marshal.AllocHGlobal(clientIpAddr.Length + 1);
Marshal.Copy(clientIpAddr, 0, ipAddress_p, clientIpAddr.Length);
Marshal.WriteByte(client_ipAddr_p, clientIpAddr.Length, 0);


好的,忘记我刚刚编写的代码。 ;-)

为什么?因为有一个甚至更简单的“一站式”解决方案,所以省去了手动分配和填充C样式以null终止的字符串的缓冲区的麻烦:Marshal.StringToHGlobalAnsi方法将转换您的代码。带有IP地址的.NET字符串直接转换为对应的C样式的以N结尾的ANSI字符串。

string ipAddress = "127.0.0.1";
IntPtr client_ipAddr_p = Marshal.StringToHGlobalAnsi(ipAddress);

(除非 open_socket 函数要求 clientIpAddr_p 指向的字符串缓冲区在调用后继续存在,否则在之后使用Marshal.FreeHGlobal方法释放分配的缓冲区不管是否使用 Marshal.AllocHGlobal Marshal.StringToHGlobalAnsi 分配了缓冲区,都将调用本机函数。)