عند زراعة الهيكل مثل Sockaddr_in، Sockaddr_in6 و Addrinfo قبل الاستخدام، وهو الصحيح: Memset، المهجع أو إما؟

StackOverflow https://stackoverflow.com/questions/894300

  •  23-08-2019
  •  | 
  •  

سؤال

كلما أنظر إلى رمز حقيقي أو مثال على رمز المقبس في الكتب، صفحات الإنسان ومواقع الويب، أرى دائما شيئا مثل:

struct sockaddr_in foo;
memset(&foo, 0, sizeof foo); 
/* or bzero(), which POSIX marks as LEGACY, and is not in standard C */
foo.sin_port = htons(42);

بدلا من:

struct sockaddr_in foo = { 0 }; 
/* if at least one member is initialized, all others are set to
   zero (as though they had static storage duration) as per 
   ISO/IEC 9899:1999 6.7.8 Initialization */ 
foo.sin_port = htons(42);

أو:

struct sockaddr_in foo = { .sin_port = htons(42) }; /* New in C99 */

أو:

static struct sockaddr_in foo; 
/* static storage duration will also behave as if 
   all members are explicitly assigned 0 */
foo.sin_port = htons(42);

يمكن العثور على نفس الشيء أيضا لوضع بنية Tradrinfo تلميحات إلى الصفر قبل اجتيازها إلى GetAddrinfo، على سبيل المثال.

لماذا هذا؟ بقدر ما أفهم، من المرجح أن تكون الأمثلة التي لا تستخدم ميمزة ما يعادلها تلك التي لا تفعل ذلك، إن لم يكن أفضل. أدرك أن هناك اختلافات:

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

هل هناك أي من هذه الاختلافات ذات الصلة أو السلوك المطلوب عند تحديد هذه الهياكل إلى الصفر وبالتالي استخدام المهيمة بدلا من الخطأ؟ إذا كان الأمر كذلك، لماذا، وأي مصدر أو مصدر آخر يتحقق من هذا؟

إذا كانت كلاهما صحيحة، فلماذا تميل ميمزتس / بيزرو إلى الظهور بدلا من المهجع؟ هل هو مجرد أسلوب؟ إذا كان الأمر كذلك، فلا بأس بذلك، لا أعتقد أننا بحاجة إلى إجابة ذاتية هي أسلوب أفضل.

ال معتاد تتمثل الممارسة في استخدام المهاجمة في تفضيل المذكرات على وجه التحديد لأن جميع Bits Zero غير مرغوب فيها عادة وبدلا من ذلك، نريد التمثيل الصحيح الصفر للنوع (الأشخاص). هو العكس الحقيقي لهذه الهياكل ذات الصلة المقبس؟

في بحثي، وجدت أن Posix يبدو أنه يتطلب فقط Sockaddr_in6 (وليس Sockaddr_in) لتكون صفرية http://www.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html. ولكن لا يذكر كيف يجب أن تكون صفرية (Memset أو تهيئة؟). أدرك مآخذ BSD مستردة بوسيكس وهي ليست المعيار الوحيد، فهل اعتبارات توافقها للأنظمة القديمة أو أنظمة غير بوسيكس الحديثة؟

شخصيا، أفضل من النمط (وربما الممارسات الجيدة) وجهة نظر لاستخدام المهجع وتجنب memset تماما، لكنني متردد لأن:

  • شفويات المصدر الأخرى والنصوص شبه القانونية مثل برمجة شبكة يونيكس استخدم BZZERO (على سبيل المثال الصفحة 101 على 2nd ed. والصفحات 124 في 3 ed. (امتلك كليهما)).
  • أنا أدرك جيدا أنهم ليسوا متطابقين، لأسباب مقدمة أعلاه.
هل كانت مفيدة؟

المحلول

مشكلة واحدة مع نهج التهيئة الجزئية (أي "{ 0 }") هل سيحذرك دول مجلس التعاون الخليجي من أن المهيذ غير مكتمل (إذا كان مستوى التحذير مرتفعا بما فيه الكفاية؛ عادة ما أستخدمه"-Wall' و غالبا '-Wextra'). من خلال نهج التهيلي المعين، لا ينبغي إعطاء تحذير، لكن C99 لا يزال يستخدم على نطاق واسع - على الرغم من أن هذه الأجزاء متوفرة على نطاق واسع إلى حد ما، إلا، ربما، في عالم مايكروسوفت.

أنا ينزع تستخدم لصالح نهج:

static const struct sockaddr_in zero_sockaddr_in;

تليها:

struct sockaddr_in foo = zero_sockaddr_in;

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


لقد تغيرت دول مجلس التعاون الخليجي مع مرور الوقت

إصدارات دول مجلس التعاون الخليجي 4.4.2 إلى 4.6.0 تولد تحذيرات مختلفة من دول مجلس التعاون الخليجي 4.7.1. على وجه التحديد، يعترف دول مجلس التعاون الخليجي 4.7.1 = { 0 } المهيأة ك "حالة خاصة" ولا تشكو، في حين أن دول مجلس التعاون الخليجي 4.6.0 وما إلى ذلك قد اشتكى.

النظر في الملف init.c:

struct xyz
{
    int x;
    int y;
    int z;
};

struct xyz xyz0;                // No explicit initializer; no warning
struct xyz xyz1 = { 0 };        // Shorthand, recognized by 4.7.1 but not 4.6.0
struct xyz xyz2 = { 0, 0 };     // Missing an initializer; always a warning
struct xyz xyz3 = { 0, 0, 0 };  // Fully initialized; no warning

عند تجميعها مع دول مجلس التعاون الخليجي 4.4.2 (على Mac OS X)، فإن التحذيرات هي:

$ /usr/gcc/v4.4.2/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$

عند تجميعها مع دول مجلس التعاون الخليجي 4.5.1، فإن التحذيرات هي:

$ /usr/gcc/v4.5.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer
init.c:9:8: warning: (near initialization for ‘xyz1.y’)
init.c:10:8: warning: missing initializer
init.c:10:8: warning: (near initialization for ‘xyz2.z’)
$

عند تجميعها مع دول مجلس التعاون الخليجي 4.6.0، فإن التحذيرات هي:

$ /usr/gcc/v4.6.0/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:9:8: warning: (near initialization for ‘xyz1.y’) [-Wmissing-field-initializers]
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$

عند تجميعها مع دول مجلس التعاون الخليجي 4.7.1، تحذيرات هي:

$ /usr/gcc/v4.7.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra  -c init.c
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$

تم تجميع المجمعين أعلاه من قبلي. المبرمون الذين قدموا Apple يعد الاسمية GCC 4.2.1 و Clang:

$ /usr/bin/clang -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:23: warning: missing field 'y' initializer [-Wmissing-field-initializers]
struct xyz xyz1 = { 0 };
                      ^
init.c:10:26: warning: missing field 'z' initializer [-Wmissing-field-initializers]
struct xyz xyz2 = { 0, 0 };
                         ^
2 warnings generated.
$ clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.65) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.4.2
Thread model: posix
$ /usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$ /usr/bin/gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$

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

ليس هناك إجابة واحدة ومجففة ... ربما لم يكن هناك أبدا، وليس هناك الآن. ما زلت تميل إلى استخدام المهيئين، ولكن memset() غالبا ما يكون بديلا صالحا.

نصائح أخرى

"بنية Sockaddr_in Foo = {0}؛" هو صالح فقط لأول مرة، في حين "memets (& foo، 0، sizeof foo)؛" سوف أوضح ذلك في كل مرة تعمل فيها الوظيفة.

أود أن أقول أنه لا صحيح لأنه يجب ألا تخلق كائنات من النوع sockaddr_اي شى نفسك. بدلا من ذلك دائما استخدام getaddrinfo (أو في بعض الأحيان getsockname أو getpeername) للحصول على العناوين.

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

كل واحد صحيح كما أشار الكثير. بالإضافة إلى ذلك، يمكنك تخصيص هذه الهياكل Calloc. التي ترجع بالفعل كتلة الذاكرة صريبة.

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