2012-10-08 15 views
15

हर सी प्रोग्रामर इस प्रसिद्ध मैक्रो के साथ एक सरणी में तत्वों की संख्या निर्धारित कर सकते हैं:मज़बूती से एक सरणी में तत्वों की संख्या का निर्धारण

int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19}; 
printf("%lu\n", NUM_ELEMS(numbers));   // 8, as expected 
:

#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a]) 

यहाँ एक ठेठ उपयोग मामला है

हालांकि, कुछ भी नहीं गलती से एक सरणी के बजाय एक सूचक पारित करने से रोकता है प्रोग्रामर:

int * pointer = numbers; 
printf("%lu\n", NUM_ELEMS(pointer)); 

मेरे सिस्टम पर, यह 2 प्रिंट करता है, क्योंकि स्पष्ट रूप से, एक सूचक एक पूर्णांक के रूप में दोगुना बड़ा होता है। मैं कैसे गलती से एक सूचक पारित करने से प्रोग्रामर को रोकने के लिए के बारे में सोचा है, और मैं एक समाधान मिला:

#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a])) 

यह काम करता है क्योंकि एक सरणी के लिए सूचक अपनी पहली तत्व के सूचक के रूप में एक ही मूल्य है। यदि आप इसके बजाय एक पॉइंटर पास करते हैं, तो पॉइंटर की तुलना एक सूचक के साथ की जाएगी, जो लगभग हमेशा झूठी होती है। (एकमात्र अपवाद एक पुनरावर्ती शून्य सूचक, वह है, एक शून्य सूचक है जो अपने आप को इंगित करता है मुझे लगता है कि के साथ रह सकते हैं।।)

गलती से एक सरणी के बजाय एक सूचक गुजर अब कार्यावधि में एक त्रुटि से चलाता है:

Assertion `(void*)&(pointer) == (void*)(pointer)' failed. 

अच्छा!

  1. अल्पविराम अभिव्यक्ति मान्य मानक सी के बाईं संकार्य के रूप में assert की मेरी उपयोग है: अब मैं सवालों की एक जोड़ी है? यही है, क्या मानक मुझे अभिव्यक्ति के रूप में assert का उपयोग करने की अनुमति देता है? क्षमा करें अगर यह एक मूक सवाल :)

  2. जांच किसी भी तरह संकलन समय पर किया जा सकता है है?

  3. मेरे सी संकलक सोचता है कि int b[NUM_ELEMS(a)]; एक VLA है। अन्यथा उसे मनाने के लिए कोई रास्ता?

  4. मैं पहली बार इस के बारे में सोचना है? यदि हां, तो मैं स्वर्ग में मेरे लिए कितने कुंवारी इंतजार कर सकता हूं? :)

+1

भाग के लिए (4), यह सुनिश्चित है कि यह * 72 * नहीं है। मुझे लगता है कि टी टोपी किसी और चीज के लिए आरक्षित मूल्य है ... –

+0

क्या आपका मतलब है 'आकार [0] '? –

+0

प्रत्येक वास्तविक सी प्रोग्रामर जानता है कि सरणी के आकार का ट्रैक रखना उनकी समस्या है, न कि कंपाइलर्स और "इसे समझने" के लिए ये चाल सख्ती से सीमित उपयोगिता के कारण हैं क्योंकि वह जानकारी केवल दायरे में ही बरकरार है।यदि आप चाहते हैं कि संकलक आपके लिए इसका ख्याल रखे, तो एक स्मार्ट भाषा का उपयोग करें। मेरा मतलब है, आपको केवल सी ++ पर जाना होगा और 'std :: vector' या (C++ 11 के साथ)' std :: array' का उपयोग करना होगा, इसलिए यह एक बड़ा बदलाव नहीं है। – dmckee

उत्तर

9

Is my usage of assert as the left operand of the comma expression valid standard C? That is, does the standard allow me to use assert as an expression?

हाँ, यह वैध है के रूप में अल्पविराम ऑपरेटर के बाईं संकार्य प्रकार void की अभिव्यक्ति हो सकता है अनिश्चित सेक्स के कुंवारी की एक अनिश्चित संख्या।और assert फ़ंक्शन में void है इसके रिटर्न प्रकार के रूप में।

My C compiler thinks that int b[NUM_ELEMS(a)]; is a VLA. Any way to convince him otherwise?

यह मानना ​​है इसलिए क्योंकि एक अल्पविराम अभिव्यक्ति का परिणाम एक निरंतर अभिव्यक्ति नहीं है (e..g, 1, 2 नहीं एक निरंतर अभिव्यक्ति है)।

EDIT1: नीचे दिए गए अद्यतन को जोड़ें।

मैं अपने मैक्रो का एक और संस्करण जो संकलन समय पर काम करता है:

#define NUM_ELEMS(arr)             \ 
(sizeof (struct {int not_an_array:((void*)&(arr) == &(arr)[0]);}) * 0 \ 
    + sizeof (arr)/sizeof (*(arr))) 

और जो स्थिर भंडारण अवधि के साथ वस्तु के लिए प्रारंभकर्ता साथ भी भी काम करने के लिए लगता है। और यह भी int b[NUM_ELEMS(a)]

EDIT2 के अपने उदाहरण के साथ सही ढंग से काम:

@DanielFischer टिप्पणी को संबोधित करने के। साथ gccबिना-pedantic सिर्फ इसलिए gcc स्वीकार करता मैक्रो ऊपर काम करता है:

(void *) &arr == arr 

एक पूर्णांक निरंतर अभिव्यक्ति के रूप में, जबकि यह मानता है

(void *) &ptr == ptr 

एक पूर्णांक निरंतर अभिव्यक्ति नहीं है। सी के अनुसार वे दोनों पूर्णांक निरंतर अभिव्यक्ति नहीं हैं और -pedantic, gcc दोनों मामलों में निदान को सही तरीके से जारी करते हैं।

मेरे ज्ञान के लिए यह NUM_ELEM मैक्रो लिखने के लिए कोई 100% पोर्टेबल तरीका नहीं है। सी में प्रारंभिक निरंतर अभिव्यक्तियों के साथ अधिक लचीला नियम हैं (सी 99 में 6.6 पी 7 देखें) जिसका उपयोग इस मैक्रो को लिखने के लिए किया जा सकता है (उदाहरण के लिए sizeof और यौगिक अक्षर के साथ) लेकिन ब्लॉक-स्कोप सी में प्रारंभिक अभिव्यक्तियों को लगातार अभिव्यक्तियों की आवश्यकता नहीं होती है, इसलिए यह एक ही मैक्रो होना संभव नहीं है जो सभी मामलों में काम करता है।

EDIT3:

मुझे लगता है कि यह लिनक्स कर्नेल एक ARRAY_SIZE मैक्रो (include/linux/kernel.h में) है कि उल्लेख के लायक है कि इस तरह के एक जांच जब विरल (कर्नेल स्थिर विश्लेषण चेकर) निष्पादित किया जाता है लागू करता है।

उनके समाधान पोर्टेबल नहीं है और दो जीएनयू एक्सटेंशन का इस्तेमाल करते हैं:

#define NUM_ELEMS(arr) \ 
(sizeof(struct {int :-!!(__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])));}) \ 
    + sizeof (arr)/sizeof (*(arr))) 
:

  • typeof ऑपरेटर
  • __builtin_types_compatible_p builtin समारोह

मूल रूप से यह ऐसा ही कुछ की तरह लग रहा

+0

बहुत बढ़िया! यदि आप ठीक है तो मैं आपको कुंवारी के आधे हिस्से दूंगा। – fredoverflow

+0

दुर्भाग्यवश, '-pedantic-error' के साथ, मुझे 'त्रुटि: बिट-फ़ील्ड' not_an_array 'चौड़ाई नहीं मिली पूर्णांक निरंतर अभिव्यक्ति [-pedantic] 'gcc से, clang डिफ़ॉल्ट रूप से एक त्रुटि देता है :( –

+0

@DanielFischer मेरा दूसरा देखें संपादित करें कि आपकी टिप्पणी को संबोधित करें – ouah

3
  1. हां। अल्पविराम ऑपरेटर की बाईं अभिव्यक्ति को हमेशा शून्य अभिव्यक्ति के रूप में मूल्यांकन किया जाता है (सी 99 6.5.17 # 2)। चूंकि assert() एक शून्य अभिव्यक्ति है, शुरुआत करने में कोई समस्या नहीं है।
  2. शायद। जबकि सी प्रीप्रोसेसर को प्रकारों और जानवरों के बारे में पता नहीं है और पते की तुलना नहीं कर सकते हैं, आप संकलन समय पर आकार() का मूल्यांकन करने के लिए एक ही चाल का उपयोग कर सकते हैं। एक सरणी घोषित करना जिसमें आयाम एक बूलियन अभिव्यक्ति है। जब 0 यह एक बाधा उल्लंघन है और एक निदान जारी किया जाना चाहिए। मैंने इसे आजमाया है, लेकिन अभी तक सफल नहीं हुआ है ... शायद जवाब वास्तव में "नहीं" है।
  3. संख्या (सूचक प्रकार के) निर्मोक निरंतर भाव पूर्णांक नहीं कर रहे हैं।
  4. शायद नहीं (सूर्य के तहत इन दिनों कोई नई बात नहीं)। :-)
संबंधित मुद्दे