কেন এই কোড আউটপুট দেয় C++Sucks
? এর পেছনে ধারণাটি কী?
#include <stdio.h>
double m[] = {7709179928849219.0, 771};
int main() {
m[1]--?m[0]*=2,main():printf((char*)m);
}
skcuS++C
।
কেন এই কোড আউটপুট দেয় C++Sucks
? এর পেছনে ধারণাটি কী?
#include <stdio.h>
double m[] = {7709179928849219.0, 771};
int main() {
m[1]--?m[0]*=2,main():printf((char*)m);
}
skcuS++C
।
উত্তর:
নম্বরটিতে 7709179928849219.0
একটি 64-বিট হিসাবে নিম্নলিখিত বাইনারি উপস্থাপনা রয়েছে double
:
01000011 00111011 01100011 01110101 01010011 00101011 00101011 01000011
+^^^^^^^ ^^^^---- -------- -------- -------- -------- -------- --------
+
চিহ্নটির অবস্থান প্রদর্শন করে; ^
ঘাতক এবং-
অংশক এর (এক্সপোনেন্ট ছাড়া মান অর্থাৎ)।
যেহেতু উপস্থাপনাটি বাইনারি এক্সপোনেন্ট এবং ম্যান্টিসা ব্যবহার করে, সংখ্যাকে দ্বিগুণ করে একের সাথে এক্সপোনেন্টকে বৃদ্ধি করে। আপনার প্রোগ্রামটি যথাযথভাবে এটি 771 বার করে, তাই 1075-এ শুরু হওয়া সূচকটি (এর দশমিক প্রতিনিধিত্ব 10000110011
) শেষে 1075 + 771 = 1846 হয়ে যায়; 1846 এর বাইনারি উপস্থাপনা হয় 11100110110
। ফলাফলের ধরণটি এরকম দেখাচ্ছে:
01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011
-------- -------- -------- -------- -------- -------- -------- --------
0x73 's' 0x6B 'k' 0x63 'c' 0x75 'u' 0x53 'S' 0x2B '+' 0x2B '+' 0x43 'C'
এই প্যাটার্নটি আপনি যে স্ট্রিংটি প্রিন্ট করেছেন কেবল তার পিছনেই দেখায় to একই সময়ে, অ্যারের দ্বিতীয় উপাদানটি শূন্য হয়ে যায়, নাল টার্মিনেটর সরবরাহ করে, স্ট্রিংটি পাসের জন্য উপযুক্ত করে তোলে printf()
।
7709179928849219
মানটি পেস্ট করেছি এবং বাইনারি উপস্থাপনা ফিরে পেয়েছি।
আরও পঠনযোগ্য সংস্করণ:
double m[2] = {7709179928849219.0, 771};
// m[0] = 7709179928849219.0;
// m[1] = 771;
int main()
{
if (m[1]-- != 0)
{
m[0] *= 2;
main();
}
else
{
printf((char*) m);
}
}
এটি পুনরাবৃত্তি কল করে main()
771 বার ।
শুরুতে, m[0] = 7709179928849219.0
যা দাঁড়িয়েছে জন্য C++Suc;C
। প্রতিটি কলে, m[0]
দ্বিগুণ হয়ে যায়, শেষ দুটি অক্ষর "মেরামত" করতে। শেষ কলটিতে m[0]
ASCII চর প্রতিনিধিত্ব করে C++Sucks
এবং m[1]
এতে কেবল জিরো থাকে, সুতরাং এতে স্ট্রিংয়ের জন্য নাল টার্মিনেটর রয়েছেC++Sucks
। সব অনুমানের অধীনেm[0]
8 টি বাইটে সঞ্চিত হয়, সুতরাং প্রতিটি চরটি 1 বাইট নেয়।
পুনরাবৃত্তি এবং অবৈধ main()
কলিং ছাড়া এটি এর মতো দেখতে পাবেন:
double m[] = {7709179928849219.0, 0};
for (int i = 0; i < 771; i++)
{
m[0] *= 2;
}
printf((char*) m);
দাবি অস্বীকার : এই উত্তরটি প্রশ্নের মূল ফর্মটিতে পোস্ট করা হয়েছিল, এতে কেবল সি ++ উল্লেখ করা হয়েছিল এবং এতে একটি সি ++ শিরোনাম অন্তর্ভুক্ত ছিল। প্রশ্নের খাঁটি সিতে রূপান্তরটি মূল প্রশ্নকর্তার ইনপুট ছাড়াই সম্প্রদায় করেছিল।
সাধারণভাবে বলতে গেলে, এই প্রোগ্রামটি সম্পর্কে তর্ক করা অসম্ভব কারণ এটি অসম্পূর্ণ (যেমন এটি আইনী সি ++ নয়)। এটি সি ++ 11 [বেসিক.স্টার্ট.মাইন] পি 3 লঙ্ঘন করে:
ফাংশন প্রধান কোনও প্রোগ্রামের মধ্যে ব্যবহার করা হবে না।
এটি অন্যদিকে, এটি নির্ভর করে যে একটি সাধারণ গ্রাহক কম্পিউটারে, একটি double
8 বাইট দীর্ঘ হয় এবং একটি নির্দিষ্ট সুপরিচিত অভ্যন্তরীণ উপস্থাপনা ব্যবহার করে। অ্যারের প্রাথমিক মানগুলি গণনা করা হয় যাতে "অ্যালগরিদম" সম্পাদন করা হলে, প্রথমটির চূড়ান্ত মানটি double
এমন হবে যে অভ্যন্তরীণ উপস্থাপনা (8 বাইট) 8 টি অক্ষরের ASCII কোড হবে C++Sucks
। অ্যারেতে দ্বিতীয় উপাদানটি তখন 0.0
, যার প্রথম বাইটটি 0
অভ্যন্তরীণ উপস্থাপনায় থাকে এটি একে বৈধ সি-স্টাইলের স্ট্রিং তৈরি করে। এরপরে এটি ব্যবহার করে আউটপুট প্রেরণ করা হয় printf()
।
এইচডাব্লুতে এটি চালানোর ফলে উপরের কিছু আবদ্ধ না করে এর পরিবর্তে আবর্জনা পাঠ্য হবে (বা সম্ভবত সীমার বাইরেও অ্যাক্সেস)।
basic.start.main
এর একই শব্দযুক্ত সাথে 3.6.1 / 3 ছিল had
main()
।
সম্ভবত কোডটি বোঝার সবচেয়ে সহজ উপায় হ'ল বিপরীতে থাকা জিনিসগুলির মাধ্যমে কাজ করা। আমরা মুদ্রণের জন্য একটি স্ট্রিং দিয়ে শুরু করব - ভারসাম্যের জন্য, আমরা "সি ++ রকস" ব্যবহার করব। গুরুতর বিষয়: ঠিক আসলটির মতোই, এটি ঠিক আটটি অক্ষর দীর্ঘ long যেহেতু আমরা আসলটির মতো (মোটামুটি) করতে যাচ্ছি এবং এটি বিপরীতক্রমে মুদ্রণ করব, তাই আমরা এটিকে বিপরীত ক্রমে রেখে শুরু করব। আমাদের প্রথম পদক্ষেপের জন্য, আমরা কেবল সেই বিট প্যাটার্নটিকে একটি হিসাবে দেখব double
এবং ফলাফলটি মুদ্রণ করব:
#include <stdio.h>
char string[] = "skcoR++C";
int main(){
printf("%f\n", *(double*)string);
}
এটি উত্পাদন করে 3823728713643449.5
। সুতরাং, আমরা এমন কোনও উপায়ে পরিচালনা করতে চাই যা সুস্পষ্ট নয়, তবে বিপরীত করা সহজ। আমি আধা-নির্বিচারে 256 দ্বারা গুণন বেছে নেব, যা আমাদের দেয় 978874550692723072
। এখন, আমাদের কেবল 256 দ্বারা বিভক্ত করার জন্য কিছু অবলম্বন কোড লিখতে হবে, তারপরে বিপরীত ক্রমে এর পৃথক বাইটগুলি মুদ্রণ করতে হবে:
#include <stdio.h>
double x [] = { 978874550692723072, 8 };
char *y = (char *)x;
int main(int argc, char **argv){
if (x[1]) {
x[0] /= 2;
main(--x[1], (char **)++y);
}
putchar(*--y);
}
এখন আমাদের কাছে প্রচুর ingালাই রয়েছে, (পুনরাবৃত্ত) main
সম্পূর্ণভাবে উপেক্ষা করা হয়েছে (তবে মূল্যবৃদ্ধি এবং হ্রাস পেতে মূল্যায়ন একেবারে গুরুত্বপূর্ণ), এবং অবশ্যই আমরা কী করছি এই বিষয়টি আড়াল করার জন্য সম্পূর্ণ স্বেচ্ছাচারিত সংখ্যার সংখ্যা সত্যিই বেশ সোজা।
অবশ্যই, পুরো পয়েন্টটি যেহেতু অস্পষ্ট, তাই যদি আমাদের মনে হয় আমরা আরও পদক্ষেপ নিতে পারি। যেমন উদাহরণস্বরূপ, আমরা আমাদের if
বিবৃতিটিকে একক অভিব্যক্তিতে পরিণত করতে শর্ট সার্কিট মূল্যায়নের সুবিধা নিতে পারি , তাই মূল চেহারাটি এইরকম দেখাচ্ছে:
x[1] && (x[0] /= 2, main(--x[1], (char **)++y));
putchar(*--y);
অপ্রচলিত কোড (এবং / অথবা কোড গল্ফ) এর সাথে অভ্যস্ত না এমন কারও কাছে এটি সত্যই অদ্ভুত লাগতে শুরু করে - and
কিছু অর্থহীন ভাসমান পয়েন্ট সংখ্যাটির লজিকাল এবং তার থেকে প্রাপ্ত ফেরতের মান গণনা করা এবং বাতিল করাmain
, যা এমনকি কোনও ফিরিয়ে দেয় না মান। সবচেয়ে খারাপ, সংক্ষিপ্ত-সার্কিট মূল্যায়ন কীভাবে উপলব্ধি করে (এবং চিন্তা না করে), এটি অবিলম্বে সুস্পষ্টও না হতে পারে যে এটি কীভাবে অসীম পুনরাবৃত্তি এড়ায়।
আমাদের পরবর্তী পদক্ষেপটি সম্ভবত প্রতিটি অক্ষর মুদ্রণটিকে সেই চরিত্রটি সন্ধান থেকে পৃথক করা। আমরা যথাযথ সহজেই সঠিক অক্ষরটি যেখান থেকে ফেরতের মান হিসাবে তৈরি করতে পারি main
এবং কী main
ফিরে আসে তা মুদ্রণ করে এটি করতে পারি :
x[1] && (x[0] /= 2, putchar(main(--x[1], (char **)++y)));
return *--y;
কমপক্ষে আমার কাছে, এটি যথেষ্ট আপত্তিজনক বলে মনে হচ্ছে, তাই আমি এটি এ ছেড়ে দেব।
এটি কেবল একটি ডাবল অ্যারে (16 বাইট) তৈরি করছে যা - যদি চর অ্যারে হিসাবে ব্যাখ্যা করা হয় - "সি ++ সাকস" স্ট্রিংয়ের জন্য ASCII কোডগুলি তৈরি করুন
তবে কোডটি প্রতিটি সিস্টেমে কাজ করছে না, এটি নিম্নলিখিত কয়েকটি অপরিজ্ঞাত তথ্যের উপর নির্ভর করে:
নিম্নলিখিত কোডটি মুদ্রণ করে C++Suc;C
, সুতরাং সম্পূর্ণ গুণটি কেবল শেষ দুটি অক্ষরের জন্য
double m[] = {7709179928849219.0, 0};
printf("%s\n", (char *)m);
অন্যরা প্রশ্নটি পুরোপুরিভাবে ব্যাখ্যা করেছে, আমি একটি নোট যুক্ত করতে চাই যে এটি স্ট্যান্ডার্ড অনুযায়ী নির্ধারিত আচরণ ।
সি ++ 11 3.6.1 / 3 প্রধান ফাংশন
ফাংশন প্রধান কোনও প্রোগ্রামের মধ্যে ব্যবহার করা হবে না। মূলটির লিঙ্কেজ (3.5) বাস্তবায়ন-সংজ্ঞায়িত। এমন একটি প্রোগ্রাম যা মুখ্যটিকে মুছে ফেলা হিসাবে সংজ্ঞায়িত করে বা প্রধানকে ইনলাইন, স্ট্যাটিক বা কনস্টেক্সপ্র হিসাবে ঘোষিত হয় যা দুর্গঠিত। নামটির নামটি অন্যথায় সংরক্ষিত নয়। [উদাহরণস্বরূপ: সদস্য ফাংশন, শ্রেণি এবং গণনাগুলিকে প্রধান বলা যেতে পারে, অন্য নামের জায়গাগুলিতে সত্তাও। পরবর্তী উদাহরণ]
কোডটি আবার এভাবে লেখা যেতে পারে:
void f()
{
if (m[1]-- != 0)
{
m[0] *= 2;
f();
} else {
printf((char*)m);
}
}
এটি যা করছে তা বাইটের একটি সেট তৈরি করছে double
অ্যারেতেm
'সি ++ সাকস' অক্ষরের সাথে মিলিত হয়ে নাল-টার্মিনেটর দ্বারা অনুসরণ করা হয়। তারা কোডটি দ্বিগুণ করেছেন যা ডাবল মান বাছাই করে যা দ্বিগুণ হয়ে গেলে 77 77১ বার উত্পন্ন হয়, স্ট্যান্ডার্ড উপস্থাপনায়, অ্যারের দ্বিতীয় সদস্য দ্বারা সরবরাহিত নাল টার্মিনেটরের সাথে বাইট সেট করে।
মনে রাখবেন যে এই কোডটি ভিন্ন এন্ডিয়ান উপস্থাপনার অধীনে কাজ করবে না। এছাড়াও, কল করার main()
কঠোরভাবে অনুমতি নেই।
f
রিটার্ন কেন আসে int
?
int
প্রশ্নটিতে রিটার্নটি অনুলিপি করছিলাম । আমাকে এটি ঠিক করতে দিন।
প্রথমে আমাদের স্মরণ করা উচিত যে ডাবল যথার্থ সংখ্যাগুলি মেমরিতে বাইনারি ফর্ম্যাটে নিম্নরূপে সংরক্ষণ করা হয়:
(i) সাইন ইন করার জন্য 1 বিট
(ii) ঘাতকের জন্য 11 বিট
(iii) परिमाणের জন্য 52 বিট
বিটের ক্রমটি (i) থেকে কমে যায় (iii)।
প্রথমে দশমিক ভগ্নাংশ সংখ্যা সমান ভগ্নাংশ বাইনারি সংখ্যায় রূপান্তরিত হয় এবং তারপরে এটি বাইনারি আকারের আকারের ক্রম হিসাবে প্রকাশ করা হয়।
সুতরাং সংখ্যাটি 7709179928849219.0 হয়
(11011011000110111010101010011001010110010101101000011)base 2
=1.1011011000110111010101010011001010110010101101000011 * 2^52
দৈর্ঘ্যের বিট ১ বিবেচনা করার সময়, পরিমাপ পদ্ধতির সমস্ত ক্রম শুরু হওয়ার সাথে সাথে এটি অবহেলিত 1 ।
সুতরাং প্রস্থের অংশটি হয়ে যায়:
1011011000110111010101010011001010110010101101000011
এখন ক্ষমতা 2 হয় 52 , আমরা যেমন নম্বর biasing যোগ করতে হবে 2 ^ (এক্সপোনেন্ট জন্য বিট -1) -1 অর্থাত 2 ^ (11 -1) -1 = 1023 , তাই আমাদের সূচক হয়ে 52 + + 1023 = 1075
এখন আমাদের কোড mutiplies সঙ্গে সংখ্যা 2 , 771 বার যার দ্বারা বৃদ্ধি এক্সপোনেন্ট তোলে 771
সুতরাং আমাদের প্রকাশক (1075 + 771) = 1846 যার বাইনারি সমতুল্য (11100110110)
এখন আমাদের সংখ্যাটি ইতিবাচক তাই আমাদের সাইন বিট 0 হয় ।
সুতরাং আমাদের পরিবর্তিত নম্বর হয়ে যায়:
সাইন বিট + এক্সপোনেন্ট + প্রস্থ (বিটগুলির সরল সংমিশ্রণ)
0111001101101011011000110111010101010011001010110010101101000011
যেহেতু মিটার চর পয়েন্টারে রূপান্তরিত হয়েছে আমরা এলএসডি থেকে বিট প্যাটার্নটিকে 8 এর অংশে বিভক্ত করব
01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011
(যার হেক্স সমতুল্য :)
0x73 0x6B 0x63 0x75 0x53 0x2B 0x2B 0x43
চরিত্রের মানচিত্র থেকে কোনটি প্রদর্শিত হয়েছে:
s k c u S + + C
এখন একবার এটি এম করা হয়ে গেলে [1] 0 হয় যার অর্থ একটি শূন্য চরিত্র
এখন ধরে নিচ্ছেন যে আপনি এই প্রোগ্রামটি একটি সামান্য এন্ডিয়ান মেশিনে চালাচ্ছেন (লোয়ার অর্ডার বিটটি নীচের ঠিকানায় সংরক্ষণ করা হয়) সুতরাং পয়েন্টার এম পয়েন্টারটি সর্বনিম্ন ঠিকানার বিটে এবং তারপরে 8 এর চকে বিট নিয়ে এগিয়ে যায় (টাইপ হিসাবে চরকে টানা * ) এবং প্রিন্টফ () শেষ খণ্ডে যখন 00000000 এর মুখোমুখি হয় ...
এই কোডটি বহনযোগ্য নয়।