सी और सी ++ में अलग-अलग व्यवहार करने वाले एनम कॉन्स्टेंट


81

ऐसा क्यों होता है:

#include <stdio.h>
#include <limits.h>
#include <inttypes.h>

int main() {
    enum en_e {
        en_e_foo,
        en_e_bar = UINT64_MAX,
    };
    enum en_e e = en_e_foo;
    printf("%zu\n", sizeof en_e_foo);
    printf("%zu\n", sizeof en_e_bar);
    printf("%zu\n", sizeof e);
}

4 8 8C में और 8 8 8C ++ में (4 बाइट ints वाले प्लेटफॉर्म पर) प्रिंट करें ?

मैं इस धारणा के तहत था कि UINT64_MAXअसाइनमेंट सभी एन्यूमरेशन कॉन्स्टेंट्स को कम से कम 64 बिट्स के लिए मजबूर करेगा, लेकिन en_e_fooसादे सी में 32 पर रहता है।

विसंगति के लिए तर्क क्या है?


1
कौन सा कंपाइलर? मुझे नहीं पता कि इससे कोई फ़र्क पड़ता है, लेकिन हो सकता है।
मार्क रैनसम

@MarkRansom यह gcc के साथ आया था लेकिन क्लैंग समान व्यवहार करता है।
पीएसकोकिक


3
"4 बाइट ints के साथ एक प्लेटफॉर्म पर" यह सिर्फ प्लेटफॉर्म नहीं है, लेकिन कंपाइलर जो कि प्रकार की चौड़ाई निर्धारित करता है। यह सब हो सकता है। (प्रति कीथ के जवाब में, यह वास्तव में नहीं है, लेकिन सामान्य रूप से ऐसी संभावनाओं के बारे में पता होना चाहिए)
लाइटनेस दौड़ ऑर्बिट में

1
@PSkocik: वास्तव में एक बदलाव नहीं है, बस इस सवाल को सी और सी ++ दोनों का एक वैध उपयोग मिला (यह पूछने पर कि कुछ कोड दोनों के बीच भिन्न व्यवहार का कारण बनता है)। इसके अलावा ठीक है: यह पूछते हुए कि C ++ से C पुस्तकालयों को कैसे कॉल किया जाए, और C ++ को कैसे लिखा जाए जिसे C. से बुलाया जा सकता है। बहुत ठीक नहीं है: C सवाल पूछना और "इस पर अधिक नेत्रगोलक" होने पर C ++ टैग को फेंकना। इसके अलावा ठीक नहीं है: C ++ प्रश्न पूछना और उसके बाद "सुनिश्चित करें कि आप C के लिए भी उत्तर दें"। (और सामान्य शिकायतकर्ताओं के लिए - बहुत ठीक नहीं: C ++ टैग को C टैग में बदलना क्योंकि कोड उन मानकों का उपयोग करता है जो दोनों मानकों में मौजूद हैं)
Ben Voigt

जवाबों:


80

C में, एक enumस्थिरांक प्रकार का होता है int। C ++ में, यह एन्यूमरेटेड प्रकार का है।

enum en_e{
    en_e_foo,
    en_e_bar=UINT64_MAX,
};

सी में, यह एक बाधा का उल्लंघन है , निदान की आवश्यकता होती है ( यदि इससे UINT64_MAX अधिक है INT_MAX, जो यह बहुत संभव है)। एसी कंपाइलर कार्यक्रम को पूरी तरह से अस्वीकार कर सकता है, या यह एक चेतावनी प्रिंट कर सकता है और फिर एक निष्पादन योग्य उत्पन्न कर सकता है जिसका व्यवहार अपरिभाषित है। (यह 100% स्पष्ट नहीं है कि एक कार्यक्रम जो एक बाधा का उल्लंघन करता है जरूरी अपरिभाषित व्यवहार है, लेकिन इस मामले में मानक यह नहीं कहता है कि व्यवहार क्या है, इसलिए यह अभी भी अपरिभाषित व्यवहार है।)

gcc 6.2 इस बारे में चेतावनी नहीं देता है। दबंग करता है। यह gcc में एक बग है; यह गलत तरीके से कुछ नैदानिक ​​संदेशों को रोकता है जब मानक हेडर से मैक्रोज़ का उपयोग किया जाता है। बग रिपोर्ट का पता लगाने के लिए ग्रेज़गोरज़ स्ज़ेपकोव्स्की का धन्यवाद: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613

C ++ में, प्रत्येक एन्यूमरेशन प्रकार में एक अंतर्निहित प्रकार होता है , जो कुछ पूर्णांक प्रकार (जरूरी नहीं int) होता है। यह अंतर्निहित प्रकार सभी स्थिर मूल्यों का प्रतिनिधित्व करने में सक्षम होना चाहिए। तो इस मामले में, दोनों en_e_fooऔर en_e_barप्रकार के होते हैं en_e, जो कम से कम 64 बिट्स चौड़ी होनी चाहिए, भले ही intसंकरा है।


10
त्वरित ध्यान दें: UINT64_MAXअधिक से अधिक 65 बिट्स की INT_MAXआवश्यकता नहीं है int
बेन वोइगट

10
वास्तव में अजीब बात यह है कि जीसीसी (5.3.1) के साथ एक चेतावनी का उत्सर्जन करता है है -Wpedanticऔर 18446744073709551615ULLनहीं बल्कि साथ UINT64_MAX
1914 को nwellnhof

4
@ डैस्कैंडी: नहीं, intएक हस्ताक्षरित प्रकार होना चाहिए, इसलिए उसे रीप्लांट UINT64_MAX(2 ** 64-1) करने में सक्षम होने के लिए कम से कम 65 बिट्स होने चाहिए ।
कीथ थॉम्पसन

1
@KeithThompson, 6.7.2.2 का कहना है कि "एक एन्यूमरेटर सूची में पहचानकर्ताओं को ऐसे स्थिरांक के रूप में घोषित किया जाता है, जिनके पास int है और जहां भी अनुमति है, वे प्रकट हो सकते हैं।" मेरी समझ यह है कि एक सी सी एनम की घोषणा करने वाले कॉन्स्टेंट एनम के प्रकार का उपयोग नहीं करते हैं, इसलिए वहां से उन्हें अलग-अलग प्रकार बनाने के लिए एक बड़ा खिंचाव नहीं है (विशेषकर यदि यह मानक के विस्तार के रूप में लागू किया जाता है)।
zneak

2
@AndrewHenle: एनम en_e_barसे बड़ा नहीं है, en_e_fooछोटा है। एनम चर सबसे बड़ा स्थिरांक जितना बड़ा था।
बेन वोइगट

25

वह कोड केवल प्रथम स्थान पर मान्य C नहीं है।

C99 और C11 दोनों में धारा 6.7.2.2 कहती है कि:

प्रतिबंध:

वह अभिव्यक्ति जो expression एन्यूमरेशन स्थिरांक के मान को दर्शाता है वह पूर्णांक स्थिर अभिव्यक्ति होगी जिसका मान a के रूप में प्रतिनिधित्व करने योग्य होता है int

एक कंपाइलर डायग्नोस्टिक अनिवार्य है क्योंकि यह एक बाधा उल्लंघन है, 5.1.1.3 देखें:

एक अनुरूपण कार्यान्वयन कम से कम एक डायग्नोस्टिक संदेश (कार्यान्वयन-परिभाषित तरीके से पहचाना गया) का उत्पादन करेगा यदि प्रीप्रोसेसिंग अनुवाद इकाई या अनुवाद इकाई में किसी भी वाक्यविन्यास नियम या बाधा का उल्लंघन होता है, भले ही व्यवहार स्पष्ट रूप से अपरिष्कृत या कार्यान्वयन के रूप में निर्दिष्ट हो। परिभाषित।


23

में सी है, जबकि एक enumएक अलग प्रकार माना जाता है, प्रगणक ही हमेशा प्रकार है int

C11 - 6.7.2.2 गणन विनिर्देशन

3 एक एन्यूमरेटर सूची में पहचानकर्ता को कॉन्स्टेंट के रूप में घोषित किया गया है जो कि int ...

इस प्रकार, आपके द्वारा देखा गया व्यवहार एक संकलक विस्तार है।

मैं कहूंगा कि अगर इसका मूल्य बहुत बड़ा है तो केवल एक एन्यूमरेटर के आकार का विस्तार करना समझ में आता है।


दूसरी ओर, C ++ में सभी एन्यूमरेटर्स का प्रकार हैenum उनके द्वारा घोषित किए गए हैं।

उसके कारण, प्रत्येक एन्यूमरेटर का आकार समान होना चाहिए। तो, enumसबसे बड़े प्रगणक को संग्रहीत करने के लिए पूरे आकार का विस्तार किया जाता है।


11
यह एक संकलक विस्तार है, लेकिन नैदानिक ​​उत्पन्न करने में विफलता एक गैर-अनुरूपता है।
बेन वोइगट

16

जैसा कि अन्य लोगों ने कहा है, बाधा के कारण कोड (सी में) बीमार है।

जीसीसी बग # 71613 (जून 2016 की रिपोर्ट) है, जिसमें कहा गया है कि मैक्रों के साथ कुछ उपयोगी चेतावनियां खामोश हैं।

सिस्टम हेडर से मैक्रोज़ का उपयोग करने पर उपयोगी चेतावनियों को शांत कर दिया जाता है। उदाहरण के लिए, एक चेतावनी के नीचे के उदाहरण में, दोनों एनमों के लिए उपयोगी होगा, लेकिन केवल एक चेतावनी दिखाई गई है। अन्य चेतावनियों के लिए भी ऐसा ही हो सकता है।

मौजूदा वर्कअराउंड मैक्रो को एक्री +ऑपरेटर के साथ प्रस्तुत करने के लिए हो सकता है :

enum en_e {
   en_e_foo,
   en_e_bar = +UINT64_MAX,
};

जो GCC 4.9.2 के साथ मेरी मशीन पर संकलन त्रुटि देता है:

$ gcc -std=c11 -pedantic-errors -Wall main.c 
main.c: In function ‘main’:
main.c:9:20: error: ISO C restricts enumerator values to range ofint’ [-Wpedantic]
         en_e_bar = +UINT64_MAX

12

C11 - 6.7.2.2/2

एक संकेतन स्थिरांक के मान को परिभाषित करने वाली अभिव्यक्ति पूर्णांक स्थिर अभिव्यक्ति होगी जिसका मान एक के रूप में प्रतिनिधित्व करने योग्य होता है int

en_e_bar=UINT64_MAXएक बाधा उल्लंघन है और यह उपरोक्त कोड को अमान्य बनाता है। C11 ड्राफ्ट में कहा गया है कि कार्यान्वयन की पुष्टि करके एक नैदानिक ​​संदेश का उत्पादन किया जाना चाहिए:

एक अनुरूपण कार्यान्वयन कम से कम एक डायग्नोस्टिक संदेश (कार्यान्वयन-परिभाषित तरीके से पहचाना गया) का उत्पादन करेगा यदि प्रीप्रोसेसिंग अनुवाद इकाई या अनुवाद इकाई में किसी भी वाक्यविन्यास नियम या बाधा का उल्लंघन होता है, [...]

ऐसा लगता है कि जीसीसी में कुछ बग हैं और यह नैदानिक ​​संदेश का उत्पादन करने में विफल रहा। (बग Grzegorz Szpetkowski द्वारा जवाब में बताया गया है


8
"अपरिभाषित व्यवहार" एक रनटाइम प्रभाव है। sizeofएक संकलन-समय ऑपरेटर है। यहां कोई UB नहीं है, और यहां तक ​​कि अगर वहाँ थे, तो यह प्रभावित नहीं कर सकता था sizeof
बेन वोइगट

2
आपको मानक उद्धरण ढूंढना चाहिए जो कि एनुमेरंट एक इंट में फिट नहीं हो सकते हैं। मुझे उस कथन पर अत्यधिक संदेह है और मेरा वोट तब तक ठोस रहेगा जब तक कि यह साफ़ नहीं हो जाता।
zneak

3
@Sergey: C मानक वास्तव में कहता है "अभिव्यक्ति जो एक संलयन स्थिरांक के मान को परिभाषित करती है वह पूर्णांक स्थिर अभिव्यक्ति होगी जो एक मान के रूप में प्रतिनिधित्व योग्य है।" लेकिन इसका उल्लंघन करना एक बाधा उल्लंघन होगा, निदान की आवश्यकता है, यूबी की नहीं।
बेन वोइगट

3
@ झटके: हाँ? यह एक बाधा का उल्लंघन है, और "एक अनुरूप कार्यान्वयन कम से कम एक नैदानिक ​​संदेश (कार्यान्वयन-डे fi नेड तरीके से पहचान in एड) का उत्पादन करेगा यदि प्रीप्रोसेसिंग अनुवाद इकाई या अनुवाद इकाई में किसी भी वाक्यविन्यास या बाधा का उल्लंघन होता है, भले ही व्यवहार भी हो। स्पष्ट रूप से विशिष्ट as ed as fi ned या कार्यान्वयन-डे fi ned।
बेन वोइगट

2
अतिप्रवाह और छंटनी के बीच अंतर है। अतिप्रवाह तब होता है जब आपके पास एक अंकगणितीय ऑपरेशन होता है जो अपेक्षित परिणाम प्रकार के लिए बहुत बड़ा मूल्य पैदा करता है, और हस्ताक्षरित अतिप्रवाह यूबी है। ट्रंकेशन तब होता है जब आपके पास ऐसा मान होता है जो लक्ष्य प्रकार के लिए बहुत बड़ा था (जैसे short s = 0xdeadbeef), और व्यवहार कार्यान्वयन-परिभाषित है।
zneak

5

मैंने मानकों पर एक नज़र डाली और मेरा कार्यक्रम 6.7.2.2p2 के कारण सी में एक बाधा उल्लंघन प्रतीत होता है :

अड़चनें: एक संलयन स्थिरांक के मान को परिभाषित करने वाली अभिव्यक्ति पूर्णांक स्थिर अभिव्यक्ति होगी जो एक मान के रूप में प्रतिनिधित्व योग्य है।

और 7.2.5 के कारण C ++ में परिभाषित:

यदि अंतर्निहित प्रकार निश्चित नहीं है, तो प्रत्येक एन्यूमरेटर का प्रकार इसके आरम्भिक मान का प्रकार है: - यदि एन्यूमरेटर के लिए एक इनिशलाइज़र निर्दिष्ट किया गया है, तो इनिशियलाइज़िंग मान का एक ही प्रकार होता है, और निरंतर-अभिव्यक्ति एक अभिन्न प्रकार होगा निरंतर अभिव्यक्ति (5.19)। - यदि पहले एन्यूमरेटर के लिए कोई इनिशियलाइज़र निर्दिष्ट नहीं किया गया है, तो इनिशियलाइज़िंग मान में एक अनिर्दिष्ट इंटीग्रल प्रकार होता है। - अन्यथा आरंभिक मान का प्रकार पूर्ववर्ती एन्यूमरेटर के प्रारंभिक मूल्य का प्रकार समान है जब तक कि उस मूल्य में वृद्धि का मान प्रतिनिधित्व योग्य नहीं होता है, उस स्थिति में प्रकार अनिर्दिष्ट अभिन्न प्रकार होता है जिसमें वृद्धित मूल्य शामिल होता है। यदि ऐसा कोई प्रकार मौजूद नहीं है, तो प्रोग्राम बीमार है।


3
यह सी में "अपरिभाषित" नहीं है, यह "बीमार" है क्योंकि एक बाधा का उल्लंघन किया गया है। संकलक उल्लंघन के संबंध में एक निदान अवश्य उत्पन्न करता है।
बेन वोइगट

@BenVoigt मुझे अंतर के बारे में सिखाने के लिए धन्यवाद। इसे उत्तर में तय किया (जो मैंने बनाया क्योंकि मैं अन्य उत्तरों में C ++ मानक से एक उद्धरण याद करता था)।
पीएसकोकिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.