سؤال

وأنا أكتب مكتبة C عبر منصة ولكن في النهاية أنا عندي خطأ في unittests بلدي، ولكن فقط على أجهزة ويندوز. لقد تتبع المشكلة وجدت انها ذات الصلة إلى المواءمة بين الهياكل (أنا باستخدام صفائف هياكل الاحتفاظ بالبيانات لكائنات مماثلة متعددة). المشكلة هي: memset (sizeof (البنية)) والهياكل وضع أعضاء واحدا تلو الآخر إنتاج مختلفة نتيجة بايت إلى بايت وبالتالي memcmp () بإرجاع "لا يساوي" نتيجة

وهنا هو رمز للتوضيح:

#include <stdio.h>
#include <string.h>

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

S1 s1, s2;

int main()
{
    printf("%d %d\n", sizeof(S1), sizeof(S2));

    memset(&s1, 0xFF, sizeof(S1));
    memset(&s2, 0x00, sizeof(S1));

    s1.a = 0LL; s1.b = 0;

    if (0 == memcmp(&s1, &s2, sizeof(S1)))
        printf("Equal\n");
    else
        printf("Not equal\n");

    return 0;
}

وهذا الرمز مع MSVC 2003 @ ويندوز المنتجات التالية الإخراج:

16 8
Not equal

ولكن نفس الرمز مع دول مجلس التعاون الخليجي 3.3.6 @ لينكس يعمل كما هو متوقع:

12 8
Equal

وهذا يجعل لي وحدة اختبار صعب جدا.

هل أفهم بشكل صحيح أن MSVC يستخدم حجم أكبر نوع الأصلي (طويلة المدى) لتحديد محاذاة إلى هيكل؟

ويمكن لأي شخص أن تعطيني المشورة كيف يمكنني تغيير قانون بلدي لجعله أكثر قوة ضد هذه المشكلة محاذاة غريبة؟ في قانون بلدي الحقيقي أعمل مع صفائف من الهياكل عن طريق المؤشرات العامة لتنفيذ memset / memcmp وأنا عادة لا أعرف بالضبط نوع، وليس لدي سوى sizeof (البنية) قيمة.

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

المحلول

وما قمنا به هو استخدام #pragma حزمة لتحديد كيفية كبيرة يجب أن تكون الكائنات:

#pragma pack(push, 2)

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

#pragma pack(pop)

إذا قمت بذلك، سوف الهياكل يكون نفس الحجم على كل المنابر.

نصائح أخرى

وتوقعاتك وحدة اختبار هو خاطئ. لا ينبغي أن (أو رمز ذلك اختبارات) تفحص بنية وعازلة بايت بواسطة البايت. للحصول على بيانات بايت دقيق رمز يجب خلق منطقة عازلة بايت صراحة على كومة أو على كومة وملء مع مقتطفات من كل عضو. يمكن الحصول على مقتطفات بطريقة مستقلة CPU-endianness باستخدام عملية التحول الحق ضد قيم الأعداد والصب الناتج على نوع بايت مثل (الحرف غير الموقعة).

وراجع للشغل، المقتطف يكتب الماضي S2. هل يمكن تحديد ذلك عن طريق تغيير هذا

memset(&s2, 0x00, sizeof(S1));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S1)))

وعلى هذا،

memset(&s2, 0x00, sizeof(S2));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S2)))

ولكن النتيجة هي من الناحية الفنية "غير معروف" لمحاذاة أعضاء في هياكل غير مترجم محددة.

ودليل مجلس التعاون الخليجي:

<اقتباس فقرة>   

لاحظ أن المحاذاة من أي نوع البنية أو اتحاد معين مطلوب من قبل معيار ISO C أن تكون على الأقل من مضاعفات الكمال من أدنى المضاعف المشترك من التحالفات جميع أعضاء البنية أو اتحاد في المسألة.

وأيضا، وهذا يدخل عادة عنصرا من الحشو (أي حشو بايت لديها بنية الانحياز). يمكنك استخدام #pragma مع وسيطة من packed. ملاحظة #pragmas لسنا وسيلة محمولة للعمل. للأسف، وهذا هو أيضا عن الطريقة الوحيدة للعمل في قضيتك.

والمراجع: هنا دول مجلس التعاون الخليجي على محاذاة الهيكل. محاذاة بنية MSDN .

لاحظ أن هذه ليست "غريبة" مشكلة المحاذاة. اختارت MSVC للتأكد من أن البنية يتم محاذاة على حد 64 بت لأنه يحتوي عضو 64 بت لذلك يضيف بعض الحشو في نهاية البنية لضمان صفائف من هذه الكائنات سيكون محاذاة كل عنصر بشكل صحيح. أنا مندهش فعلا أن دول مجلس التعاون الخليجي لا تفعل الشيء نفسه.

وأنا الغريب ما كنت اختبار وحدة يفعل ذلك يضرب بعقبة مع هذا - أكثر من المواءمة بين أفراد هيكل ليس من الضروري إلا إذا كنت بحاجة لتتناسب مع شكل ملف ثنائي أو بروتوكول الأسلاك أو كنت حقا بحاجة للحد من الذاكرة المستخدمة من قبل هيكل (يستخدم خصوصا في الأنظمة المدمجة). دون أن يعرفوا ما نحاول القيام به في الاختبارات الخاصة بك لا أعتقد يمكن إعطاء اقتراح جيد. التعبئة الهيكل قد يكون حلا، لكنه يأتي في بعض التكاليف - الأداء (خصوصا على منصات غير إنتل) وقابلية (كيفية إعداد البنية التعبئة حتى ويمكن أن تكون مختلفة من مترجم إلى المترجم). هذه قد لا يهم بالنسبة لك، ولكن قد يكون هناك طريقة أفضل لحل المشكلة في حالتك.

ويمكنك إما أن تفعل شيئا من هذا القبيل

#ifdef _MSC_VER
#pragma pack(push, 16)
#endif

/* your struct defs */

#ifdef _MSC_VER
#pragma pack(pop)
#endif

ولإعطاء المحاذاة مترجم التوجيه إجبار

وأو الذهاب إلى خيارات المشروع وتغيير محاذاة البنية الافتراضية [تحت إنشاء رمز]

وهيكل الحشو لقيم 64 بت مختلفة على المجمعين مختلفة. رأيت الخلافات بين حتى بين أهداف مجلس التعاون الخليجي.

ملحوظة أن الحشو صراحة إلى محاذاة 64 بت سوف فقط إخفاء المشكلة. وسوف أعود إذا بدأت الهياكل التعشيش بسذاجة، لأن المجمعين سوف لا يزال يختلفون على محاذاة الطبيعية للهياكل الداخلية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top