سؤال

أواجه مشكلة صغيرة في استخدام FieldOffset بشكل صحيح مع المصفوفات.الكود أدناه هو مثال حيث لا يعمل بشكل صحيح بالنسبة لي:

[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
    [FieldOffset(0)]
    public byte[] data;

    [FieldOffset(0)]
    public short[] idx16;

    [FieldOffset(0)]
    public int[] idx32;
}

إذا قمت على سبيل المثال بتعيين المصفوفة المسماة "بيانات" على مصفوفة بايت متسلسلة ثم حاولت استرداد البيانات على شكل اختصارات باستخدام الحقل "idx16"، فستظل الفهرسة محاذية باعتبارها بايت [].وهذا يعني أن idx161 جلب البايت الثاني في البيانات، وليس الكلمة الثانية ذات 16 بت (البايت 2 و3).إذا قمت بإجراء معكوس، أقوم بفهرسة الشورتات بدلاً من البايتات، مما يعني أن محاذاة الإزاحة موروثة من البيانات المصدر.سؤالي، هل هناك طريقة للتغلب على هذا؟أعلم أنه يمكنني تعويض قيمة الفهرس عن طريق ضرب حجم العنصر، لكن هل هناك طريقة أخرى؟

هنا هي إجابة وجدتها هنا على StackOverflow، ولكن عند تجربة هذا الرمز، تبين أنه لا يعمل بشكل صحيح.جربته باستخدام اختبار الوحدة في VS باستخدام الكود التالي دون أي نجاح:

[TestMethod()]
public void SumTest() {
    float[] fArr = {2.0f, 0.5f, 0.0f, 1.0f};
    MemoryStream ms = new MemoryStream();
    for (int i = 0; i < fArr.Length; i++) {
        ms.Write(BitConverter.GetBytes(fArr[i]), 0, sizeof(float));
    }
    byte[] buff = ms.ToArray();
    double expected = 3.5f;
    double actual = Sum(buff);
    Assert.AreEqual(expected, actual);
}

شكرا كثيرا مسبقا!

هل كانت مفيدة؟

المحلول

المشكلة هي (حسب ما أستطيع رؤيته) أنك وحدت مراجع من المصفوفات - لذا فإن أي مصفوفة يتم تعيينها أخيرًا سيفوز.بمجرد وجود مصفوفة، فإنها تستخدم المفهرس (وليس إزاحة البايت) - لذلك لا يهم الحجم.

من المحتمل أن تكون طريقة القيام بذلك "بشكل صحيح" (أو بشكل غير صحيح، حسب الحالة) باستخدام تعليمات برمجية غير آمنة - أخذ المؤشر إلى المصفوفة - شيء من هذا القبيل:

    IndexStruct s = new IndexStruct();
    s.data = new byte[] { 1, 0, 0, 0, 1, 1 };

    unsafe
    {
        fixed (short* data = s.idx16)
        {
            Console.WriteLine(data[0]); // should be 1 (little-endian)
            Console.WriteLine(data[1]); // should be 0
            Console.WriteLine(data[2]); // should be 257
        }
    }

بالطبع، لست متأكدًا من أنني أوصي به - ولكن يبدو أن هذا يحقق ما تريد؟

وأتساءل أيضا ما إذا كان يمكنك إسقاط struct بالكامل ومجرد استخدام الوصول غير الآمن إلى ملف byte[] مباشرة:

    byte[] raw = new byte[] { 1, 0, 0, 0, 1, 1 };
    unsafe
    {
        fixed (byte* addr = raw)
        {
            short* s = (short*)addr;
            Console.WriteLine(s[0]); // should be 1
            Console.WriteLine(s[1]); // should be 0
            Console.WriteLine(s[2]); // should be 257
        }
    }

نصائح أخرى

تعرف

وFieldOffset لديك حيث أن كل من عناصر البيانات الخاصة بك هي داخل البنية ..

ومن خلال وضع كل منهم إلى 0، أنت تقول المترجم أنهم جميعا في موقف 0.

والشيء الثاني أراه هو أنك تخلق مجموعة من وحدات البايت، والسراويل، و[إينتس].

وراجع: MSDN StructLayoutAttribute

[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
        [FieldOffset(0)]
        public byte[16] data;

        [FieldOffset(16)]
        public short idx16;

        [FieldOffset(18)]
        public int idx32;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top