سؤال

كيف يمكنك مقارنة حالتين من بنيات المساواة في المعيار C؟

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

المحلول

لا توفر لغة C أي تسهيلات لغوية للقيام بذلك - عليك القيام بذلك بنفسك ومقارنة كل عضو في البنية حسب العضو.

نصائح أخرى

قد تميل إلى استخدامها memcmp(&a, &b, sizeof(struct foo)), ، ولكنها قد لا تعمل في جميع الحالات.قد يضيف المترجم مساحة المخزن المؤقت للمحاذاة إلى البنية، ولا يتم ضمان أن تكون القيم الموجودة في مواقع الذاكرة الموجودة في مساحة المخزن المؤقت أي قيمة معينة.

ولكن، إذا كنت تستخدم calloc أو memset الحجم الكامل للهياكل قبل استخدامها يستطيع افعل أ أجوف مقارنة ب memcmp (إذا كانت البنية الخاصة بك تحتوي على مؤشرات، فسوف تتطابق فقط إذا كان العنوان الذي تشير إليه المؤشرات هو نفسه).

إذا كنت تفعل ذلك كثيرًا، فإنني أقترح كتابة دالة تقارن بين الهيكلين.وبهذه الطريقة، إذا قمت بتغيير البنية، فلن تحتاج إلا إلى تغيير المقارنة في مكان واحد.

أما عن كيفية القيام بذلك....تحتاج إلى مقارنة كل عنصر على حدة

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

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

ما ورد أعلاه سوف يفشل في بنية مثل هذا:

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

يجب عليك استخدام المقارنة بين الأعضاء لتكون آمنًا.

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

http://www.pixelbeat.org/programming/gcc/auto_init.html

@Greg محق في أنه يجب على المرء كتابة وظائف مقارنة صريحة في الحالة العامة.

من الممكن استخدامها memcmp لو:

  • لا تحتوي البنيات على أي حقول فاصلة عائمة محتملة NaN.
  • لا تحتوي الهياكل على حشوة (استخدام -Wpadded مع clang للتحقق من ذلك) أو تتم تهيئة البنيات بشكل صريح باستخدام memset عند التهيئة.
  • لا توجد أنواع أعضاء (مثل Windows BOOL) التي لها قيم مميزة ولكن متكافئة.

ما لم تكن تبرمج للأنظمة المدمجة (أو تكتب مكتبة يمكن استخدامها عليها)، فلن أقلق بشأن بعض الحالات الأساسية في معيار C.القريب مقابل.لا يوجد تمييز للمؤشر البعيد على أي جهاز 32 أو 64 بت.لا يوجد نظام غير مضمن أعرفه به عدة NULL مؤشرات.

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

لم أر مثل هذه المكتبة لتوليد التعليمات البرمجية.ومع ذلك، يبدو الأمر بسيطًا نسبيًا.

ومع ذلك، فإن هذا هو الحال أيضًا، حيث أن وظائف المساواة المولدة هذه غالبًا ما تؤدي إلى حدوث خطأ على مستوى التطبيق.على سبيل المثال، ينبغي أن اثنين UNICODE_STRING هل يمكن مقارنة الهياكل في Windows بشكل سطحي أو عميق؟

يعتمد ذلك على ما إذا كان السؤال الذي تطرحه هو:

  1. هل هذين البنيتين نفس الكائن؟
  2. هل لديهم نفس القيمة؟

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

في الحالة الخاصة التي لا تحتوي فيها البنيات على مؤشرات، يمكنك إجراء memcmp لإجراء مقارنة بت للبيانات الموجودة في كل منها دون الحاجة إلى معرفة معنى البيانات.

تأكد من أنك تعرف ما تعنيه كلمة "يساوي" لكل عضو - وهو أمر واضح بالنسبة إلى ints ولكنه أكثر دقة عندما يتعلق الأمر بقيم الفاصلة العائمة أو الأنواع المحددة من قبل المستخدم.

memcmp لا يقارن الهيكل، memcmp يقارن الثنائي، وهناك دائمًا بيانات غير صحيحة في البنية، لذلك تظهر دائمًا خطأ في المقارنة.

قارن عنصرًا بعنصر فهو آمن ولا يفشل.

إذا كانت الهياكل تحتوي على عناصر أولية فقط أو إذا كنت مهتمًا بالمساواة الصارمة، فيمكنك القيام بشيء مثل هذا:

int my_struct_cmp(const struct my_struct * lhs, const struct my_struct * rhs)
{
    return memcmp(lhs, rsh, sizeof(struct my_struct));
}

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

ومع ذلك، عليك أن تدرك أنه كان عليك استخدام memset(&a, sizeof(struct my_struct), 1) لإلغاء نطاق ذاكرة الهياكل كجزء من تهيئة ADT.

إذا تمت تهيئة المتغيرين باستخدام calloc أو تم ضبطهما على 0 بواسطة memset حتى تتمكن من مقارنة البنيتين مع memcmp ولا داعي للقلق بشأن البيانات المهملة للبنية وسيسمح لك ذلك بكسب الوقت

يستخدم هذا المثال المتوافق ملحق برنامج التحويل البرمجي #pragma pack من Microsoft Visual Studio لضمان تعبئة أعضاء البنية بإحكام قدر الإمكان:

#include <string.h>

#pragma pack(push, 1)
struct s {
  char c;
  int i;
  char buffer[13];
};
#pragma pack(pop)

void compare(const struct s *left, const struct s *right) { 
  if (0 == memcmp(left, right, sizeof(struct s))) {
    /* ... */
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top