2010-03-08 10 views
10

इस कार्यक्रमprintf में फ़्लोट/डबल से int को संभालने का रूपांतरण कैसा है?

int main() 
{ 
     float f = 11.22; 
     double d = 44.55; 
     int i,j; 

     i = f;   //cast float to int 
     j = d;   //cast double to int 

     printf("i = %d, j = %d, f = %d, d = %d", i,j,f,d); 
     //This prints the following: 
     // i = 11, j = 44, f = -536870912, d = 1076261027 

     return 0; 
} 

कोई व्याख्या कर सकते हैं क्यों पूर्णांक के लिए डबल/नाव से कास्टिंग पहले मामले में ठीक से काम करता है, और जब printf में किया काम नहीं करता है पर विचार करें?
यह प्रोग्राम 32-बिट लिनक्स मशीन पर gcc-4.1.2 पर संकलित किया गया था।


संपादित करें: Zach's answer लगता प्रारूप विनिर्देशक के तार्किक है, यानी उपयोग यह पता लगाने के लिए रवाना पॉप क्या ढेर। हालांकि फिर इस अनुवर्ती प्रश्न पर विचार करें:

int main() 
{ 

    char c = 'd'; // sizeof c is 1, however sizeof character literal 
        // 'd' is equal to sizeof(int) in ANSI C 

    printf("lit = %c, lit = %d , c = %c, c = %d", 'd', 'd', c, c); 
    //this prints: lit = d, lit = 100 , c = d, c = 100 
    //how does printf here pop off the right number of bytes even when 
    //the size represented by format specifiers doesn't actually match 
    //the size of the passed arguments(char(1 byte) & char_literal(4 bytes))  

return 0; 
} 

यह कैसे काम करता है?

+0

मुझे भी इसी तरह का संदेह था। यह धागा देखें: http://stackoverflow.com/questions/2377733/how-does-this-program-work – Lazer

+1

char एक एकल वर्ण है - यह केवल 8-बिट पूर्णांक है। जब आप इंटीग्रल प्रकार पर किसी प्रकार का ऑपरेशन करते हैं जो int से छोटे होते हैं, तो उन्हें पूर्णांक में पदोन्नत किया जाता है। फ़ंक्शन को कॉल करते समय इसमें शामिल है। तो वास्तव में यह आपके प्रिंटफ कॉल को काम करने के कारण यादृच्छिक मौका नहीं है, यह व्यवहार परिभाषित किया गया है। व्यावहारिक रूप से बोलते हुए, अधिकांश सी एबीआई में आप हमेशा स्टैक पर पारित प्रति चर कम से कम एक मशीन शब्द आवंटित करते हैं। –

+0

@ सूरजजैन हां, ' 'पैरामीटर केवल डिफ़ॉल्ट तर्क पदोन्नति (सी 11 §6.5.2.2/6, §7.16.1.1/2) के अनुसार प्रचारित प्रकारों को पारित करता है, जो गारंटी देता है कि' char' और 'int' को संगत बनाया गया है । हालांकि, यह अंकगणित के लिए उपयोग किए जाने वाले प्रचारों के समान नहीं है। साथ ही, एबीआई के मामले में भाषा के बारे में तर्क करना खतरनाक है।यह पुष्टि करना कि ठीक है नियमों की जांच करने के लिए वास्तव में ठीक है। – Potatoswatter

उत्तर

19

printf फ़ंक्शन प्रारूप विनिर्देशकों का उपयोग यह पता लगाने के लिए करता है कि स्टैक को पॉप अप करना क्या है। तो जब यह %d देखता है, तो यह 4 बाइट्स को बंद करता है और उन्हें int के रूप में व्याख्या करता है, जो गलत है ((float)3.0 का द्विआधारी प्रतिनिधित्व (int)3 जैसा नहीं है)।

आपको या तो %f प्रारूप विनिर्देशकों का उपयोग करने की आवश्यकता होगी या int पर तर्क डालना होगा।

$ gcc -Wall -Werror test.c 
cc1: warnings being treated as errors 
test.c: In function ‘main’: 
test.c:10: error: implicit declaration of function ‘printf’ 
test.c:10: error: incompatible implicit declaration of built-in function ‘printf’ 
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘double’ 
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 5 has type ‘double’ 

सवाल का संपादित हिस्से के लिए प्रतिक्रिया::

सी पूर्णांक पदोन्नति नियम सब कहते हैं कि तुम gcc का एक नया पर्याप्त संस्करण का उपयोग कर रहे हैं, तो मजबूत चेतावनी चालू करने में त्रुटि की इस तरह फैल जाती है int से छोटे प्रकार के प्रकार को एक vararg के रूप में पारित होने पर int पर पदोन्नत किया जाता है। तो आपके मामले में, 'd' को int पर प्रचारित किया जा रहा है, तो printf int से पॉप-अप कर रहा है और char पर कास्टिंग कर रहा है। इस व्यवहार के लिए मुझे सबसे अच्छा संदर्भ मिल सकता था this blog entry

+3

+1 मुझे मजबूत चेतावनियां पसंद हैं। –

+0

मेरे पास एक फॉलो अप प्रश्न है, कृपया मेरे प्रश्न का संपादित हिस्सा देखें। अगर प्रश्न स्पष्ट नहीं है तो कृपया टिप्पणी करें ... धन्यवाद – Sandip

+1

यह सच है कि तर्क सूची के परिवर्तनीय भाग में पारित होने पर 'char' तर्कों को 'int' में पदोन्नत किया जाता है (यह तब होता है जब' c' पास हो जाता है उदाहरण), लेकिन चरित्र शाब्दिक स्थिरांक (जैसे '' d'') पहले से ही 'int' प्रकार के हैं। (ध्यान दें कि सिद्धांत में, कुछ कंपाइलरों के तहत, 'char' तर्कों को इसके बजाय' हस्ताक्षरित int 'में प्रचारित किया जा सकता है)। – caf

2

क्योंकि आप नाव फॉर्मेट स्पेसिफायर उपयोग नहीं कर रहे, साथ प्रयास करें:

printf("i = %d, j = %d, f = %f, d = %f", i,j,f,d); 

अन्यथा, यदि आप 4 ints चाहते आप तर्क गुजर printf करने से पहले उन्हें कास्ट करने के लिए है:

printf("i = %d, j = %d, f = %d, d = %d", i,j,(int)f,(int)d); 
0

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

4

Jack's answer आपकी समस्या को ठीक करने का तरीका बताता है। मैं यह बताने जा रहा हूं कि आपको अपने अप्रत्याशित परिणाम क्यों मिल रहे हैं।

float f = 11.22; 
double d = 44.55; 
int i,j,k,l; 

i = (int) f; 
j = (int) d; 
k = *(int *) &f;   //cast float to int 
l = *(int *) &d;   //cast double to int 

printf("i = %d, j = %d, f = %d, d = %d", i,j,k,l); 

कारण यह है कि f और d मूल्यों के रूप में printf को पास किया जाता है, और फिर इन मूल्यों int रों के रूप में व्याख्या कर रहे हैं: अपने कोड के बराबर है। यह बाइनरी मान नहीं बदलता है, इसलिए प्रदर्शित संख्या float या double का द्विआधारी प्रतिनिधित्व है। float से int से वास्तविक कास्ट जेनरेट असेंबली में अधिक जटिल है।

+2

में अपना उत्तर देख सकते हैं, उसे अपने आउटपुट को समझने में मदद मिल सकती है, लेकिन कोड बराबर नहीं है। आप misinterpretation printf, या किसी भी variadic फ़ंक्शन के लिए समकक्ष कोड नहीं लिख सकते हैं, जबकि अभी भी वैरैडिक फ़ंक्शन को कॉल करते हैं। –

+0

मेरा कोड करता है (मेरे लिए, और शायद उसके लिए) उसका कोड क्या कर रहा है (उसके लिए)। दोनों अनिर्धारित व्यवहार हैं, इसलिए किसी को विशेष रूप से कुछ भी करने की गारंटी नहीं है। –

7

ऐसी कोई बात नहीं है "printf में int कास्टिंग" जैसी कोई चीज़ नहीं है। printf ऐसा नहीं करता है और कोई कास्टिंग नहीं कर सकता है। असंगत प्रारूप विनिर्देशक अनिर्धारित व्यवहार की ओर जाता है।

प्रैक्टिस printf में केवल कच्चे डेटा और reinterprets को स्वरूप विनिर्देशक द्वारा उल्लिखित प्रकार के रूप में प्राप्त होता है। यदि आप इसे double मान पास करते हैं और int प्रारूप विनिर्देशक (जैसे %d) निर्दिष्ट करते हैं, printf उस double मान लेगा और अंधाधुंध इसे int पुन: परिभाषित करेगा। परिणाम पूरी तरह से अप्रत्याशित होंगे (यही कारण है कि यह औपचारिक रूप से सी में अपरिभाषित व्यवहार का कारण बनता है)।

+0

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

+0

@ पीटर ए श्नाइडर: वास्तव में? परिस्थितियों के बारे में क्या है जहां प्रारूप स्ट्रिंग रन-टाइम मान है? यदि आप "रूपांतरण विनिर्देश मानक का हिस्सा हैं" भले ही आप संकलक को कुछ भी "कास्ट" करने की अपेक्षा करते हैं? अधिक आम तौर पर, यह सी भाषा डिजाइन का एक मूल सिद्धांत है: इसकी मानक लाइब्रेरी संकलक की "जादुई" विशेषताओं पर निर्भर नहीं है (कुछ अपवादों के साथ, शायद)। लगभग हर लाइब्रेरी सुविधा उपयोगकर्ता द्वारा सी भाषा में ही लागू की जा सकती है। जब तक सी भाषा उस मौलिक सिद्धांत का पालन करती है, तब तक 'printf' को किसी भी कंपाइलर जादू के साथ "संवर्धित नहीं किया जाएगा"। – AnT

+0

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

1

आपका फॉलो-अप कोड काम करने का कारण यह है कि चरित्र स्थिरता को स्टैक पर धक्का देने से पहले एक इंट को बढ़ावा दिया जाता है। तो printf% c के लिए 4 बाइट्स और% d के लिए पॉप करता है। वास्तव में, चरित्र स्थिरांक प्रकार int हैं, प्रकार टाइप नहीं। सी उस तरह अजीब है।

0

यह ध्यान देने योग्य है कि printf, एक परिवर्तनीय-लंबाई तर्क सूची वाले फ़ंक्शन होने के कारण, कभी भी फ़्लोट प्राप्त नहीं होता है; फ्लोट तर्क "पुराने स्कूल" को युगल में पदोन्नत किया जाता है।

हाल ही में एक मानक प्रारूप पहले "पुराने स्कूल" डिफ़ॉल्ट प्रोन्नति का परिचय (n1570, 6.5.2.2/6):

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

तो यह चर्चा करता चर तर्क सूची (6.5.2.2/7):

The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

printf के परिणाम यह है कि एक असली फ्लोट "प्रिंट" करना असंभव है। एक फ्लोट अभिव्यक्ति को हमेशा डबल पर बढ़ावा दिया जाता है, जो आईईईई 754 कार्यान्वयन के लिए 8 बाइट मान है। यह पदोन्नति कॉलिंग पक्ष पर होती है; जब इसके निष्पादन शुरू होता है तो printf में पहले से ही 8 बाइट तर्क होगा।

यदि हम 11.22 को डबल पर रखते हैं और इसकी सामग्री का निरीक्षण करते हैं, तो मेरे x86_64-pc-cygwin gcc के साथ मैं बाइट अनुक्रम 000000e0a3702640 देखता हूं।

यह printf द्वारा मुद्रित int मान को समझाता है: इस लक्ष्य पर इट्स में अभी भी 4 बाइट हैं, ताकि केवल पहले चार बाइट्स 000000e0 का मूल्यांकन किया जा सके, और फिर थोड़ा अंत में, यानी 0xe0000000 के रूप में। यह दशमलव में -536870912 है।

यदि हम सभी 8 बाइट्स को उलट देते हैं, क्योंकि इंटेल प्रोसेसर छोटे एंडियन में युगल स्टोर करता है, तो भी हमें 402670a3e0000000 मिलते हैं। हम आईईईई प्रारूप on this web site में इस बाइट अनुक्रम का प्रतिनिधित्व करने वाले मान की जांच कर सकते हैं; यह 1.122E1 के करीब है, यानि 11.22, अपेक्षित परिणाम।

संबंधित मुद्दे