使用fixed关键字固定零长度数组

时间:2012-06-02 23:30:02

标签: c# unsafe

我有一些unsafe C#代码无法更改,并公开了这样的方法:

static unsafe void Foo(
    byte* a, int aLength,
    byte* b, int bLength,
    byte* c, int cLength,
    byte* d, int dLength,
    byte* e, int eLength);

我正在调用这样的方法:

static void Bar(
    byte[] a, int aOffset, int aLength,
    byte[] b, int bOffset, int bLength,
    byte[] c, int cOffset, int cLength,
    byte[] d, int dOffset, int dLength,
    byte[] e, int eOffset, int eLength)
{
    fixed (byte* a_ = &a[aOffset])
    fixed (byte* b_ = &b[bOffset])
    fixed (byte* c_ = &c[cOffset])
    fixed (byte* d_ = &d[dOffset])
    fixed (byte* e_ = &e[eOffset])
    {
        Foo(a_, aLength,
            b_, bLength,
            c_, cLength,
            d_, dLength,
            e_, eLength);
    }
}

(为简洁起见,省略了参数验证。)

除非其中一个字节数组的长度为零,否则这种方法很有效。在这种情况下,我得到一个IndexOutOfRangeException。

  

索引超出了数组的范围。

如何防止异常,最好不要编写大量样板代码而不从fixed转换为其他内容?

不安全的方法不读取或写入长度为零的参数。

3 个答案:

答案 0 :(得分:2)

您无法访问数组中为空的任何元素,因此请使用包含您可以访问的元素的虚拟数组替换任何空数组:

if (aLength == 0) a = new int[1];
if (bLength == 0) b = new int[1];
if (cLength == 0) c = new int[1];
if (dLength == 0) d = new int[1];
if (eLength == 0) e = new int[1];

答案 1 :(得分:0)

static void Bar( ... )
{
  byte[] dummy = new byte[1];
  fixed (byte* a_ = a.Lenght>0 ? &a[aOffset] : &dummy[0])
  ...
}

太笨重了?

修改

仅出于历史原因离开上述地方,?运营商无法工作。

因此,您需要不使用fixed关键字,并执行类似

的操作
public unsafe struct UnsafePointerStruct
{
    public GCHandle gch;
    public byte* addr;
}

static unsafe UnsafePointerStruct GetUnsafePointer(Array a, int ai)
{
    UnsafePointerStruct ups=new UnsafePointerStruct();
    ups.gch=GCHandle.Alloc(a,GCHandleType.Pinned);
    if (a.Length<=ai) ups.addr=(byte*)IntPtr.Zero;
    ups.addr=(byte*)Marshal.UnsafeAddrOfPinnedArrayElement(a,ai);
    return ups;
}

static unsafe void Bar(
    byte[] a, int aOffset, int aLength,
    byte[] b, int bOffset, int bLength,
    byte[] c, int cOffset, int cLength,
    byte[] d, int dOffset, int dLength,
    byte[] e, int eOffset, int eLength)
{
    UnsafePointerStruct upsa=GetUnsafePointer(a,aOffset);
    UnsafePointerStruct upsb=GetUnsafePointer(b,bOffset);
    UnsafePointerStruct upsc=GetUnsafePointer(c,cOffset);
    UnsafePointerStruct upsd=GetUnsafePointer(d,dOffset);
    UnsafePointerStruct upse=GetUnsafePointer(e,eOffset);

    Foo(upsa.addr, aLength,
        upsb.addr, bLength,
        upsc.addr, cLength,
        upsd.addr, dLength,
        upse.addr, eLength);

    upsa.gch.Free();
    upsb.gch.Free();
    upsc.gch.Free();
    upsd.gch.Free();
    upse.gch.Free();
}

答案 2 :(得分:0)

由于它不读取或写入长度为零的数组,因此可以更改

fixed (byte* a_ = &a[aOffset])
fixed (byte* b_ = &b[bOffset])
fixed (byte* c_ = &c[cOffset])
fixed (byte* d_ = &d[dOffset])
fixed (byte* e_ = &e[eOffset])

fixed (byte* a_ = (a.Length == 0 ? (byte*)IntPtr.Zero : &a[aOffset]))
fixed (byte* b_ = (b.Length == 0 ? (byte*)IntPtr.Zero : &b[aOffset]))
fixed (byte* c_ = (c.Length == 0 ? (byte*)IntPtr.Zero : &c[aOffset]))
fixed (byte* d_ = (d.Length == 0 ? (byte*)IntPtr.Zero : &d[aOffset]))
fixed (byte* e_ = (e.Length == 0 ? (byte*)IntPtr.Zero : &e[aOffset]))

但是如果你这样做然后尝试读取或写入其中一个空数组,你将得到一个nullpointer异常。