هل من الآمن استخدام -1 لتعيين كافة البتات على أنها صحيحة؟

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

  •  03-07-2019
  •  | 
  •  

سؤال

لقد رأيت هذا النمط يستخدم كثيرًا في C & C++.

unsigned int flags = -1;  // all bits are true

هل هذه طريقة محمولة جيدة لتحقيق ذلك؟أو يستخدم 0xffffffff أو ~0 أحسن؟

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

المحلول

أنصحك أن تفعل ذلك تمامًا كما أوضحت، لأنه الأكثر مباشرة.التهيئة ل -1 والتي سوف تعمل دائماً, ، مستقلة عن تمثيل الإشارة الفعلي، بينما ~ في بعض الأحيان سيكون لديك سلوك مفاجئ لأنه يجب أن يكون لديك نوع المعامل الصحيح.عندها فقط سوف تحصل على أعلى قيمة لـ unsigned يكتب.

للحصول على مثال لمفاجأة محتملة، فكر في هذا:

unsigned long a = ~0u;

لن يقوم بالضرورة بتخزين نمط يحتوي على جميع البتات 1 فيه a.ولكنه سيقوم أولاً بإنشاء نمط يحتوي على جميع البتات 1 في ملف unsigned int, ، ومن ثم إسنادها إلى a.ماذا يحدث عندما unsigned long يحتوي على عدد أكبر من البتات وهو أنه ليس كل هذه البتات هي 1.

وفكر في هذا، والذي سيفشل في التمثيل المكمل غير الاثنين:

unsigned int a = ~0; // Should have done ~0u !

والسبب في ذلك هو ذلك ~0 يجب أن يعكس كل البتات.عكس ذلك سوف يسفر عن ذلك -1 على آلة مكملة ثنائية (وهي القيمة التي نحتاجها!) لا أَثْمَر -1 على تمثيل آخر.على الآلة المكملة للفرد، ينتج صفرًا.وهكذا، على الجهاز المكمل، سيتم تهيئة ما ورد أعلاه a إلى الصفر.

الشيء الذي يجب أن تفهمه هو أن الأمر كله يتعلق بالقيم - وليس بالبتات.تتم تهيئة المتغير بـ a قيمة.إذا قمت في المُهيئ بتعديل بتات المتغير المستخدم للتهيئة، فسيتم إنشاء القيمة وفقًا لتلك البتات.القيمة التي تحتاجها للتهيئة a إلى أعلى قيمة ممكنة، هو -1 أو UINT_MAX.والثاني يعتمد على نوع a - سوف تحتاج إلى استخدام ULONG_MAX ل unsigned long.ومع ذلك، فإن الأول لن يعتمد على نوعه، وهي طريقة رائعة للحصول على أعلى قيمة.

نحن لا الحديث عما إذا كان -1 يحتوي على كل البتات واحدة (لا تحتوي دائمًا).وكان لا الحديث عما إذا كان ~0 لديه كل بت واحد (لديه، بطبيعة الحال).

ولكن ما نتحدث عنه هو نتيجة التهيئة flags المتغير هو.ومن أجل ذلك، فقط -1 سوف تعمل مع كل نوع وآلة.

نصائح أخرى

  • unsigned int flags = -1; محمول.
  • unsigned int flags = ~0; غير محمول لأنه يعتمد على تمثيل مكتمل.
  • unsigned int flags = 0xffffffff; غير محمول لأنه يفترض 32 بت ints.

إذا كنت تريد ضبط كافة البتات بطريقة يضمنها معيار C، فاستخدم البت الأول.

وبصراحة أعتقد أن كل من FFF هي أكثر قابلية للقراءة. أما بالنسبة للتعليق أن لها النموذج المضاد، إذا كنت تهتم حقا أن يتم تعيين كافة بت / مسح، وأود أن يجادل بأن وربما كنت في حالة حيث كنت تهتم حجم المتغير على أي حال، الأمر الذي يدعو إلى شيء من هذا القبيل دفعة :: uint16_t، وما إلى ذلك.

وهناك طريقة التي تتجنب المشاكل المذكورة هي ببساطة القيام به:

unsigned int flags = 0;
flags = ~flags;

والمحمولة وإلى هذه النقطة.

وأنا لست متأكدا باستخدام صحيح غير الموقعة للاعلام هو فكرة جيدة في المقام الأول في C ++. ماذا عن bitset وما شابه ذلك؟

وstd::numeric_limit<unsigned int>::max() هو أفضل ل0xffffffff يفترض أن صحيح غير الموقعة عدد صحيح 32-بت.

unsigned int flags = -1;  // all bits are true

"هل هذه طريقة جيدة [،] محمولة لإنجاز هذا؟"

محمول؟ نعم.

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

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

unsigned int flags = static_cast<unsigned int>(-1);

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

نصيحتي هي الانتباه إلى نوع الهدف والتأكد من عدم وجود تحويلات ضمنية.على سبيل المثال:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

وكلها الصحيح والأكثر وضوحا الى زملائك المبرمجين

ومع C++ 11:يمكننا ان نستخدم auto لجعل أيًا من هذه الأمور أكثر بساطة:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

أنا أعتبر الصحيح والواضح أفضل من مجرد الصحيح.

وتحويل -1 إلى أي نوع غير موقعة على تكفلها المعايير لينتج في كل منها. استخدام ~0U سيئة عموما منذ 0 ديه اكتب unsigned int ولن ملء جميع البتات من نوع غير موقعة أكبر، إلا إذا كنت صراحة أكتب شيئا مثل ~0ULL. على أنظمة عاقل، وينبغي أن تكون مطابقة ل~0 -1، ولكن منذ معيار يسمح منها، مكملا وعلامة / تمثيلات الحجم، بالمعنى الدقيق للكلمة انها ليست المحمولة.

وبطبيعة الحال لا بأس دائما لكتابة 0xffffffff إذا كنت تعرف أنك تحتاج بالضبط 32 بت، ولكن -1 لديه ميزة أنه سوف يعمل في أي سياق حتى عندما كنت لا تعرف حجم نوع، مثل وحدات الماكرو التي العمل على أنواع متعددة، أو إذا كان حجم نوع يختلف حسب التنفيذ. إذا كنت لا تعرف نوع، طريقة آمنة أخرى للحصول على كل منها هو الحد حدات الماكرو UINT_MAX، ULONG_MAX، ULLONG_MAX، وما إلى ذلك.

وأنا شخصيا دائما استخدام -1. كان دائما يعمل وليس لديك للتفكير في الامر.

نعم. كما ورد في إجابات أخرى، -1 هو الأكثر المحمولة؛ ومع ذلك، فإنه ليس الدلالي جدا ويطلق تحذيرات مترجم.

لحل هذه القضايا، وهذه محاولة المساعد بسيط:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

والاستعمال:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

وطالما لديك #include <limits.h> كأحد الخاص بك يتضمن، يجب عليك استخدام فقط

unsigned int flags = UINT_MAX;

إذا كنت تريد تستحق طويلة من بت، هل يمكن استخدام

unsigned long flags = ULONG_MAX;

ومضمونة هذه القيم لديك كل بت قيمة مجموعة النتائج إلى 1، بغض النظر عن كيفية تنفيذ الأعداد الصحيحة وقعت.

وأنا لن تفعل الشيء -1. إنها بالأحرى غير بديهية (بالنسبة لي على الأقل). تعيين البيانات الموقعة إلى متغير غير موقعة يبدو لمجرد أن يكون انتهاك النظام الطبيعي للأشياء.

في الوضع الخاص بك، أنا دائما استخدام 0xFFFF. (استخدم العدد الصحيح من خ لحجم متغير بالطبع).

[راجع للشغل، وأنا نادرا ما نرى -1 خدعة القيام به في التعليمات البرمجية في العالم الحقيقي.]

وبالإضافة إلى ذلك، إذا كنت تهتم حقا عن بت الفردية في vairable، فإنه سيكون فكرة جيدة للبدء باستخدام عرض ثابت uint8_t، uint16_t، وأنواع uint32_t.

في IA-32 معالجات إنتل فلا بأس أن يكتب 0xFFFFFFFF إلى تسجيل 64 بت والحصول على النتائج المتوقعة. وذلك لأن IA32e (تمديد 64 بت إلى IA32) تدعم فقط immediates 32 بت. في تعليمات 64 بت و<م> تسجيل تمديد immediates 32 بت إلى 64 بت.

وفيما يلي غير المشروع:

mov rax, 0ffffffffffffffffh

وفيما يلي يضع 64 1S في RAX:

mov rax, 0ffffffffh

وفقط للتأكد من اكتمالها، وفيما يلي يضع 32 1S في الجزء السفلي من RAX (الملقب EAX):

mov eax, 0ffffffffh

وفي الواقع لقد كان برامج تفشل عندما أردت أن أكتب 0xffffffff إلى متغير 64 بت وحصلت على 0xffffffffffffffff بدلا من ذلك. في C هذا من شأنه أن يكون:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

والنتيجة هي:

x is 0xffffffffffffffff

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

وانظر الجواب litb لتفسير واضح جدا من هذه القضايا.

وبلدي الخلاف هو أنه، للغاية بالمعنى الدقيق للكلمة، لا توجد ضمانات لكلتا الحالتين. أنا لا أعرف من أي العمارة التي لا تمثل قيمة غير موقعة من "واحد أقل من اثنين إلى قوة عدد البتات" كما وضعت كل بت، ولكن هنا هو ما المعيار يقول فعلا (3.9.1 / 7 زائد ملاحظة 44):

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

ويتولى تمثيل أنواع أساسية تحدد القيم عن طريق استخدام نظام الترقيم الثنائي الخالص. [ملاحظة 44:] تمثيل الموضعية للالأعداد الصحيحة التي تستخدم الأرقام الثنائية 0 و 1، التي القيم التي يمثلها بت المتعاقبة هي المضافة، تبدأ ب 1، ويمكن مضاعفتها السلطة لا يتجزأ المتعاقبة من 2، ربما باستثناء بعض الشيء مع أعلى منصب.

وهذا يترك إمكانية لأحد بت أن يكون أي شيء على الإطلاق.

وعمليا: نعم

ونظريا: لا

و-1 = 0xFFFFFFFF (من أو أيا كان حجم وكثافة العمليات على النظام الأساسي الخاص بك) هو صحيح فقط مع اثنين من الحساب مكمل. في الممارسة العملية، وأنها ستعمل، ولكن هناك آلات إرث هناك (كبيرة IBM، الخ) حيث كنت قد حصلت بدلا علامة قليلا الفعلي من التمثيل تكملة اثنين و. الخاص المقترح ~ 0 حل يجب أن تعمل في كل مكان.

وعلى الرغم من أن 0xFFFF (أو 0xFFFFFFFF، الخ) قد يكون أسهل في القراءة، ويمكن كسر قابلية في التعليمات البرمجية التي من شأنها أن يكون الأمر خلاف ذلك المحمولة. تنظر، على سبيل المثال، روتين مكتبة لحساب عدد العناصر في بنية البيانات لديها بعض البتات مجموعة (بت الدقيقة التي يحددها المتصل). قد يكون روتين الملحد تماما على ما تمثل بت، ولكن لا تزال هناك حاجة أن يكون هناك "مجموعة كل بت" ثابت. في مثل هذه الحالة، -1 سيكون أفضل بكثير من ثابت عرافة لأنها ستعمل مع أي حجم الشيء.

وإمكانية أخرى، إذا تم استخدام قيمة typedef للقناع بت، سيكون لاستخدام ~ (bitMaskType) 0؛ إذا حدث قناع بت أن يكون فقط نوع 16 بت، وهذا التعبير فقط وتعيين 16 بت (حتى لو كان "الباحث" وإلا سيكون 32 بت)، ولكن منذ 16 بت سوف يكون كل المطلوبة، يجب أن تكون الأمور على ما يرام <م> المقدمة أن واحدا يستخدم في الواقع النوع المناسب في التلبيس.

وبالمناسبة، تعبيرا عن شكل longvar &= ~[hex_constant] لها مسكتك سيئة إذا ثابت عرافة كبير جدا لتناسب في int، ولكن سوف تناسب في unsigned int. إذا كان int هو 16 بت، ثم longvar &= ~0x4000; أو longvar &= ~0x10000. سوف مسح بت واحد من longvar، ولكن longvar &= ~0x8000; واضحة للخروج قليلا 15 وجميع البتات فوق ذلك. والقيم التي تناسب في int يكون المشغل تكملة تطبيقها على نوع من int، ولكن النتيجة ستكون علامة تمتد إلى long، ووضع البتات العليا. القيم التي هي كبيرة جدا لunsigned int سيكون المشغل تكملة تطبيق لكتابة long. القيم التي تتراوح ما بين تلك الأحجام، ومع ذلك، سيتم تطبيق مشغل مكملا لكتابة unsigned int، والتي سوف يتم تحويلها إلى النوع long دون تمديد علامة.

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

std::bitset<32> const flags(-1);

وهذا سوف تحتوي دائما على المبلغ المحدد من البتات التي تحتاج إليها. فإنه يبني std::bitset مع كل بت تعيين إلى 1 لنفس الأسباب المذكورة في إجابات أخرى.

وأنها آمنة بالتأكيد، كما -1 سوف يكون دائما مجموعة كل بت المتاحة، لكني أحب ~ 0 أفضل. -1 فقط لا معنى كبير لunsigned int. 0xFF ... ليست جيدة لأنها تعتمد على العرض من نوع.

وأقول:

int x;
memset(&x, 0xFF, sizeof(int));

وهذا سوف دائما تعطيك النتيجة المرجوة.

الاستفادة من حقيقة أن تعيين جميع البتات لواحد من النوع غير الموقع يعادل أخذ أقصى قيمة ممكنة للنوع المحدد،
وتوسيع نطاق السؤال للجميع غير موقعة أنواع الأعداد الصحيحة:

تعيين -1 يعمل لأي غير موقعة نوع عدد صحيح (int غير الموقعة، uint8_t، uint16_t، وما إلى ذلك) لكل من C وC++.

كبديل، بالنسبة لـ C++، يمكنك إما:

  1. يشمل <limits> والاستخدام std::numeric_limits< your_type >::max()
  2. اكتب وظيفة قالب مخصص (سيسمح هذا أيضًا بإجراء بعض عمليات التحقق من السلامة العقلية، على سبيل المثال.إذا كان نوع الوجهة هو بالفعل نوع غير موقع)

يمكن أن يكون الغرض إضافة المزيد من الوضوح، مثل التعيين -1 سوف تحتاج دائما إلى بعض التعليقات التوضيحية.

وهناك طريقة لجعل بت معنى أكثر وضوحا وبعد لتجنب تكرار نوع:

const auto flags = static_cast<unsigned int>(-1);

ونعم التمثيل أظهرت جدا الصحيح من ذلك بكثير كما لو كنا نفعل ذلك على العكس من ش سوف تحتاج إلى مشغل لعكس جميع البتات ولكن في هذه الحالة منطق واضح وصريح جدا اذا ما نظرنا الى حجم الأعداد الصحيحة في الجهاز

وعلى سبيل المثال في معظم الآلات عدد صحيح هو 2 بايت = 16 بت أقصى قدر من القيمة التي يمكن أن تعقد هو 2 ^ 16-1 = 65535 2 ^ 16 = 65536

و0٪ 65536 = 0 -1٪ 65536 = 65535 والتي corressponds إلى 1111 ............. (1) ويتم تعيين كافة البتات إلى 1 (إذا اعتبرنا الطبقات بقايا 65536 وزارة الدفاع) ومن هنا كان الكثير التوالي إلى الأمام.

وأعتقد

وإذا ش لا النظر في هذا المفهوم هو تناول الطعام تماما ل[إينتس] غير موقعة، وأنه يعمل فعلا

والتحقق من مجرد جزء البرنامج التالي

وكثافة العمليات الرئيسية () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

و}

والجواب ب = 4294967295 whcih هي -1٪ 2 ^ 32 في 4 الأعداد الصحيحة بايت

وبالتالي فإنه غير صحيح تماما عن الأعداد الصحيحة غير الموقعة

وفي حالة تقرير أي اختلافات Jane: جين

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