প্রিন্টফ কেন ("% f", 0) করে; অপরিবর্তিত আচরণ দেবেন?


87

বিবৃতি

printf("%f\n",0.0f);

প্রিন্ট 0।

তবে, বিবৃতি

printf("%f\n",0);

এলোমেলো মান মুদ্রণ।

আমি বুঝতে পারি যে আমি একধরনের অপরিজ্ঞাত আচরণ প্রদর্শন করছি, তবে কেন আমি বিশেষভাবে তা বুঝতে পারি না।

একটি ভাসমান পয়েন্ট মান যেখানে সমস্ত বিট 0 হয় এখনও 0 এর floatমান সহ বৈধ
floatএবং intআমার মেশিনে এটি একই আকারের (এটি এমনকি প্রাসঙ্গিক হলে)।

ভাসমান পয়েন্টের পরিবর্তে পূর্ণসংখ্যার আক্ষরিক ব্যবহার কেন printfএই আচরণের কারণ?

পিএস একই ব্যবহার দেখা যায় যদি আমি ব্যবহার করি

int i = 0;
printf("%f\n", i);

37
printfএকটি আশা করছে double, এবং আপনি এটি একটি প্রদান করছি intfloatএবং intআপনার মেশিনে একই আকার হতে পারে, তবে 0.0fবাস্তবে doubleএকটি বৈকল্পিক যুক্তি তালিকায় ধাক্কা দেওয়ার সময় এটি রূপান্তরিত হয় (এবং এটি printfপ্রত্যাশা করে)। সংক্ষেপে, আপনি যে printfসুনির্দিষ্ট ব্যবহার করছেন তা এবং আপনি যে যুক্তি সরবরাহ করছেন তার উপর ভিত্তি করে আপনি দর কষাকষির শেষ করছেন না ।
WhozCraig

22
ভারার্গস-ফাংশনগুলি স্বয়ংক্রিয়ভাবে ফাংশন আর্গুমেন্টগুলিকে সংশ্লিষ্ট প্যারামিটারের ধরণে রূপান্তর করে না, কারণ তারা পারে না। প্রোটোটাইপ সহ নন-ভারার্গস ফাংশনের বিপরীতে প্রয়োজনীয় তথ্য সংকলকটিতে উপলভ্য নয়।
ইওএফ

4
ওহো ... "বৈচিত্র্য।" আমি সবেমাত্র একটি নতুন শব্দ শিখেছি ...
মাইক রবিনসন


4
পরবর্তী জিনিসটি চেষ্টা করার (uint64_t)0পরিবর্তে একটি পাস করা 0এবং দেখুন যে আপনি এখনও এলোমেলো আচরণ পান (ধরে নিচ্ছেন doubleএবং uint64_tএকই আকার এবং প্রান্তিককরণ আছে)। বিভিন্ন রেজিস্টারগুলিতে বিভিন্ন ধরণের পাস হওয়ার কারণে আউটপুটটি এখনও কিছু প্ল্যাটফর্মগুলিতে (যেমন x86_64) এলোমেলো থাকবে will
ইয়ান অ্যাবট

উত্তর:


121

"%f"বিন্যাস ধরনের একটি আর্গুমেন্ট প্রয়োজন double। আপনি এটিকে ধরণের যুক্তি দিচ্ছেন int। সে কারণেই আচরণটি সংজ্ঞায়িত।

মানটি গ্যারান্টি দেয় না যে অল-বিট-শূন্য একটি বৈধ উপস্থাপনা 0.0(যদিও এটি প্রায়শই হয়), বা কোনও doubleমান, বা যা intএবং doubleএকই আকার হয় (এটি মনে রাখবেন double, তা নয় float), অথবা, এমনকি তারা সমান হলেও আকার, যে তারা একইভাবে একটি ভেরিয়াদিক ফাংশন আর্গুমেন্ট হিসাবে পাস করা হয়।

এটি আপনার সিস্টেমে "কাজ করা" হতে পারে। এটি অপরিজ্ঞাত আচরণের সবচেয়ে খারাপ সম্ভাব্য লক্ষণ কারণ এটি ত্রুটি নির্ণয় করা কঠিন করে তোলে।

N1570 7.21.6.1 অনুচ্ছেদ 9:

... কোনও যুক্তি যদি সম্পর্কিত রূপান্তর সম্পর্কিত নির্দিষ্টকরণের জন্য সঠিক ধরণের না হয় তবে আচরণটি অপরিজ্ঞাত।

প্রকারের যুক্তিগুলিতে floatপ্রচার করা হয় double, এ কারণেই এটি কার্যকর হয় printf("%f\n",0.0f)। সংখ্যার সংখ্যার সংকীর্ণের থেকে intপ্রমোশন করা intবা উন্নীত করা হয় unsigned int। এই প্রচার বিধিগুলি (N1570 6.5.2.2 অনুচ্ছেদ 6 দ্বারা নির্দিষ্ট) এর ক্ষেত্রে সহায়তা করে না printf("%f\n", 0)

মনে রাখবেন যে আপনি যদি 0কোনও অ-বৈকল্পিক ফাংশনে একটি ধ্রুবকটি সরবরাহ করেন যা একটি doubleযুক্তির প্রত্যাশা করে , তবে ফাংশনটির প্রোটোটাইপটি দৃশ্যমান বলে ধরে নিয়ে আচরণটি ভালভাবে সংজ্ঞায়িত হয়েছে। উদাহরণস্বরূপ, sqrt(0)(পরে #include <math.h>) পরোক্ষভাবে যুক্তি পরিবর্তন করে 0থেকে intথেকে double- কারণ কম্পাইলার ঘোষণা থেকে দেখতে পারেন sqrtযে এটি একটি আশা doubleযুক্তি। এটির জন্য এ জাতীয় কোনও তথ্য নেই printf। ভেরিয়াদিক ফাংশনগুলির মতো printfবিশেষ এবং এগুলিতে কল লিখিত ক্ষেত্রে আরও যত্নের প্রয়োজন।


13
এখানে বেশ কয়েকটি দুর্দান্ত মূল পয়েন্ট। প্রথমত, এটি এতটা doubleনয় floatযে অপের প্রস্থের অনুমানটি ধরে রাখতে পারে না (সম্ভবত না)। দ্বিতীয়ত, অনুমান যে পূর্ণসংখ্যা শূন্য এবং ভাসমান-পয়েন্ট শূন্য একই বিট প্যাটার্ন রয়েছে তাও ধারণ করে না। ভাল কাজ
অরবিট

4
@ লুকাসট্রজেসনিউইস্কি: ঠিক আছে, তবে আমার উত্তর কীভাবে প্রশ্ন জিজ্ঞেস করে তা আমি দেখতে পাই না। আমি কেন এমনটি ব্যাখ্যা না করেই floatপ্রচারিত হয়েছি তা জানিয়েছিলাম double, তবে এটি মূল বিষয় ছিল না।
কিথ থমসন

4
@ রবার্টব্রিস্টো-জনসন: সংকলকগণের জন্য বিশেষ হুকের দরকার নেই printf, যদিও জিসিসি, উদাহরণস্বরূপ, এর কিছু রয়েছে যাতে এটি ত্রুটিগুলি নির্ণয় করতে পারে ( যদি বিন্যাসের স্ট্রিংটি আক্ষরিক হয়)। সংকলকটি এর printfথেকে ঘোষণাটি দেখতে পারে <stdio.h>, যা এটি বলে যে প্রথম প্যারামিটারটি একটি const char*এবং বাকীটি দ্বারা নির্দেশিত , ...। না, %fহয় double(এবং floatউন্নীত করা হয় double), এবং %lfজন্য long double। সি স্ট্যান্ডার্ড একটি স্ট্যাক সম্পর্কে কিছুই বলে না। এটি printfকেবল যখন সঠিকভাবে বলা হয় তার আচরণ নির্দিষ্ট করে ।
কিথ থম্পসন

4
@ রবার্টব্রিস্টো-জনসন: পুরাতন ধাক্কায়, "লিন্ট" প্রায়শই জিসিসি দ্বারা সম্পাদিত অতিরিক্ত অতিরিক্ত কিছু পরীক্ষা করে। একটি floatপাস printfউন্নীত হয় double; এটি সম্পর্কে মায়াবী কিছুই নেই, এটি ভেরিয়াদিক ফাংশনগুলি কল করার জন্য একটি ভাষা নিয়ম। printfকলকারী তার কাছে যা দাবি করেছে তা ফর্ম্যাট স্ট্রিংয়ের মাধ্যমেই জানে ; যদি দাবিটি ভুল হয় তবে আচরণটি সংজ্ঞায়িত।
কিথ থমসন

4
ক্ষুদ্র সংশোধন: lদৈর্ঘ্য পরিবর্তক "একটি নিম্নলিখিত উপর কোনো প্রভাব নেই a, A, e, E, f, F, g, অথবা Gরূপান্তর specifier", একটি জন্য দৈর্ঘ্য পরিবর্তক long doubleরূপান্তর হয় L। (@ রবার্টব্রিস্টো-জনসনও আগ্রহী হতে পারেন)
ড্যানিয়েল ফিশার

58

প্রথমত, যেমন বেশ কিছু উত্তর স্পর্শ কিন্তু, আমার মন, বানান আউট পরিষ্কারভাবে যথেষ্ট: এটা করে কাজে একটি পূর্ণসংখ্যা প্রদান সবচেয়ে প্রেক্ষিতে যেখানে একটি লাইব্রেরি ফাংশন একটি সময় লাগে doubleবা floatযুক্তি। সংকলক স্বয়ংক্রিয়ভাবে একটি রূপান্তর sertোকাবে। উদাহরণস্বরূপ, sqrt(0)ভালভাবে সংজ্ঞায়িত করা হয়েছে এবং ঠিক তেমন আচরণ করবে sqrt((double)0)এবং সেখানে ব্যবহৃত অন্য কোনও পূর্ণসংখ্যার ধরণের এক্সপ্রেশনটির ক্ষেত্রেও এটি একই।

printfএটা ভিন্ন. এটি ভিন্ন কারণ এটি একটি পরিবর্তনশীল সংখ্যক আর্গুমেন্ট নেয়। এর ফাংশন প্রোটোটাইপ হয়

extern int printf(const char *fmt, ...);

অতএব, আপনি যখন লিখুন

printf(message, 0);

সংকলকটি কী ধরণের দ্বিতীয় printf তর্কটি প্রত্যাশা করে সে সম্পর্কে কোনও তথ্য নেই । এটিতে কেবল যুক্তি প্রকাশের ধরণ রয়েছে, যা intদিয়ে যাওয়া। অতএব, বেশিরভাগ লাইব্রেরির ফাংশনগুলির বিপরীতে, প্রোগ্রামার আপনার পক্ষে, আর্গুমেন্টের তালিকাটি বিন্যাসের স্ট্রিংয়ের প্রত্যাশার সাথে মেলে তা নিশ্চিত করে তোলা।

(আধুনিক সংকলকগণ একটি ফর্ম্যাট স্ট্রিংটি সন্ধান করতে পারে এবং আপনাকে জানাতে পারে যে আপনার কাছে কী ধরনের অমিল রয়েছে তবে তারা কী বোঝাতে চেয়েছিল তা সম্পাদন করার জন্য রূপান্তরগুলি সন্নিবেশ করা শুরু করবে না, কারণ আপনার কোডটি এখন ভাঙা উচিত, যখন আপনি লক্ষ্য করবেন , কয়েক বছর পরে কম সহায়ক সংকলক দিয়ে পুনর্নির্মাণ যখন।)

এখন, প্রশ্নের অর্ধেকটি ছিল: বেশিরভাগ আধুনিক সিস্টেমে (int) 0 এবং (ভাসমান) 0.0 টি দেওয়া, উভয়ই 32 বিট হিসাবে উপস্থিত সমস্তগুলি শূন্য, কেন এটি দুর্ঘটনাক্রমে কার্যকর হয় না? সি স্ট্যান্ডার্ডটি কেবল বলেছে "এটি কাজ করার দরকার নেই, আপনি নিজেরাই আছেন", তবে এটি কাজ করবে না কেন দুটি সবচেয়ে সাধারণ কারণ সম্পর্কে আমাকে বানান করতে দিন; যে সম্ভবত সাহায্য করবে আপনি বুঝতে কেন এটা প্রয়োজনীয় না।

প্রথমত, reasonsতিহাসিক কারণে, আপনি যখন floatএকটি পরিবর্তনশীল আর্গুমেন্ট তালিকার মাধ্যমে পাস করেন এটি উন্নীত হয় double, যা বেশিরভাগ আধুনিক সিস্টেমে 64৪ বিট প্রশস্ত। সুতরাং তাদের printf("%f", 0)32 টির প্রত্যাশী কেবল 32 শূন্য বিট পাস করুন cal

দ্বিতীয়, সমান তাত্পর্যপূর্ণ কারণ হ'ল ভাসমান-পয়েন্ট ফাংশন আর্গুমেন্টগুলি পূর্ণসংখ্যার তর্কগুলির চেয়ে আলাদা জায়গায় পাস করা যেতে পারে । উদাহরণস্বরূপ, বেশিরভাগ সিপিইউতে পূর্ণসংখ্যা এবং ফ্লোটিং-পয়েন্ট মানগুলির জন্য পৃথক রেজিস্টার ফাইল থাকে, সুতরাং এটি একটি নিয়ম হতে পারে যে 0 থেকে 4 এর মধ্যে আর্গুমেন্টগুলি রেজিস্টারগুলিতে r4 এর মাধ্যমে r4 এর মাধ্যমে পূর্ণসংখ্যা হয়, তবে f4-এর মাধ্যমে f0 হয় যদি তারা ভাসমান-পয়েন্ট হয়। সুতরাং printf("%f", 0)যে শূন্য জন্য রেজিস্টার f1 দেখুন, কিন্তু এটি মোটেও নেই।


4
এমন কোনও আর্কিটেকচার রয়েছে যা ভ্যারিয়্যাডিক ফাংশনগুলির জন্য রেজিস্টার ব্যবহার করে, এমনকি তাদের মধ্যে যেগুলি সাধারণ ক্রিয়াকলাপের জন্য ব্যবহার করে? আমি ভেবেছিলাম যে অন্য কারণগুলি [ভাসা / সংক্ষিপ্ত / গৃহীত যুক্তিগুলি বাদ দিয়ে] ঘোষিত করা যেতে পারে তারপরেও বৈকল্পিক ফাংশনগুলি যথাযথভাবে ঘোষণা করা দরকার ()
র্যান্ডম 832

4
@ র্যান্ডম 832 আজকাল, বৈকল্পিক এবং একটি সাধারণ ফাংশনের কলিং কনভেনশনের মধ্যে কেবলমাত্র পার্থক্যটি হ'ল কোনও ভেরিয়েডিককে সরবরাহ করা কিছু অতিরিক্ত ডেটা থাকতে পারে, যেমন সরবরাহিত আসল সংখ্যার গণনা। অন্যথায় সবকিছু সাধারণ ক্রিয়াকলাপের জন্য ঠিক একই জায়গায় যায়। উদাহরণস্বরূপ x86-64.org/docamentation/abi.pdf- এর বিভাগ 3.2 দেখুন , যেখানে ভেরিয়াদিকের একমাত্র বিশেষ চিকিত্সাটি একটি ইঙ্গিতটি দেওয়া হয়েছে AL। (হ্যাঁ, এর অর্থ এটি বাস্তবায়ন করা va_argআগের চেয়ে অনেক জটিল))
zwol

@ র্যান্ডম 832: আমি সর্বদা ভেবেছিলাম কারণটির কারণ হ'ল কিছু স্থাপত্যগুলিতে একটি নির্দিষ্ট নম্বর এবং প্রকারের যুক্তিগুলির সাথে বিশেষ নির্দেশাবলী ব্যবহার করে আরও দক্ষতার সাথে প্রয়োগ করা যেতে পারে।
celtschk

@celtschk আপনি SPARC এবং IA64 এর "রেজিস্টার উইন্ডোজ" সম্পর্কে ভাবছেন, যা খুব কম সংখ্যক তর্ক যুক্তি দিয়ে ফাংশন কলগুলির সাধারণ ক্ষেত্রে ত্বরান্বিত করার কথা ছিল (হায়, বাস্তবে তারা কেবল বিপরীতে কাজ করে)। তারা বিশেষত ভেরিয়াদিক ফাংশন কলগুলি চিকিত্সার জন্য সংকলকটির প্রয়োজন হয় না, কারণ যে কোনও একটি কল সাইটে আর্গুমেন্টের সংখ্যা সর্বদা একটি সংকলন-সময় ধ্রুবক থাকে, নির্বিশেষে কলিটি বৈচিত্রপূর্ণ কিনা।
zwol

@ জওয়ল: না, আমি ৮০৮86-এর ret nনির্দেশনার কথা ভাবছিলাম , যেখানে nএকটি হার্ড-কোডেড পূর্ণসংখ্যা ছিল, যা বৈকল্পিক ফাংশনের জন্য প্রযোজ্য ছিল না। তবে আমি জানি না যে কোনও সি সংকলক আসলে এর সদ্ব্যবহার করেছে কিনা (নন-সি সংকলক অবশ্যই করেছিলেন)।
celtschk

13

সাধারণত যখন আপনি কোনও ফাংশন কল করেন যা একটি আশা করে doubleতবে আপনি একটি সরবরাহ করেন int, সংকলক স্বয়ংক্রিয়ভাবে আপনার জন্য একটিতে রূপান্তরিত হবে double। এটি সঙ্গে হয় না printf, কারণ আর্গুমেন্টগুলির ধরণগুলি ফাংশন প্রোটোটাইপে নির্দিষ্ট করা হয়নি - সংকলক জানে না যে কোনও রূপান্তর প্রয়োগ করা উচিত।


4
এছাড়াও, printf() বিশেষত এমনভাবে নকশা করা হয়েছে যাতে এর তর্কগুলি যে কোনও ধরণের হতে পারে। ফর্ম্যাট-স্ট্রিংয়ের প্রতিটি উপাদান দ্বারা কোন ধরণের প্রত্যাশা করা উচিত তা অবশ্যই আপনার জানা উচিত এবং আপনাকে অবশ্যই এটি সঠিকভাবে সরবরাহ করতে হবে।
মাইক রবিনসন

@ মাইকরোবিনসন: আচ্ছা, যে কোনও আদিম সি টাইপ। যা সমস্ত সম্ভাব্য ধরণের একটি খুব খুব ছোট উপসেট।
ম্যাসাল্টারস

13

কেন একটি ভাসা আক্ষরিক পরিবর্তে একটি পূর্ণসংখ্যার আক্ষরিক ব্যবহার এই আচরণের কারণ?

কারণ 1 ম হিসাবে printf()টেপা প্যারামিটার নেই const char* formatstring। এটি ...বাকী সকলের জন্য সি-স্টাইলের উপবৃত্ত ( ) ব্যবহার করে।

এটি কেবলমাত্র ফর্ম্যাট স্ট্রিংয়ে দেওয়া বিন্যাসের ধরণ অনুসারে সেখানে পাস করা মানগুলি কীভাবে ব্যাখ্যা করতে হবে তা ঠিক করে।

চেষ্টা করার সময় আপনার একই ধরণের অপরিজ্ঞাত আচরণ হবে

 int i = 0;
 const double* pf = (const double*)(&i);
 printf("%f\n",*pf); // dereferencing the pointer is UB

4
কিছু নির্দিষ্ট বাস্তবায়ন printfসেভাবে চলতে পারে (পাস করা আইটেমগুলি মান নয়, ঠিকানা নয়)। সি স্ট্যান্ডার্ডটি কীভাবে printf এবং অন্যান্য বৈকল্পিক ফাংশনগুলি কাজ করে তা নির্দিষ্ট করে না , এটি কেবল তাদের আচরণটি নির্দিষ্ট করে specif বিশেষত, স্ট্যাক ফ্রেমের কোনও উল্লেখ নেই।
কিথ থম্পসন

একটি ছোট টাল: printfআছে এক টাইপ প্যারামিটার, বিন্যাস স্ট্রিং, ধরনের যা const char*। বিটিডাব্লু, প্রশ্নটি সি এবং সি উভয়কে ট্যাগ করা হয়েছে, এবং সি সত্যই আরও প্রাসঙ্গিক; আমি সম্ভবত reinterpret_castএকটি উদাহরণ হিসাবে ব্যবহার করা হবে না ।
কিথ থম্পসন

কেবল একটি আকর্ষণীয় পর্যবেক্ষণ: একই অপরিবর্তিত আচরণ, এবং সম্ভবত অভিন্ন ব্যবস্থার কারণে, তবে বিশদে কিছুটা পার্থক্য সহ: প্রশ্ন হিসাবে একটি ইন্ট পাস করাতে, ইউবি প্রিন্টফের মধ্যে ঘটে যখন আন্তঃকে দ্বিগুণ হিসাবে ব্যাখ্যা করার চেষ্টা করা হয় - আপনার উদাহরণে , এটি আগে থেকেই ঘটে বাহিরে যখন PF dereferencing ...
আকনকাগুয়া

@ আঙ্কাকাগুয়া যোগ করেছেন স্পষ্টতা।
ῥεῖ

এই কোডের নমুনা হ'ল কঠোরভাবে Aliasing লঙ্ঘনের জন্য ইউবি, প্রশ্নটি যা জিজ্ঞাসা করছে তার সম্পূর্ণ ভিন্ন সমস্যা। উদাহরণস্বরূপ আপনি পূর্ণসংখ্যার বিভিন্ন রেজিস্টারে ভাসমানগুলি পাস করার সম্ভাবনাটিকে সম্পূর্ণভাবে উপেক্ষা করেন।
এমএম

12

একটি ভুল-ম্যাচ printf()নির্দিষ্টকারী "%f"এবং টাইপ (int) 0ব্যবহার অনির্ধারিত আচরণের দিকে নিয়ে যায়।

যদি কোনও রূপান্তর স্পেসিফিকেশন অবৈধ হয় তবে আচরণটি সংজ্ঞায়িত। সি 11 ডিআর §7.21.6.1 9

প্রার্থীদের ইউবি হওয়ার কারণগুলি।

  1. এটি অনুমান অনুসারে ইউবি এবং সংকলনটি আরও ভাল - 'নূফ বললেন।

  2. doubleএবং intবিভিন্ন আকারের হয়।

  3. doubleএবং intবিভিন্ন স্ট্যাক ব্যবহার করে তাদের মানগুলি পাস করতে পারে (সাধারণ বনাম এফপিইউ স্ট্যাক।)

  4. একজন double 0.0 পারে সব শূন্য বিট প্যাটার্ন দ্বারা সংজ্ঞায়িত না। (বিরল)


10

এটি আপনার সংকলক সতর্কতা থেকে শেখার সেই দুর্দান্ত সুযোগগুলির মধ্যে একটি।

$ gcc -Wall -Wextra -pedantic fnord.c 
fnord.c: In function ‘main’:
fnord.c:8:2: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
  printf("%f\n",0);
  ^

বা

$ clang -Weverything -pedantic fnord.c 
fnord.c:8:16: warning: format specifies type 'double' but the argument has type 'int' [-Wformat]
        printf("%f\n",0);
                ~~    ^
                %d
1 warning generated.

সুতরাং, অপরিবর্তিত printfআচরণ তৈরি করছে কারণ আপনি এটিকে একটি বেমানান যুক্তি দিয়ে যাচ্ছেন।


9

আমি বিভ্রান্তিকর কি তা নিশ্চিত নই।

আপনার ফর্ম্যাট স্ট্রিং একটি আশা করে double; আপনি পরিবর্তে একটি সরবরাহ int

উভয় ধরণের একই বিট প্রস্থের সম্পূর্ণ অপ্রাসঙ্গিক কিনা তা বাদে এটি এর মতো ভাঙ্গা কোড থেকে হার্ড মেমরি লঙ্ঘনের ব্যতিক্রমগুলি এড়াতে আপনাকে সহায়তা করতে পারে।


4
@Voo: যে বিন্যাসে স্ট্রিংটি পরিবর্তক হয় দুর্ভাগ্যবশত নামে, কিন্তু আমি এখনও দেখতে পাচ্ছি না কেন আপনি মনে চাই যে intএখানে গ্রহণযোগ্য হতে পারে।
হালকা ঘোড়দৌড়

4
@ ভু: "(যা বৈধ ফ্লোট প্যাটার্ন হিসাবেও যোগ্যতা অর্জন করবে)" কেন intকোনও বৈধ ফ্লোট প্যাটার্ন হিসাবে যোগ্যতা অর্জন করবে ? দুইটির পরিপূরক এবং বিভিন্ন ভাসমান-পয়েন্ট এনকোডিংগুলিতে প্রায় কিছুই মিল নেই।
অরবিটে হালকাতা রেস

4
এটি বিভ্রান্তিকর কারণ, বেশিরভাগ লাইব্রেরির ফাংশনগুলির জন্য, 0টাইপ করা যুক্তিটির জন্য পূর্ণসংখ্যার শাব্দিক সরবরাহ করা doubleসঠিক কাজটি করে। এটি কোনও শিক্ষানবিশের কাছে সুস্পষ্ট নয় যে সংকলক printfদ্বারা যুক্তিযুক্ত স্লটগুলিতে সম্বোধনের জন্য একই রূপান্তরটি করে না %[efg]
zwol

4
@ ভু: যদি আপনি কীভাবে এটি ভয়াবহভাবে ভুল করতে পারেন সে বিষয়ে আগ্রহী হন, তবে x86-64 SysV ABI তে ভাসমান-পয়েন্ট যুক্তিগুলি পূর্ণসংখ্যার যুক্তিগুলির চেয়ে আলাদা রেজিস্ট্রার-সেটটিতে পাস করা হয়েছে তা বিবেচনা করুন।
ইওএফ

4
@ লাইটনেসেসেসিন অরবিট আমি মনে করি যে কেন কিছু ইউবি হয় তা নিয়ে আলোচনা করা সর্বদা যথাযথ , যার মধ্যে সাধারণত বাস্তবায়ন অক্ষাংশ কী অনুমোদিত এবং সাধারণ ক্ষেত্রে আসলে কী ঘটে তা নিয়ে কথা বলা জড়িত।
zwol

4

"%f\n"দ্বিতীয় printf()প্যারামিটারের টাইপ থাকলেই অনুমানযোগ্য ফলাফলের নিশ্চয়তা দেয় double। এর পরে, বৈকল্পিক ফাংশনগুলির একটি অতিরিক্ত যুক্তি ডিফল্ট আর্গুমেন্ট প্রচারের বিষয়। পূর্ণসংখ্যার যুক্তিগুলি পূর্ণসংখ্যার প্রচারের অধীনে আসে, যার ফলশ্রুতি-পয়েন্ট টাইপ করা মানগুলির ফলাফল কখনও হয় না। এবং floatপরামিতিগুলিতে উন্নীত হয় double

এটিকে শীর্ষে তুলতে: স্ট্যান্ডার্ডটি দ্বিতীয় যুক্তিটি floatবা doubleঅন্য কিছু হতে দেয় ।


4

কেন এটি আনুষ্ঠানিকভাবে ইউবি হয় তা এখন বেশ কয়েকটি উত্তরে আলোচিত হয়েছে।

আপনি কেন বিশেষভাবে এই আচরণটি পাওয়ার কারণটি প্ল্যাটফর্ম নির্ভর, তবে সম্ভবত নিম্নলিখিতটি রয়েছে:

  • printfমান ভারার্গ প্রচার অনুসারে এর যুক্তিগুলি প্রত্যাশা করে। মানে একটি যে floatএকটি হতে হবে doubleএবং কিছু ছোট একটি চেয়ে intএকটি হতে হবে int
  • আপনি এমন একটি পাশ intযাচ্ছেন যেখানে ফাংশনটি প্রত্যাশা করে a double। আপনার intসম্ভবত 32 বিট, আপনার double64 বিট। তার মানে হল যে চারটি স্ট্যাক বাইট যেখানে আর্গুমেন্টটি বসার কথা সেখান থেকে শুরু হয়েছিল 0, তবে নিম্নলিখিত চারটি বাইটে যথেচ্ছ বিষয়বস্তু রয়েছে। এটি প্রদর্শিত মান নির্ধারণের জন্য ব্যবহৃত হয় যা প্রদর্শিত হয়।

0

এই "নির্ধারিত মান" ইস্যুটির মূল কারণটি ম্যাক্রো বহন করে এমন ধরণের পয়েন্টারগুলিতে ভেরিয়েবল পরামিতি বিভাগে যে intমানটি দেওয়া হয়েছিল তাতে পয়েন্টারের কাস্টে দাঁড়িয়ে থাকে ।printfdoubleva_arg

এটি একটি মেমরি অঞ্চলে একটি রেফারেন্সিং ঘটায় যা প্রিন্টফের সাথে প্যারামিটার হিসাবে পাস করা মান দিয়ে পুরোপুরি শুরু করা হয়নি, কারণ doubleআকারের মেমরি বাফার অঞ্চলটি intআকারের চেয়ে বেশি ।

সুতরাং, যখন এই পয়েন্টারটিকে অবজ্ঞাপূর্ণ করা হয়, তখন এটি একটি নির্ধারিত মান, বা আরও ভাল একটি "মান" ফিরিয়ে আনা হয় যা অংশে প্যারামিটার হিসাবে পাস করা মান ধারণ করে এবং printfবাকী অংশের জন্য অন্য স্ট্যাক বাফার অঞ্চল বা একটি কোড অঞ্চল থেকেও আসতে পারে ( একটি মেমরি ফল্ট ব্যতিক্রম উত্থাপন), একটি আসল বাফার ওভারফ্লো


এটি হবে "printf," এবং "va_arg" ... এর semplificated কোড বাস্তবায়নের এই নির্দিষ্ট অংশ বিবেচনা করতে পারেন

printf,

va_list arg;
....
case('%f')
      va_arg ( arg, double ); //va_arg is a macro, and so you can pass it the "type" that will be used for casting the int pointer argument of printf..
.... 


ডাবল ভ্যালু প্যারামিটার কোড কেস ম্যানেজমেন্টের ভিপ্রিন্টফের (gnu ইম্পল বিবেচনা করে) বাস্তব বাস্তবায়ন হ'ল:

if (__ldbl_is_dbl)
{
   args_value[cnt].pa_double = va_arg (ap_save, double);
   ...
}



va_arg

char *p = (double *) &arg + sizeof arg;  //printf parameters area pointer

double i2 = *((double *)p); //casting to double because va_arg(arg, double)
   p += sizeof (double);



তথ্যসূত্র

  1. "প্রিন্টফ" (ভিপ্রিন্টফ) এর gnu প্রকল্প গ্লিবসি বাস্তবায়ন
  2. প্রিন্টফের বিভাজন কোডের উদাহরণ
  3. ভিএআরগের বিভাজন কোডের উদাহরণ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.