هل هناك طريقة للوصول إلى البتات الفردية مع الاتحاد؟

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

  •  29-09-2019
  •  | 
  •  

سؤال

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

typedef union 
{
    unsigned char status;
    bit bits[8];
}DeviceStatus;

لكن المترجم لا يحب هذا. يبدو أنه لا يمكنك استخدام البتات في الهيكل. إذن ماذا يمكنني أن أفعل بدلاً من ذلك؟

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

المحلول

بالتأكيد ، لكنك تريد بالفعل استخدام بنية لتحديد البتات مثل هذا

typedef union
{
  struct
  {
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
    unsigned char bit8 : 1;
  }u;
  unsigned char status;
}DeviceStatus;

ثم يمكنك الوصول إلى DeviceStatus ds; يمكنك الوصول ds.u.bit1. أيضًا ، سيسمح لك بعض المترجمين فعليًا أن يكون لديك هياكل مجهولة داخل الاتحاد ، بحيث يمكنك الوصول فقط ds.bit1 إذا كنت ommit u من typedef.

نصائح أخرى

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

int bit0 = 1;
int bit1 = 2;
int bit2 = 4;
int bit3 = 8;
int bit4 = 16;
int bit5 = 32;
int bit6 = 64;
int bit7 = 128;

if (status & bit1)
    // whatever...

آخر هو استخدام Bitfields:

struct bits { 
   unsigned bit0 : 1;
   unsigned bit1 : 1;
   unsigned bit2 : 1;
// ...
};

typedef union {
    unsigned char status;
    struct bits bits;
} status_byte;

some_status_byte.status = whatever;
if (status_byte.bits.bit2)
    // whatever...

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

كما ذكرنا بالفعل ، لا يمكنك معالجة الذاكرة أصغر من بايت في C. سأكتب ماكرو:

#define BIT(n) (1 << n)

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

if (status & BIT(1)) {
   // Do something if bit 1 is set
} elseif (~status | BIT(2) {
   // Do something else if bit 2 is cleared
} else  {
   // Set bits 1 and 2
   status |= BIT(1) | BIT(2)
   // Clear bits 0 and 4
   status &= ~(BIT(0) | BIT(4))
   // Toggle bit 5 
   status ^= BIT(5)
}

هذا يمنحك الوصول بالقرب من النظام المقترح ، والذي سيستخدم [] بدلاً من ().

typedef union
{
  unsigned char status;
  struct bitFields
  {
    _Bool bit0 : 1;
    _Bool bit1 : 1;
    _Bool bit2 : 1;
    _Bool bit3 : 1;
    _Bool bit4 : 1;
    _Bool bit5 : 1;
    _Bool bit6 : 1;
    _Bool bit7 : 1;
  } bits;
}DeviceStatus;

أصغر وحدة يمكن معالجتها في C هي دائمًا بايت (تسمى char في ج). لا يمكنك الوصول قليلاً مباشرة. إن أقرب طريقة للوصول إلى البتات هي تحديد نوع البيانات المسمى bitpointer وتحديد بعض الوظائف أو الماكرو لها:

#include <stdbool.h>

typedef struct bitpointer {
    unsigned char *pb; /* pointer to the byte */
    unsigned int bit; /* bit number inside the byte */
} bitpointer;

static inline bool bitpointer_isset(const bitpointer *bp) {
    return (bp->pb & (1 << bp->bit)) != 0;
}

static inline void bitpointer_set(const bitpointer *bp, bool value) {
    unsigned char shifted = (value ? 1 : 0) << bp->bit;
    unsigned char cleared = *bp->pb &~ (1 << bp->bit);
    *(bp->pb) = cleared | shifted;
}

أوصي ضد النقابات لأنها كذلك محدد التنفيذ سواء تم ملء MSB إلى LSB أو LSB-to-MSB (انظر ISO C99 ، 6.7.2.1p10).

يمكنك القيام بذلك عن طريق وضع البتات في بنية داخل الاتحاد ، ولكن قد يعمل أو لا يعمل ، اعتمادًا على تنفيذك. لا يحدد تعريف اللغة بالترتيب الذي سيتم مطابقة البتات المنفصلة مع أجزاء unsigned char; ؛ والأسوأ من ذلك ، أنه لا يضمن حتى أن تتداخل البتات مع unsigned char (قد يقرر المترجم وضع البتات المنفصلة نحو الجانب الأكثر أهمية من الكلمة و unsigned char نحو الجانب الأقل أهمية أو العكس).

التقنية المعتادة في وضعك هي استخدام عمليات bitwise. حدد الثوابت التي سميت على اسم البتات ، على سبيل المثال ،

#define FLAG_BUSY 0x01
#define FLAG_DATA_AVAILABLE 0x02
#define FLAG_TRANSMISSION_IN_PROGRESS 0x04
...
#define FLAG_ERROR 0x80

ثم للقراءة وكتابة البتات الفردية:

if (status & FLAG_BUSY) ... /* test if the device is busy */
status &= ~FLAG_ERROR; /* turn off error flag */
status |= FLAG_TRANSMISSION_IN_PROGRESS /* turn on transmission-in-progress flag */
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top