سؤال

لدي تطبيق Arduino (في الواقع مكتبة جيدا) له عدد من أعلام الحالة في ذلك - وأصدا أصلا أن أعلنتهم ببساطة كإنهاء (حسنا Uint8_t حتى 8 بت غير موقعة في هذه الحالة). ولكن كان بإمكاني الجمع بينها جميعا في عدد صحيح واحد واستخدام عمليات Bitmask لتعيين الحالة واختبارها.

مثال على السابق:

if (_shift == HIGH)
{
    _shift = LOW;
}
else
{
    _shift = HIGH;
}

مثال على الأخير

#define SHIFT_BIT 0

if (bitRead(_flags, SHIFT_BIT) == HIGH)
{
   bitWrite(_flags, SHIFT_BIT, LOW);
}
else
{
   bitWrite(_flags, SHIFT_BIT, HIGH);
}

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

(أضيفت)

للاكتمال هنا تعريف الأسلاك لأولئك Bitwrite وما إلى ذلك. وحدات الماكرو:

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
هل كانت مفيدة؟

المحلول

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

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

نصائح أخرى

اعتمادا على امتثال مترجم AVR-GCC، الذي أنت غير متأكد، يمكنك القيام بشيء من هذا القبيل والحفاظ على الأشياء لطيفة ونظيفة.

struct flags {
    unsigned int flag1 : 1;  //1 sets the length of the field in bits
    unsigned int flag2 : 4;
}; 

flags data;

data.flag1 = 0;
data.flag2 = 12;

if (data.flag1 == 1)
{
    data.flag1 = 0;
}
else
{
    data.flag1 = 1;
}

إذا كنت تريد أيضا الوصول إلى العلم بالكامل بالكامل، ثم:

union 
{
    struct {
        unsigned int flag1 : 1;  //1 sets the length of the field in bits
        unsigned int flag2 : 4;
    } bits;
    unsigned int val;
} flags;

يمكنك بعد ذلك الوصول إلى بعض مستويات 2 من غير مباشر: variable.bits.flag1<- إرجاع علامة بت واحد أو مع مستوى واحد للحصول على الأعلام الإزانية بأكملها: variable.val <- إرجاع int

يمكن أن يكون أكثر وضوحا إذا قمت بإزالة الحاجة إلى استخدام الثوابت HIGH و LOW, من خلال تقسيم طريقتين. اصنع فقط bitSet و bitClear أساليب. bitSet يحدد قليلا ل HIGH, ، و bitClear يحدد قليلا ل LOW. وبعد ثم يصبح:

#define SHIFT_BIT 0

if (bitRead(_flags, SHIFT_BIT) == HIGH)
{
    bitClear(_flags, SHIFT_BIT);
}
else
{
    bitSet(_flags, SHIFT_BIT);
}

وبالطبع، إذا كان لديك فقط HIGH == 1 و LOW == 0, ، ثم لا تحتاج إلى التحقق ==.

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

وسيلة سيئة للقيام بذلك ستكون استخدام الأرقام "السحرية":

if( _flags | 0x20 ) {  // What does this number mean?
   do_something();
}

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

إذا كنت بحاجة إلى تحسين، يجب أن تعرف ما:

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

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

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

  • يستخدم الإصدار الثاني أقل ذاكرة الوصول العشوائي، خاصة إذا كان لديك الكثير من هذه البتات.

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

إذا كنت تتحدث عن قابلية القراءة، ومجموعات البت ++، فلماذا لا أجد أي شيء std::bitset هناك؟ أنا أفهم أن سباق المبرمج المضمن مريح للغاية مع القمجة الثقيلة، وقد تطورت عميدا لقباؤها الشفافة (الأقنعة، وليس السباقات :)، ولكن بصرف النظر عن الأقنعة والأظطاء، فإن المكتبة القياسية لها حل أنيق للغاية أيضا.

مثال:

#include <bitset>

enum tFlags { c_firstflag, c_secondflag, c_NumberOfFlags };

...

std::bitset<c_NumberOfFlags> bits;

bits.set( c_firstflag );
if( bits.test( c_secondflag ) ) {
  bits.clear();
}

// even has a pretty print function!
std::cout << bits << std::endl;// does a "100101" representation.

لحقول البت، من الأفضل استخدام العمليات المنطقية، حتى تتمكن من القيام:

if (flags & FLAG_SHIFT) {
   flags &= ~FLAG_SHIFT;
} else {
   flags |= FLAG_SHIFT;
}

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

#define bitIsSet(flags,bit) flags | bit
#define bitSet(flags,bit) flags |= bit
#define bitClear(flags,bit) flags &= ~bit

ليس لديك النفقات العامة لوظائف الدعوة، ويصبح التعليمات البرمجية أكثر قابلية للقراءة مرة أخرى.

لم ألعب مع اردوينو (حتى الآن) ولكن قد يكون بالفعل وحدات ماكرو لهذا النوع من الأشياء، وأنا لا أعرف.

أود أن أقول أول شيء يهمني هو: "# Define Shift 0" لماذا لا تستخدم ثابتا بدلا من ماكرو؟ بقدر ما تحصل الكفاءة، يسمح الثابت بتحديد النوع، وبالتالي التأكد مما لا يحتاج إلى أي تحويل بعد ذلك بعد ذلك.

أما بالنسبة لكفاءة تكنيك: - أولا، تخلص من جملة أخرى (لماذا ضبط البتة إلى الأعلى إذا كانت قيمة عالية بالفعل؟ سوف تفعل الوظيفة، أن تكون فعالة وأن تكون قابلة للقراءة.

بالنسبة للتخزين، ل C ++، سأميل إلى استخدام Bitset (جنبا إلى جنب مع Enum).

هل من السهل جدا أن نقول:

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