memcmp из PInvoke в C# не работает должным образом для массивов больше, чем 4x4
Часть PInkove была взята из какого-то SO ответа (извините, я потерял ссылку на оригинал).
Ниже приведена полная программа. Выход false
,
using System;
using System.Runtime.InteropServices;
namespace Memcpy
{
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(Array b1, Array b2, long count);
public static bool CompareArrays(Array b1, Array b2)
{
// Validate buffers are the same length.
// This also ensures that the count does not exceed the length of either buffer.
return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}
static void Main(string[] args)
{
var array1 = new int[,]
{
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
};
var array2 = new int[,]
{
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
};
Console.WriteLine(CompareArrays(array1, array2));
}
}
}
Если я изменю размер массива на 4x4, то вывод получится true
Почему memcmp ведет себя так?
1 ответ
У вашего кода много проблем. Вот что я вижу:
msvcrt.dll
Среда выполнения C++ подвержена изменениям, поэтому любой код, основанный на ней, рискует в будущем сломаться.- Конечный параметр
memcmp
имеет типsize_t
, Это тот же размер, что и указатель, и поэтому отображается наUIntPtr
, Помните, что C#long
имеет ширину 64 бита. - Использование
Array
в a p/invoke в лучшем случае сомнительно. Кто знает, что это за маршалы? Я ожидаю, что это маршалы как указатель на внутреннее представление класса. Это точно не маршал как указатель на начало массива. memcpy
ожидает количество байтов для сравнения, а не длину массива. Вам нужно умножить длину массива на размер элементов, чтобы получить количество байтов.
Чтобы ваша программа работала, вам нужно решить эти проблемы. Оставляя в стороне использование msvcrt, вы можете исправить свой код следующим образом:
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(IntPtr b1, IntPtr b2, UIntPtr count);
public static bool CompareArrays(Array b1, Array b2)
{
if (b1.Length != b2.Length)
return false;
if (!b1.GetType().GetElementType().Equals(b2.GetType().GetElementType()))
return false;
GCHandle gch1 = GCHandle.Alloc(b1, GCHandleType.Pinned);
try
{
GCHandle gch2 = GCHandle.Alloc(b2, GCHandleType.Pinned);
try
{
return memcmp(gch1.AddrOfPinnedObject(), gch2.AddrOfPinnedObject(),
(UIntPtr)(b1.Length * Marshal.SizeOf(b1.GetType().GetElementType()))) == 0;
}
finally
{
gch2.Free();
}
}
finally
{
gch1.Free();
}
}
Этот код, безусловно, не очень эффективен, не говоря уже о том, что он очень хакерский. Я предлагаю вам придерживаться чистого кода.net.