c - बिट अच्छा प्रथाओं में हेरफेर




standards bit (5)

अन्य उत्तर पहले से ही अधिकांश सामान को कवर कर चुके हैं, लेकिन यह उल्लेख करना सार्थक हो सकता है कि भले ही आप गैर-मानक 0 0b सिंटैक्स का उपयोग न कर 0b , आप 1 नंबर को बिट संख्या द्वारा स्थिति में स्थानांतरित करने के लिए पाली का उपयोग कर सकते हैं, अर्थात:

#define DMA_BYTE  (1U << 0)
#define DMA_HW    (1U << 1)
#define DMA_WORD  (1U << 2)
#define DMA_GO    (1U << 3)
// …

ध्यान दें कि अंतिम संख्या दस्तावेज में "बिट संख्या" कॉलम से कैसे मेल खाती है।

बिट्स सेट करने और साफ़ करने का उपयोग नहीं बदलता:

#define DMA_CONTROL_REG DMA_base_ptr[DMA_CONTROL_OFFS]

DMA_CONTROL_REG |= DMA_HW | DMA_WORD;    // set HW and WORD
DMA_CONTROL_REG &= ~(DMA_BYTE | DMA_GO); // clear BYTE and GO

एक शुरुआती सी प्रोग्रामर के रूप में, मैं सोच रहा हूं, एक डिवाइस में नियंत्रण बिट्स स्थापित करने के लिए सबसे अच्छा आसान पढ़ने और समझने में आसान समाधान क्या होगा। क्या कोई मानक हैं ? नकल करने के लिए कोई उदाहरण कोड? Google ने कोई विश्वसनीय उत्तर नहीं दिया।

उदाहरण के लिए, मेरे पास एक कंट्रोल ब्लॉक मैप है:

पहला तरीका जो मैं देख रहा हूँ वह होगा बस जरूरत बिट्स को सेट करना। यह टिप्पणियों में स्पष्टीकरण का एक गुच्छा की आवश्यकता है और लगता है कि यह सब पेशेवर नहीं है।

DMA_base_ptr[DMA_CONTROL_OFFS] = 0b10001100;

दूसरा तरीका मैं देख रहा हूँ कि थोड़ा सा क्षेत्र बनाना है। मुझे यकीन नहीं है कि यह वही होना चाहिए, जिससे मुझे चिपकना चाहिए, क्योंकि मैंने कभी भी इसका इस तरह से उपयोग नहीं किया था (मेरे द्वारा बताए गए पहले विकल्प के विपरीत)।

struct DMA_control_block_struct
{ 
    unsigned int BYTE:1; 
    unsigned int HW:1; 
    // etc
} DMA_control_block_struct;

क्या विकल्पों में से एक दूसरे से बेहतर है? क्या मेरे पास कोई विकल्प नहीं है?

किसी भी सलाह बहुत सराहना की जाएगी


आधुनिक सी संकलक तुच्छ इनलाइन कार्यों को ठीक से संभालते हैं - ओवरहेड के बिना। मैं सभी अमूर्त कार्यों को करूंगा, ताकि उपयोगकर्ता को किसी भी बिट या पूर्णांक में हेरफेर करने की आवश्यकता न हो, और कार्यान्वयन विवरण का दुरुपयोग करने की संभावना न हो।

आप निश्चित रूप से स्थिरांक का उपयोग कर सकते हैं और कार्यान्वयन विवरण के लिए कार्य नहीं कर सकते हैं, लेकिन एपीआई कार्य होना चाहिए। यदि आप एक प्राचीन संकलक का उपयोग कर रहे हैं, तो यह फ़ंक्शन के बजाय मैक्रोज़ का उपयोग करने की भी अनुमति देता है।

उदाहरण के लिए:

#include <stdbool.h>
#include <stdint.h>

typedef union DmaBase {
  volatile uint8_t u8[32];
} DmaBase;
static inline DmaBase *const dma1__base(void) { return (void*)0x12340000; }

// instead of DMA_CONTROL_OFFS
static inline volatile uint8_t *dma_CONTROL(DmaBase *base) { return &(base->u8[12]); }
// instead of constants etc
static inline uint8_t dma__BYTE(void) { return 0x01; }

inline bool dma_BYTE(DmaBase *base) { return *dma_CONTROL(base) & dma__BYTE(); }
inline void dma_set_BYTE(DmaBase *base, bool val) {
  if (val) *dma_CONTROL(base) |= dma__BYTE();
  else *dma_CONTROL(base) &= ~dma__BYTE();
}
inline bool dma1_BYTE(void) { return dma_BYTE(dma1__base()); }
inline void dma1_set_BYTE(bool val) { dma_set_BYTE(dma1__base(), val); }

इस तरह के कोड को मशीन जनरेट किया जाना चाहिए: मैं gsl उपयोग करता हूं (0mq प्रसिद्धि का) एक टेम्पलेट के आधार पर उत्पन्न करने के लिए और कुछ XML इनपुट रजिस्टरों के विवरण को सूचीबद्ध करता है।


जब आप चर को उनके मूल्यों को संग्रहीत करने के लिए घोषित करते हैं, तो आपको ज्ञात डिफ़ॉल्ट मान के लिए बिट्स को इनिशियलाइज़ करना सुनिश्चित करना चाहिए। C , जब आप एक वैरिएबल की घोषणा करते हैं तो आप एक पते पर मेमोरी ब्लॉक कर रहे होते हैं और ब्लॉक का आकार इसके प्रकार पर आधारित होता है। यदि आप वैरिएबल को इनिशियलाइज़ नहीं करते हैं तो आप अपरिभाषित / अनपेक्षित व्यवहार का सामना कर सकते हैं क्योंकि वैरिएबल का मान उस ब्लॉक में मेमोरी के मूल्य / स्थिति जो भी आप घोषित किया गया था, उससे प्रभावित होगा। चर को एक डिफ़ॉल्ट मान से प्रारंभ करके, आप इसकी मौजूदा स्थिति की स्मृति को ब्लॉक कर रहे हैं और इसे ज्ञात स्थिति में डाल रहे हैं।

जहाँ तक पठनीयता है, आपको बिट के मूल्यों को संग्रहीत करने के लिए एक बिट फ़ील्ड का उपयोग करना चाहिए। एक बिट फ़ील्ड आपको एक संरचना में बिट्स के मूल्यों को संग्रहीत करने में सक्षम बनाता है। इससे आपको व्यवस्थित करना आसान हो जाता है क्योंकि आप डॉट नोटेशन का उपयोग कर सकते हैं। साथ ही, आपको यह बताने के लिए कि विभिन्न क्षेत्रों को एक सर्वोत्तम अभ्यास के रूप में उपयोग करने के लिए बिट फ़ील्ड की घोषणा पर टिप्पणी करना सुनिश्चित करना चाहिए। हम उम्मीद करते है कि यह आपके सवाल का जवाब दे देगा। आप C प्रोग्रामिंग के साथ शुभकामनाएँ!


पुराने स्कूल सी रास्ता बिट्स का एक गुच्छा परिभाषित करने के लिए है:

#define WORD  0x04
#define GO    0x08
#define I_EN  0x10
#define LEEN  0x80

फिर आपकी इनिशियलाइज़ेशन बन जाती है

DMA_base_ptr[DMA_CONTROL_OFFS] = WORD | GO | LEEN;

आप व्यक्तिगत बिट्स का उपयोग करके सेट कर सकते हैं | :

DMA_base_ptr[DMA_CONTROL_OFFS] |= I_EN;

आप और ~ का उपयोग करके व्यक्तिगत बिट्स को साफ़ कर सकते हैं:

DMA_base_ptr[DMA_CONTROL_OFFS] &= ~GO;

आप व्यक्तिगत बिट्स का उपयोग करके परीक्षण कर सकते हैं:

if(DMA_base_ptr[DMA_CONTROL_OFFS] & WORD) ...

हालांकि, निश्चित रूप से बिटफिल्ड का उपयोग नहीं करते हैं। उनके पास अपने उपयोग हैं, लेकिन नहीं जब एक बाहरी विनिर्देश परिभाषित करता है कि बिट्स कुछ निश्चित स्थानों पर हैं, जैसा कि मैं मानता हूं कि यहां मामला है।

C FAQ सूची में 20.7 और 2.26 प्रश्न भी देखें।


बिट फ़ील्ड के साथ समस्या यह है कि सी मानक यह निर्धारित नहीं करता है कि जिस क्रम में उन्हें परिभाषित किया गया है वह उसी क्रम के अनुसार है जिसे वे कार्यान्वित करते हैं। तो हो सकता है कि आप उन बिट्स को सेट न करें जो आपको लगता है कि आप हैं।

C मानक राज्यों की धारा 6.7.2.1p11:

एक कार्यान्वयन किसी भी पते योग्य भंडारण इकाई को आवंटित कर सकता है जो कि एक बिट क्षेत्र को रखने के लिए पर्याप्त है। यदि पर्याप्त स्थान रहता है, तो एक बिट-फ़ील्ड जो किसी संरचना में किसी अन्य बिट-फ़ील्ड का तुरंत अनुसरण करता है, उसी यूनिट के आसन्न बिट्स में पैक किया जाएगा। यदि अपर्याप्त स्थान रहता है, तो एक बिट-फ़ील्ड जो फिट नहीं होती है उसे अगली इकाई में डाल दिया जाता है या आसन्न इकाइयों को ओवरलैप किया जाता है जो कार्यान्वयन-परिभाषित होता है। एक इकाई के भीतर बिट-फ़ील्ड्स के आवंटन का आदेश (उच्च-ऑर्डर से कम-ऑर्डर या उच्च-ऑर्डर करने के लिए कम-ऑर्डर) कार्यान्वयन-परिभाषित है। पता योग्य संग्रहण इकाई का संरेखण अनिर्दिष्ट है।

एक उदाहरण के रूप में, struct iphdr की परिभाषा को struct iphdr , जो कि लिनक्स पर /usr/include/netinet/ip.h फ़ाइल फ़ाइल से IP हेडर का प्रतिनिधित्व करता है:

struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;
    ...

आप यहां देख सकते हैं कि बिटफ़िल्ड को कार्यान्वयन के आधार पर एक अलग क्रम में रखा गया है। आपको इस विशिष्ट जाँच का उपयोग नहीं करना चाहिए क्योंकि यह व्यवहार सिस्टम पर निर्भर है। यह इस फाइल के लिए स्वीकार्य है क्योंकि यह सिस्टम का हिस्सा है। अन्य प्रणालियाँ इसे विभिन्न तरीकों से लागू कर सकती हैं।

तो एक बिटफील्ड का उपयोग न करें।

ऐसा करने का सबसे अच्छा तरीका आवश्यक बिट्स सेट करना है। हालाँकि, यह प्रत्येक बिट के लिए नामांकित स्थिरांक को परिभाषित करने और आपके द्वारा सेट किए जाने वाले बिटकॉइन या स्थिरांक का प्रदर्शन करने के लिए समझ में आता है। उदाहरण के लिए:

const uint8_t BIT_BYTE =     0x1;
const uint8_t BIT_HW   =     0x2;
const uint8_t BIT_WORD =     0x4;
const uint8_t BIT_GO   =     0x8;
const uint8_t BIT_I_EN =     0x10;
const uint8_t BIT_REEN =     0x20;
const uint8_t BIT_WEEN =     0x40;
const uint8_t BIT_LEEN =     0x80;

DMA_base_ptr[DMA_CONTROL_OFFS] = BIT_LEEN | BIT_GO | BIT_WORD;




bit