सशर्त ऑपरेटर का उपयोग करते समय C, समतल तारों की अनुमति क्यों नहीं देता है?


95

निम्नलिखित कोड समस्याओं के बिना संकलित करता है:

int main() {
    printf("Hi" "Bye");
}

हालाँकि, यह संकलन नहीं है:

int main() {
    int test = 0;
    printf("Hi" (test ? "Bye" : "Goodbye"));
}

उसका क्या कारण है?


95
स्ट्रिंग संघनन प्रारंभिक लेक्सिंग चरण का हिस्सा है; यह सी के अभिव्यक्ति सिंटैक्स का हिस्सा नहीं है। दूसरे शब्दों में, "स्ट्रिंग शाब्दिक" का कोई मूल्य नहीं है । बल्कि, स्ट्रिंग शाब्दिक स्रोत कोड के शाब्दिक तत्व हैं जो मान बनाते हैं।
केरेक एसबी

24
सिर्फ @KerrekSB उत्तर को स्पष्ट करने के लिए - तार का संघात इसे संकलित करने से पहले कोड पाठ के प्रीप्रोसेसिंग का एक हिस्सा है। जबकि कोड संकलित किए जाने के बाद (या जब सब कुछ स्थिर रहता है तो संकलन-समय में किया जा सकता है), टर्नरी ऑपरेटर का मूल्यांकन रनटाइम में किया जाता है।
यूजीन श।

2
विवरण: इस पोस्ट में, "Hi"और "Bye"कर रहे हैं स्ट्रिंग शाब्दिक , नहीं तार के रूप में सी मानक पुस्तकालय में इस्तेमाल किया। स्ट्रिंग शाब्दिकों के साथ , संकलक संघनन करेगा "H\0i" "B\0ye"। उसी के साथ नहींsprintf(buf,"%s%s", "H\0i" "B\0ye");
chux - Reinstate Monica

15
कम-से-कम वही कारण जो आप नहीं कर सकते हैंa (some_condition ? + : - ) b
user253751

4
ध्यान दें कि यहां तक ​​कि printf("Hi" ("Bye"));काम नहीं करेगा - इसके लिए टर्नरी ऑपरेटर की आवश्यकता नहीं है; कोष्ठक पर्याप्त है (हालांकि printf("Hi" test ? "Bye" : "Goodbye")यह भी संकलन नहीं होगा)। केवल सीमित संख्या में टोकन हैं जो एक स्ट्रिंग शाब्दिक का पालन कर सकते हैं। कोमा ,, खुला वर्ग ब्रैकेट [, क्लोज़ स्क्वायर ब्रैकेट ](जैसा कि 1["abc"]- और हाँ, यह भीषण है), क्लोज़ राउंड ब्रैकेट ), क्लोज़ कर्ली ब्रैकेट }(एक इनिशियलर या इसी तरह के संदर्भ में), और अर्धविराम ;वैध (और एक अन्य स्ट्रिंग शाब्दिक) हैं; मुझे यकीन नहीं है कि कोई अन्य भी है।
जोनाथन लेफ़लर

जवाबों:


121

सी स्टैंडर्ड के अनुसार (5.1.1.2 अनुवाद चरण)

1 अनुवाद के वाक्यविन्यास नियमों के बीच की वरीयता निम्नलिखित चरणों द्वारा निर्दिष्ट की गई है।)

  1. आसन्न स्ट्रिंग शाब्दिक टोकन समवर्ती होते हैं।

और उसके बाद ही

  1. टोकन को अलग करने वाले सफेद-अंतरिक्ष वर्ण अब महत्वपूर्ण नहीं हैं। प्रत्येक प्रीप्रोसेसिंग टोकन को टोकन में परिवर्तित किया जाता है। परिणामी टोकन को वाक्य रचना के रूप में क्रमबद्ध और शब्दार्थ रूप से विश्लेषित और अनुवादित किया जाता है

इस निर्माण में

"Hi" (test ? "Bye" : "Goodbye")

आसन्न स्ट्रिंग शाब्दिक टोकन नहीं हैं। इसलिए यह निर्माण अमान्य है।


43
यह केवल इस दावे को दोहराता है कि यह सी में अनुमति नहीं है। यह स्पष्ट नहीं करता है कि क्यों , जो सवाल था। पता नहीं क्यों यह 5 घंटे में 26 upvotes जमा हुआ .... और स्वीकार करते हैं, कम नहीं! बधाई हो।
को ऑर्बिट में लाइटनेस दौड़

4
यहां @LightnessRacesinOrbit से सहमत होना होगा। क्यों नहीं करना चाहिए (test ? "Bye" : "Goodbye")अनिवार्य रूप से स्ट्रिंग शाब्दिक दोनों में से किसी को evaulate बनाने "Hi" "Bye" या "Hi Goodbye"? (मेरे प्रश्न का उत्तर अन्य उत्तरों में दिया गया है)
पागल

48
@LightnessRacesinOrbit, क्योंकि जब लोग आम तौर पर पूछते हैं कि सी में कुछ संकलन क्यों नहीं होता है, तो वे स्पष्टीकरण के लिए पूछ रहे हैं कि यह किस नियम पर टूटता है, न कि क्यों मानक पुरातनता के लेखकों ने इसे इस तरह से चुना।
user1717828

4
@LightnessRacesinOrbit आपके द्वारा वर्णित प्रश्न संभवतः विषय से हटकर होगा। मैं कोई भी तकनीकी कारण नहीं देख सकता कि इसे लागू करना क्यों संभव नहीं है, इसलिए विनिर्देश के लेखकों के एक निश्चित उत्तर के बिना, सभी उत्तर राय आधारित होंगे। और यह आम तौर पर "व्यावहारिक" या "जवाबदेह" सवालों की श्रेणी में नहीं आएगा (जैसा कि सहायता केंद्र इंगित करता है कि हमें आवश्यकता है)।
jpmc26

12
@LightnessRacesinOrbit यह बताता है कि : "क्योंकि C मानक ने ऐसा कहा था"। यह नियम क्यों परिभाषित किया गया है, इस विषय पर सवाल करना बंद विषय होगा।
user11153

135

C11 मानक के अनुसार, अध्याय §5.1.1.2, आसन्न स्ट्रिंग शाब्दिकों का संयोजन:

आसन्न स्ट्रिंग शाब्दिक टोकन समवर्ती होते हैं।

अनुवाद के चरण में होता है । दूसरी ओर:

printf("Hi" (test ? "Bye" : "Goodbye"));

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


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

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

बस FYI, रन-टाइम स्ट्रिंग ( शाब्दिक नहीं ) के लिए, हमारे पास लाइब्रेरी फंक्शन हैstrcat() जो दो स्ट्रिंग्स को समेटता है । सूचना, विवरण का उल्लेख है:

char *strcat(char * restrict s1,const char * restrict s2);

strcat()समारोह एक स्ट्रिंग की प्रतिलिपि की ओर इशारा द्वारा संलग्न कर देता है s2(समाप्त अशक्त चरित्र सहित) अंत के लिए स्ट्रिंग द्वारा की ओर इशारा कियाs1s2के अंत में अशक्त चरित्र को अधिलेखित करता है s1। [...]

तो, हम देख सकते हैं, s1एक स्ट्रिंग है , न कि एक स्ट्रिंग शाब्दिक । हालांकि, जैसा कि सामग्री s2किसी भी तरह से नहीं बदली गई है, यह बहुत अच्छी तरह से एक स्ट्रिंग शाब्दिक हो सकता है ।


आप इसके बारे में एक अतिरिक्त व्याख्या जोड़ना चाहते हैं strcat: गंतव्य सरणी s2पहले से मौजूद वर्णों के बाद प्लस एक अशक्त टर्मिनेटर से वर्ण प्राप्त करने के लिए पर्याप्त लंबी होनी चाहिए ।
चकरली

39

स्ट्रिंग शाब्दिक संयोजन पूर्व-संकलक द्वारा संकलन-समय पर किया जाता है। इस संघटन के मूल्य के बारे में पता करने का कोई तरीका नहीं है test, जो कि कार्यक्रम को वास्तव में निष्पादित होने तक ज्ञात नहीं है। इसलिए, इन स्ट्रिंग शाब्दिकों को समाप्‍त नहीं किया जा सकता है।

क्योंकि सामान्य मामला यह है कि आपके पास संकलन-समय पर ज्ञात मूल्यों के लिए इस तरह का निर्माण नहीं होगा, सी मानक को ऑटो-कॉन्सेप्टन सुविधा को सबसे बुनियादी मामले तक सीमित करने के लिए डिज़ाइन किया गया था: जब शाब्दिक एक दूसरे के साथ सही होते हैं ।

लेकिन यहां तक ​​कि अगर यह इस तरह से इस प्रतिबंध को शब्द नहीं देता है, या यदि प्रतिबंध अलग-अलग तरीके से निर्मित किया गया है, तो आपके उदाहरण को समवर्ती प्रक्रिया बनाने के बिना महसूस करना असंभव होगा। और, इसके लिए, हमारे पास पुस्तकालय कार्य हैं जैसे कि strcat


3
मैंने सिर्फ धारणाएँ पढ़ी हैं। जबकि आप जो कहते हैं वह बहुत अधिक मान्य है, आप इसके लिए स्रोत प्रदान नहीं कर सकते हैं क्योंकि कोई भी नहीं है। सी के संबंध में एकमात्र स्रोत मानक दस्तावेज है (जबकि यह कई मामलों में अप्रिय है) यह नहीं बताता है कि कुछ चीजें वैसे हैं जैसे वे हैं, लेकिन सिर्फ यह बताता है कि उन्हें वह विशिष्ट तरीका होना चाहिए। इसलिए मॉस्को के जवाब से व्लाद के बारे में नाइट-पिकी को समझना अनुचित है। चूंकि ओपी को "ऐसा क्यों है?" -जबकि एकमात्र सही खट्टा जवाब है, "क्योंकि यह सी है, और सी जिस तरह से परिभाषित किया गया है सी" केवल सही उत्तर देने वाला उत्तर है।
धिन

1
यह (प्रशंसा) स्पष्टीकरण की कमी है। लेकिन यहां फिर से मधुमक्खी ने कहा कि व्लाद का जवाब मूल समस्या के स्पष्टीकरण के रूप में है और फिर आपकी सेवा करता है। फिर से कहा गया: जब आप मेरे द्वारा दी गई जानकारी की पुष्टि कर सकते हैं, तो संबंधित और सही है, मैं आपकी शिकायतों से असहमत हूं। और जब मैं विचार नहीं करूँगा ऑफॉप्टिक एशवेल, अपने पीओवी से अधिक ऑफॉपिक तो व्लादस वास्तव में है।
धिन

11
@Zaibis: स्रोत मैं है। व्लाद का जवाब बिल्कुल भी स्पष्टीकरण नहीं है; यह केवल प्रश्न के आधार की पुष्टि है। निश्चित रूप से उनमें से कोई भी "ऑफ टॉपिक" नहीं है (आप देखना चाह सकते हैं कि इस शब्द का अर्थ क्या है)। लेकिन आप अपनी राय के हकदार हैं।
को कक्षा

उपरोक्त टिप्पणियों को पढ़ने के बाद भी, मुझे अभी भी आश्चर्य है कि इस उत्तर को किसने अस्वीकार किया,, मेरा मानना ​​है कि यह एक सही उत्तर है जब तक कि ओपी इस उत्तर पर और स्पष्टीकरण नहीं मांगता।
मोहित जैन

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

30

क्योंकि C का कोई stringप्रकार नहीं है । स्ट्रिंग लिटरल को charएरे द्वारा संकलित किया जाता है, ए द्वारा संदर्भितchar* सूचक जाता है।

सी आसन्न शाब्दिकों को आपके पहले उदाहरण के अनुसार, संकलन-समय पर संयोजित करने की अनुमति देता है । सी कंपाइलर को स्ट्रिंग्स के बारे में कुछ ज्ञान है। लेकिन यह जानकारी रनटाइम पर मौजूद नहीं है, और इस तरह कॉन्सेप्टेशन नहीं हो सकता है।

संकलन प्रक्रिया के दौरान, आपका पहला उदाहरण "अनुवादित" है:

int main() {
    static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', '\0'};
    printf(char_ptr_1);
}

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

हालाँकि, आपका दूसरा उदाहरण कुछ इस तरह "अनुवादित" है:

int main() {
    static const char char_ptr_1[] = {'H', 'i', '\0'};
    static const char char_ptr_2[] = {'B', 'y', 'e', '\0'};
    static const char char_ptr_3[] = {'G', 'o', 'o', 'd', 'b', 'y', 'e', '\0'};
    int test = 0;
    printf(char_ptr_1 (test ? char_ptr_2 : char_ptr_3));
}

यह स्पष्ट होना चाहिए कि यह संकलन क्यों नहीं करता है। टर्नरी ऑपरेटर ?का मूल्यांकन रनटाइम पर किया जाता है, संकलन-समय पर नहीं, जब "स्ट्रिंग्स" का अस्तित्व अब ऐसा नहीं है, लेकिन केवल साधारण charसरणियों के रूप में , char*संकेत द्वारा संदर्भित । आसन्न स्ट्रिंग शाब्दिक के विपरीत , आसन्न चार बिंदु केवल एक वाक्यविन्यास त्रुटि है।


2
उत्कृष्ट उत्तर, संभवतः यहां सबसे अच्छा है। "यह स्पष्ट होना चाहिए कि यह संकलन क्यों नहीं करता है।" आप इसका विस्तार करने पर विचार कर सकते हैं कि "क्योंकि टर्नरी ऑपरेटर एक सशर्त मूल्यांकन है जो रन टाइम पर संकलित समय नहीं है "।
बिल्ली

नहीं static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};होना चाहिए static const char *char_ptr_1 = "HiBye";और इसी तरह बाकी बिंदुओं के लिए?
स्पिकट्रिक्स

@CoolGuy जब आप लिखते static const char *char_ptr_1 = "HiBye";हैं संकलक लाइन का अनुवाद करता है static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};, तो नहीं, इसे "स्ट्रिंग की तरह" नहीं लिखा जाना चाहिए। जैसा कि उत्तर कहता है, तार को वर्णों के एक समूह में संकलित किया जाता है, और यदि आप वर्णों की एक सरणी को "सबसे" कच्चे रूप में निर्दिष्ट कर रहे हैं, तो आप एक अल्पविराम से अलग की गई सूची का उपयोग करेंगे, जैसेstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
अंकुश

3
@ अनकुश हां। लेकिन हालांकि static const char str[] = {'t', 'e', 's', 't', '\0'};रूप में ही है static const char str[] = "test";, static const char* ptr = "test";है नहीं के रूप में ही static const char* ptr = {'t', 'e', 's', 't', '\0'};। पूर्व वैध है और संकलन करेगा लेकिन उत्तरार्द्ध अमान्य है और वह करता है जो आप अपेक्षा करते हैं।
स्पिकट्रिक्स

मैंने पिछले पैराग्राफ को निकाल दिया है और कोड उदाहरणों को सही किया है, धन्यवाद!
अहस्ताक्षरित

12

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

#include <stdio.h>
#define ccat(s, t, a, b) ((t)?(s a):(s b))

int
main ( int argc, char **argv){
  printf("%s\n", ccat("hello ", argc > 2 , "y'all", "you"));
  return 0;
}

10

उसका क्या कारण है?

टर्निरी ऑपरेटर का उपयोग करने वाला आपका कोड सशर्त रूप से दो स्ट्रिंग शाब्दिकों के बीच चयन करता है। ज्ञात या अज्ञात कोई भी स्थिति नहीं है, इसका मूल्यांकन संकलन समय पर नहीं किया जा सकता है, इसलिए यह संकलन नहीं कर सकता है। यहां तक ​​कि यह कथन printf("Hi" (1 ? "Bye" : "Goodbye"));संकलित नहीं होगा। कारण ऊपर के उत्तरों में गहराई से बताया गया है। संकलित करने के लिए मान्य टर्नरी ऑपरेटर का उपयोग करके इस तरह के एक बयान बनाने की एक और संभावना , एक प्रारूप टैग भी शामिल होगी और अतिरिक्त तर्क के रूप में स्वरूपित त्रैमासिक ऑपरेटर कथन का परिणाम होगा printf। फिर भी, printf()प्रिंटआउट "concatenated होने" केवल उन स्ट्रिंग्स और के रूप में जल्दी के रूप में की एक छाप देना होगा क्रम

#include <stdio.h>

int main() {
    int test = 0;
    printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result
}

3
SO एक ट्यूटोरियल साइट नहीं है। आपको ओपी को जवाब देना चाहिए और ट्यूटोरियल नहीं।
मिकी

1
यह ओपी के सवाल का जवाब नहीं देता है। यह ओपी की अंतर्निहित समस्या को हल करने का एक प्रयास हो सकता है, लेकिन हम वास्तव में यह नहीं जानते हैं कि क्या है।
कीथ थॉम्पसन

1
printfएक प्रारूप विनिर्देशक की आवश्यकता नहीं है ; यदि केवल संकलन समय पर किया गया था (जो ऐसा नहीं है), तो ओपी का प्रिंटफ का उपयोग मान्य होगा।
डेविड कॉनरेड

आपकी टिप्पणी के लिए धन्यवाद, @ डेविड कॉनराड। मेरा मैला शब्द वास्तव में दिखाई देगा जैसे कि बताते हुए printf()एक प्रारूप टैग की आवश्यकता होगी, जो बिल्कुल सच नहीं है। सही किया!
user3078414

यह एक बेहतर शब्द है। +1 धन्यवाद।
डेविड कॉनराड

7

में printf("Hi" "Bye"); आप चार लगातार दो सरणियों जो संकलक एक एकल सरणी में कर सकते हैं।

में printf("Hi" (test ? "Bye" : "Goodbye"));आपके पास एक सरणी (एक सरणी अपना पहला तत्व के लिए सूचक करने के लिए परिवर्तित) चार के सूचक के द्वारा पीछा किया। कंपाइलर किसी सरणी और पॉइंटर को मर्ज नहीं कर सकता है ।


0

प्रश्न का उत्तर देने के लिए - मैं प्रिंटफ की परिभाषा में जाऊंगा। फ़ंक्शन प्रिंटफ़ ने कॉन्स्ट चर * तर्क के रूप में अपेक्षा की है। कोई भी स्ट्रिंग शाब्दिक जैसे "हाय" एक कास्ट चार * है; हालाँकि एक अभिव्यक्ति जैसे कि (test)? "str1" : "str2"एक कॉन्स्टेबल चार * नहीं है क्योंकि इस तरह की अभिव्यक्ति का परिणाम केवल रन-टाइम पर पाया जाता है और इसलिए संकलन समय पर अनिश्चित है, एक ऐसा तथ्य जिसके कारण कंपाइलर शिकायत करता है। दूसरी ओर - यह पूरी तरह से अच्छी तरह से काम करता हैprintf("hi %s", test? "yes":"no")


* हालांकि एक अभिव्यक्ति के रूप में (test)? "str1" : "str2"एक नहीं है const char*... बेशक यह है! यह एक स्थिर अभिव्यक्ति नहीं है, लेकिन इसका प्रकार है const char * । लिखना ठीक रहेगा printf(test ? "hi " "yes" : "hi " "no")। ओपी की समस्या का कोई लेना-देना नहीं है printf, "Hi" (test ? "Bye" : "Goodbye")वाक्यविन्यास त्रुटि कोई फर्क नहीं पड़ता कि अभिव्यक्ति संदर्भ क्या है।
1964 में chrrlie

माना। मैंने स्वयं अभिव्यक्ति के साथ एक अभिव्यक्ति के आउटपुट को भ्रमित किया
Stats_Lover

-4

यह संकलित नहीं करता है क्योंकि प्रिंटफ फ़ंक्शन के लिए पैरामीटर सूची है

(const char *format, ...)

तथा

("Hi" (test ? "Bye" : "Goodbye"))

पैरामीटर सूची में फिट नहीं है।

gcc इसकी कल्पना करके इसे समझने का प्रयास करता है

(test ? "Bye" : "Goodbye")

एक पैरामीटर सूची है, और शिकायत करता है कि "हाय" एक फ़ंक्शन नहीं है।


6
ढेर अतिप्रवाह में आपका स्वागत है। आप सही हैं कि यह printf()तर्क सूची से मेल नहीं खाता है , लेकिन ऐसा इसलिए है क्योंकि अभिव्यक्ति कहीं भी मान्य नहीं है - केवल printf()तर्क सूची में नहीं। दूसरे शब्दों में, आपने समस्या के लिए एक बहुत विशेष कारण चुना है; सामान्य समस्या यह है कि "Hi" (C में मान्य नहीं है, अकेले में कॉल करें printf()। मेरा सुझाव है कि वोट डाउन होने से पहले आप इस उत्तर को हटा दें।
जोनाथन लेफ़लर

यह नहीं है कि सी कैसे काम करता है। यह PHP की तरह एक स्ट्रिंग शाब्दिक कॉल करने की कोशिश के रूप में पार्स नहीं किया गया है।
बिल्ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.