सी-स्टैंडर्ड के कारण कॉन्स्ट्रेस-नेस के पुनरावर्ती रूप से विचार करने का क्या कारण है?


9

C99 मानक 6.5.16: 2 में कहता है:

एक असाइनमेंट ऑपरेटर के पास अपने बाएं ऑपरेंड के रूप में एक परिवर्तनीय अंतराल होगा।

और 6.3.2.1:1 में:

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

अब, चलो const structएक constक्षेत्र के साथ एक गैर पर विचार करें ।

typedef struct S_s {
    const int _a;
} S_t;

मानक के अनुसार, निम्न कोड अपरिभाषित व्यवहार (UB) है:

S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;

इसके साथ शब्दार्थ समस्या यह है कि संलग्न इकाई ( struct) को लिखने योग्य (गैर-केवल पढ़ने योग्य) माना जाना चाहिए, इकाई के घोषित प्रकार ( S_t s1) से देखते हुए , लेकिन मानक के शब्द (2 कारणों) द्वारा लिखने योग्य नहीं माना जाना चाहिए शीर्ष पर) constक्षेत्र के कारण _a। मानक एक प्रोग्रामर को कोड पढ़ने के लिए यह स्पष्ट नहीं करता है कि असाइनमेंट वास्तव में एक यूबी है, क्योंकि यह बताना असंभव है कि डब्ल्यू / ओ struct S_s ... S_tप्रकार की परिभाषा ।

इसके अलावा, क्षेत्र में केवल पढ़ने के लिए उपयोग केवल कृत्रिम रूप से लागू किया जाता है। वहाँ कोई रास्ता नहीं constगैर के कुछ क्षेत्रों const structवास्तव में केवल पढ़ने के लिए भंडारण के लिए रखा जा रहा है। लेकिन मानक के ऐसे शब्दों के कारण उस कोड को बदल दिया जाता है, जो constइन क्षेत्रों के एक्सेसर प्रक्रियाओं में जानबूझकर खेतों के क्वालीफायर को दूर कर देता है , जैसे कि ( क्या यह C में संरचना के क्षेत्रों को कांस्टेबल बनाने के लिए एक अच्छा विचार है? ):

(*)

#include <stdlib.h>
#include <stdio.h>

typedef struct S_s {
    const int _a;
} S_t;

S_t *
create_S(void) {
    return calloc(sizeof(S_t), 1);
}

void
destroy_S(S_t *s) {
    free(s);
}

const int
get_S_a(const S_t *s) {
    return s->_a;
}

void
set_S_a(S_t *s, const int a) {
    int *a_p = (int *)&s->_a;
    *a_p = a;
}

int
main(void) {
    S_t s1;
    // s1._a = 5; // Error
    set_S_a(&s1, 5); // OK
    S_t *s2 = create_S();
    // s2->_a = 8; // Error
    set_S_a(s2, 8); // OK

    printf("s1.a == %d\n", get_S_a(&s1));
    printf("s2->a == %d\n", get_S_a(s2));

    destroy_S(s2);
}

तो, किसी कारण से, एक पूरे के structलिए केवल-पढ़ने के लिए यह घोषित करने के लिए पर्याप्त हैconst

const S_t s3;

लेकिन पूरी तरह structसे गैर-पढ़ने के लिए-केवल इसे w / o घोषित करने के लिए पर्याप्त नहीं है const

मुझे लगता है कि बेहतर होगा, या तो है:

  1. खेतों के constसाथ गैर- संरचनाओं के निर्माण को बाधित करने के लिए const, और इस तरह के एक मामले में नैदानिक ​​जारी करना। इससे यह स्पष्ट हो जाएगा कि structकेवल पढ़ने के लिए फ़ील्ड केवल-पढ़ने के लिए है।
  2. constगैर- constसंरचना से संबंधित क्षेत्र को लिखने के मामले में व्यवहार को परिभाषित करने के लिए मानक के अनुरूप कोड (*) से ऊपर बनाने के लिए।

अन्यथा व्यवहार संगत और समझने में कठिन नहीं है।

तो, सी स्टैंडर्ड के लिए कारण क्या है- constपुनरावर्ती रूप से, जैसा कि यह कहता है?


ईमानदार होने के लिए, मुझे वहाँ एक सवाल नहीं दिखता।
बार्ट वैन इनगेन शानौ

@BartvanIngenSchenau ने शरीर के अंत में विषय में बताए गए प्रश्न को जोड़ने के लिए संपादन किया
माइकल पैंकोव

1
क्यों होता है पतन?
माइकल पेंकोव

जवाबों:


4

तो, C मानक के लिए कारण क्या है कि वह इसे लगाता है?

अकेले एक प्रकार के दृष्टिकोण से, ऐसा नहीं करना निराधार होगा (दूसरे शब्दों में: बहुत टूट गया और जानबूझकर अविश्वसनीय)।

और ऐसा इसलिए है क्योंकि किसी संरचना पर "=" का अर्थ है: यह एक पुनरावर्ती कार्य है। यह इस प्रकार है कि अंततः आपके पास s1._a = <value>"टाइपिंग नियमों के अंदर" हो रहा है। यदि मानक इसे "नेस्टेड" constफ़ील्ड के लिए अनुमति देता है, तो यह एक स्पष्ट विरोधाभास के रूप में अपनी प्रकार की प्रणाली परिभाषा में एक गंभीर असंगति को जोड़ सकता है ( constक्योंकि यह सुविधा को दूर फेंक सकता है, क्योंकि यह बस इसकी बहुत परिभाषा से बेकार और अविश्वसनीय हो गया है)।

आपका समाधान (1), जहां तक ​​मैं समझता हूं, यह अनावश्यक रूप से पूरे ढांचे को constजब भी इसका कोई एक क्षेत्र होता है, मजबूर किया जाता है const। इस तरह, s1._b = bएक गैर-कॉन्स्टेबल ._bक्षेत्र के लिए गैर-कॉन्स्टेबल पर गैर-कॉन्स्टेबल s1युक्त होगा const a


कुंआ। Cबमुश्किल एक ध्वनि प्रकार की प्रणाली होती है (जैसे कि वर्षों के दौरान एक दूसरे के कोने पर बंधे कोने के मामलों की तरह)। इसके अलावा, असाइनमेंट को एक करने का दूसरा तरीका structहै memcpy(s_dest, s_src, sizeof(S_t))। और मुझे पूरा यकीन है कि इसे लागू करने का वास्तविक तरीका है। और ऐसे मामले में भी मौजूदा "टाइप सिस्टम" आपको ऐसा करने से रोकता नहीं है।
माइकल पेंकोव

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

2

कारण यह है कि केवल-पढ़ने के लिए फ़ील्ड केवल-पढ़ने के लिए हैं। कोई बड़ा आश्चर्य नहीं है।

आप गलती से मान रहे हैं कि ROM में प्लेसमेंट पर एकमात्र प्रभाव है, जो कि आस-पास के नॉन-कास्ट फील्ड में होने पर वास्तव में असंभव है। वास्तव में, ऑप्टिमाइज़र मान सकते हैं कि constअभिव्यक्तियों को लिखा नहीं गया है, और उसी के आधार पर अनुकूलन किया जा सकता है। निश्चित रूप से यह धारणा नहीं है कि जब गैर-कास्ट एलियास मौजूद है।

आपका समाधान (1) मौजूदा कानूनी और उचित कोड को तोड़ता है। ऐसा नहीं होगा। आपका समाधान (2) बहुत constसदस्यों पर अर्थ निकालता है । हालांकि यह मौजूदा कोड को नहीं तोड़ेगा, लेकिन इसमें कोई कमी नहीं है।


मुझे 90% यकीन है कि ऑप्टिमाइज़र यह मान नहीं सकते हैं कि constफ़ील्ड को लिखा नहीं गया है, क्योंकि आप हमेशा उपयोग कर सकते हैं memsetया memcpy, और यह भी मानक के अनुरूप होगा। (1) एक ध्वज द्वारा सक्षम, बहुत कम से कम, अतिरिक्त चेतावनी के रूप में लागू किया जा सकता है। (2) का तर्क यह है कि, ठीक है, बिल्कुल - ऐसा कोई तरीका नहीं है structकि पूरे ढांचे के लिखने योग्य होने पर इसे गैर-लेखन योग्य नहीं माना जा सकता ।
माइकल पैंकोव

मानक के लिए "ध्वज द्वारा निर्धारित एक वैकल्पिक निदान" मांग की मानक के लिए एक अनूठी आवश्यकता होगी। इसके अलावा, झंडा स्थापित करने से अभी भी मौजूदा कोड टूट जाएगा, इसलिए वास्तव में कोई भी ध्वज को परेशान नहीं करेगा और यह एक मृत अंत होगा। (2) के रूप में, 6.3.2.1:1 बिल्कुल विपरीत निर्दिष्ट करता है: जब भी एक घटक होता है तो संपूर्ण संरचना गैर- लेखन योग्य होती है। हालांकि, अन्य घटक अभी भी लेखन योग्य हो सकते हैं। सी एफ C ++ जो operator=सदस्यों के संदर्भ में भी परिभाषित करता है , और इसलिए operator=जब कोई एक सदस्य परिभाषित नहीं करता है const। सी और सी ++ अभी भी यहां संगत हैं।
MSalters

@ कॉन्स्टेंटियस - यह तथ्य कि आप किसी सदस्य की विवशता के बारे में जानबूझकर कुछ कर सकते हैं, वह आशावादी के लिए उस कब्ज को नजरअंदाज करने का कारण नहीं है। आप एक फंक्शन के अंदर कास्टनेस को दूर कर सकते हैं, जिससे आप सामान बदल सकते हैं। लेकिन कॉलिंग संदर्भ में ऑप्टिमाइज़र को अभी भी यह मानने की अनुमति है कि आप नहीं करेंगे। निरंतरता प्रोग्रामर के लिए उपयोगी है, लेकिन यह कुछ मामलों में ऑप्टिमाइज़र के लिए एक ईश्वर-भेजना भी है।
माइकल कोहेन

फिर एक गैर-लेखन योग्य संरचना को ओवरराइट करने की अनुमति क्यों दी जाती है memcpy? अन्य कारणों से - ठीक है, यह विरासत है, लेकिन पहली बार में इस तरह से क्यों किया गया?
माइकल पैंकोव

1
अगर आपकी टिप्पणी memcpyसही है, तो मैं अभी भी सोच रहा हूं । आपके अन्य प्रश्न में AFACIT जॉन बोडे का उद्धरण सही है: आपका कोड एक कांस्टेबल योग्य वस्तु को लिखता है और इसलिए मानक शिकायत नहीं है, चर्चा का अंत।
MSalters
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.