c++ - switch - সি++ প্রোগ্রামিং বই




সি++ স্ট্যান্ডার্ড কি একটি প্রোগ্রাম ক্র্যাশ করার জন্য একটি অনির্দেশিত পুলকে মঞ্জুরি দেয়? (4)

আমি জানি যে সি +++ এর একটি "অপরিজ্ঞাত আচরণ" সংকলকটিকে যে কোনও কিছু করতে দেয় pretty তবে, আমার একটি ক্র্যাশ হয়েছিল যা আমাকে অবাক করেছিল, কারণ আমি ধরে নিয়েছিলাম যে কোডটি যথেষ্ট নিরাপদ was

এই ক্ষেত্রে, আসল সমস্যাটি কেবলমাত্র নির্দিষ্ট সংকলক ব্যবহার করে নির্দিষ্ট প্ল্যাটফর্মে ঘটেছিল এবং কেবলমাত্র অপটিমাইজেশন সক্ষম করা থাকলে।

সমস্যাটি পুনরুত্পাদন করতে এবং এটিকে সর্বোচ্চ থেকে সরল করার জন্য আমি বেশ কয়েকটি জিনিস চেষ্টা করেছিলাম। এখানে Serialize নামক একটি ফাংশনের এক্সট্র্যাক্ট, এটি একটি বুল প্যারামিটার নেবে এবং স্ট্রিংটিকে true বা false কোনও বিদ্যমান গন্তব্য বাফারে অনুলিপি করবে।

এই ফাংশনটি কি কোনও কোড পর্যালোচনায় থাকবে, বলার কোনও উপায়ই থাকবে না যে, বাস্তবে, যদি বুল প্যারামিটারটি একটি অবিশ্বাস্য মান হয়?

// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
    // Determine which string to print based on boolValue
    const char* whichString = boolValue ? "true" : "false";

    // Compute the length of the string we selected
    const size_t len = strlen(whichString);

    // Copy string into destination buffer, which is zero-filled (thus already null-terminated)
    memcpy(destBuffer, whichString, len);
}

এই কোডটি ঝাঁকুনি 5.0.0 + অপ্টিমাইজেশান সহ কার্যকর করা হলে এটি ক্র্যাশ / ক্যান্সার হতে পারে।

প্রত্যাশিত টার্নারি-অপারেটর boolValue ? "true" : "false" boolValue ? "true" : "false" আমার পক্ষে যথেষ্ট নিরাপদ বলে মনে হচ্ছিল, আমি boolValue , " boolValue যা কিছু আবর্জনা মূল্য রয়েছে তা boolValue নয়, যেহেতু এটি সত্য বা মিথ্যা মূল্যায়ন করবে কোনওভাবেই" "

আমার কাছে একটি কম্পাইলার এক্সপ্লোরার উদাহরণ সেটআপ আছে যা ডিসমাসে সমস্যা দেখায়, এখানে সম্পূর্ণ উদাহরণ। দ্রষ্টব্য: সমস্যাটিকে তিরস্কার করার জন্য, আমি যে সংমিশ্রণটি খুঁজে পেয়েছি তা হ'ল -O2 অপ্টিমাইজেশানের সাথে ক্ল্যাং 5.0.0 ব্যবহার করে।

#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
    bool uninitializedBool;

   __attribute__ ((noinline))  // Note: the constructor must be declared noinline to trigger the problem
   FStruct() {};
};

char destBuffer[16];

// Small utility function that allocates and returns a string "true" or "false" depending on the value of the parameter
void Serialize(bool boolValue) {
    // Determine which string to print depending if 'boolValue' is evaluated as true or false
    const char* whichString = boolValue ? "true" : "false";

    // Compute the length of the string we selected
    size_t len = strlen(whichString);

    memcpy(destBuffer, whichString, len);
}

int main()
{
    // Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
    FStruct structInstance;

    // Output "true" or "false" to stdout
    Serialize(structInstance.uninitializedBool);
    return 0;
}

অপ্টিমাইজারের কারণে সমস্যা দেখা দেয়: "সত্য" এবং "মিথ্যা" স্ট্রিংগুলি কেবল দৈর্ঘ্যে 1 দ্বারা পৃথক হয় তা অনুমান করার পক্ষে যথেষ্ট চালাক ছিল So সুতরাং প্রকৃত দৈর্ঘ্য গণনা করার পরিবর্তে এটি নিজেই বুলের মান ব্যবহার করে, যা উচিত প্রযুক্তিগতভাবে 0 বা 1 হয়, এবং এর মতো হয়:

const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue;       // clang clever optimization

এটি যদিও "চালাক", তাই বলার জন্য, আমার প্রশ্নটি: সি ++ স্ট্যান্ডার্ডের দ্বারা কি একটি সংকলককে একটি বুল ধরে নিতে অনুমতি দেয় যে কেবলমাত্র '0' বা '1' এর অভ্যন্তরীণ সংখ্যাগত উপস্থাপনা থাকতে পারে এবং সেটিকে এভাবে ব্যবহার করতে পারে?

বা এটি বাস্তবায়ন-সংজ্ঞায়নের ক্ষেত্রে, এই ক্ষেত্রে বাস্তবায়নটি ধরে নিয়েছে যে এর সবগুলিতে কেবল 0 বা 1 থাকবে এবং অন্য কোনও মান অপরিবর্তিত আচরণের অঞ্চল?


হ্যাঁ, আইএসও সি ++ এই পছন্দটি করার জন্য বাস্তবায়নের অনুমতি দেয় (তবে প্রয়োজন হয় না)।

তবে এও নোট করুন যে আইএসও সি ++ এমন একটি সংকলককে কোড নির্গত করার অনুমতি দেয় যা উদ্দেশ্য অনুযায়ী ক্র্যাশ হয় (উদাহরণস্বরূপ একটি অবৈধ নির্দেশ সহ) যদি প্রোগ্রামটি ইউবির মুখোমুখি হয়, যেমন আপনাকে ত্রুটিগুলি খুঁজে পেতে সহায়তা করার উপায় হিসাবে। (বা এটি একটি ডেথস্টেশন 9000 কারণ কোনও কঠোরভাবে মেনে চলা সি ++ বাস্তবায়ন যে কোনও বাস্তব উদ্দেশ্যে কার্যকর হওয়ার জন্য যথেষ্ট নয়)। সুতরাং আইএসও সি ++ এমন একটি uint32_t এমন asm তৈরি করার মঞ্জুরি দেয় (সম্পূর্ণ ভিন্ন কারণে) এমনকি এমন একটি uint32_t যে একটি uint32_t যদিও কোনও ফাঁদের উপস্থাপনা ছাড়াই এটি একটি স্থির-লেআউট প্রকারের হতে হবে।

এটি বাস্তব বাস্তবায়ন কীভাবে কাজ করে সে সম্পর্কে একটি আকর্ষণীয় প্রশ্ন, তবে মনে রাখবেন যে উত্তরটি পৃথক হলেও আপনার কোডটি এখনও নিরাপদ হবে কারণ আধুনিক সি ++ অ্যাসেম্বলি ভাষার কোনও বহনযোগ্য সংস্করণ নয়।

আপনি x86-64 সিস্টেম ভি এবিআইয়ের জন্য সংকলন করছেন, যা উল্লেখ করে যে একটি রেজিস্টারে ফাংশন আর্গ হিসাবে একটি বুল নিখরচায় রেট true=1 এর 8 টি বিট বিট-প্যাটার্নগুলি false=0 এবং true=1 দ্বারা উপস্থাপিত হয়েছে । স্মৃতিতে, bool একটি 1-বাইট প্রকার যা আবার 0 বা 1 এর পূর্ণসংখ্যার মান হওয়া আবশ্যক।

(একটি এবিআই বাস্তবায়ন পছন্দগুলির একটি সেট যা একই প্ল্যাটফর্মের সংকলকরা এতে সম্মত হয় যাতে তারা কোড তৈরি করতে পারে যা একে অপরের ক্রিয়াকলাপগুলিতে কল করে, টাইপ আকার, কাঠামোর বিন্যাস নিয়ম এবং কলিং কনভেনশন সহ))

আইএসও সি ++ এটি নির্দিষ্ট করে না, তবে এবিআইয়ের এই সিদ্ধান্তটি ব্যাপক কারণ এটি বুল-> ইন রূপান্তরকে সস্তা করে তোলে (কেবল শূন্য-এক্সটেনশন) । আমি এমন কোনও এবিআই সম্পর্কে অবগত নই যা সংকলকটি কোনও আর্কিটেকচারের জন্য (কেবল x86 নয়) বুলের জন্য 0 বা 1 ধরে নিতে দেয় না। এটি xor eax,1 !mybool সহ !mybool xor eax,1 কে কম বিটটি ফ্লিপ করতে পছন্দ করে: যে কোনও সম্ভাব্য কোড যা একক সিপিইউ নির্দেশনায় বিট / ইন্টিজার / বুলকে 0 এবং 1 এর মধ্যে ফ্লিপ করতে পারে । বা a&&b প্রকারের জন্য এবং bool ধরণের জন্য a&&b সংকলন করে। কিছু সংকলক আসলে 8 বিট হিসাবে সংকলক হিসাবে বুলিয়ান মান গ্রহণ করে তাদের উপর অপারেশন কি অকার্যকর?

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

সংকলকটি অবশ্যই অবশ্যই তার কোড- strlen(whichString) কোনও এবিআই গ্যারান্টির পুরো সুবিধা গ্রহণ করার অনুমতি পেয়েছে এবং আপনার মতো কোড তৈরি করতে strlen(whichString) যা strlen(whichString) যা strlen(whichString) অনুকূল করে
5U - boolValue (বিটিডাব্লু, এই অপটিমাইজেশনটি ধূর্ত চালাক, তবে সম্ভবত সংক্ষিপ্ততর বনাম ব্রাঞ্চিং এবং তাত্ক্ষণিক ডেটা 2 হিসাবে স্টোর হিসাবে ম্যান্সপি memcpy করা উচিত))

অথবা সংকলকটি পয়েন্টারগুলির একটি সারণী তৈরি করতে পারে এবং আবার এটি একটি 0 বা 1 বলে ধরে ধরে বুলের পূর্ণসংখ্যার মান সহ সূচী তৈরি করতে পারে ( বারমারের উত্তরের পরামর্শেই এই সম্ভাবনাটি রয়েছে ))

আপনার __attribute((noinline)) কন্সট্রাক্টরটি অপ্টিমাইজেশান সক্ষম করার সাথে কেবল __attribute((noinline)) সৃষ্টি করেছিল কেবল __attribute((noinline)) লোড করে __attribute((noinline)) হিসাবে ব্যবহার করতে। এটি push rax (যা ছোট এবং বিভিন্ন হিসাবে sub rsp, 8 হিসাবে দক্ষ সম্পর্কে বিভিন্ন কারণে) sub rsp, 8 অবজেক্টের জন্য জায়গা তৈরি করেছিল, তাই main প্রবেশের সময় যে কোনও আবর্জনা আ’লীগের মধ্যে ছিল তা sub rsp, 8 মূল্যের জন্য ব্যবহৃত মান। এই কারণেই আপনি আসলে এমন মান পেয়েছেন যা কেবল 0 ছিল না।

5U - random garbage সহজেই একটি বৃহত স্বাক্ষরিত মানকে মোড়তে পারে, মেমপিকে আনম্যাপড মেমরিতে যেতে পারে। গন্তব্যটি স্ট্যাটিক স্টোরেজটিতে রয়েছে, স্ট্যাক নয়, সুতরাং আপনি কোনও ফেরতের ঠিকানা বা কোনও কিছু ওভাররাইট করছেন না।

অন্যান্য বাস্তবায়নগুলি বিভিন্ন পছন্দ করতে পারে, যেমন: false=0 এবং true=any non-zero value তারপরে ঝাঁকুনি সম্ভবত ইউবি-র এই নির্দিষ্ট উদাহরণের জন্য ক্র্যাশ করা কোড তৈরি করবে না। (তবে এটি চাইলে এটি অনুমতি দেওয়া হবে)) x86-64 বুলের জন্য অন্য কিছু করে এমন কোনও প্রয়োগ বাছাই করে এমন কোনও বাস্তবায়ন আমার জানা নেই, তবে সি ++ স্ট্যান্ডার্ড এমন অনেক কিছুই মঞ্জুরি দেয় যা কেউ না করে বা এমনকি করতে চায় না হার্ডওয়ারে এটি বর্তমান সিপিইউগুলির মতো কিছু।

আইএসও সি ++ আপনাকে bool অবজেক্টের উপস্থাপনা পরীক্ষা বা সংশোধন করার সময় আপনি কী পাবেন তা অনির্দিষ্ট করে ছেড়ে দেয় । (উদাহরণস্বরূপ, মাইনকেপিকে বুলকে স্বাক্ষরবিহীন চরে অন্তর্ভুক্ত করে, যা আপনাকে করার অনুমতি দেয় কারণ char* কোনও কিছুর নাম করতে পারে And এবং unsigned char চরটিতে কোনও প্যাডিং বিট নেই তার গ্যারান্টি রয়েছে, সুতরাং সি ++ স্ট্যান্ডার্ড আপনাকে কোনও ইউবি ছাড়াই আনুষ্ঠানিকভাবে হেক্সডাম্প অবজেক্টের অনুমতি দেয় । অবজেক্টের প্রতিনিধিত্বের অনুলিপি করার জন্য পয়েন্টার-কাস্টিং char foo = my_bool নির্ধারণের চেয়ে আলাদা, অবশ্যই, তাই 0 বা 1 তে বুলিওনাইজেশন ঘটবে না এবং আপনি কাঁচা বস্তুর প্রতিনিধিত্ব পাবেন))

আপনি noinline সহ সংকলক থেকে এই সম্পাদনের পথে noinline আংশিকভাবে "লুকিয়ে" noinline । এমনকি যদি এটি ইনলাইন না করে তবে ইন্টারপ্রেসিডুরাল অপটিমাইজেশনগুলি এখনও ফাংশনের একটি সংস্করণ তৈরি করতে পারে যা অন্য ফাংশনের সংজ্ঞা উপর নির্ভর করে। (প্রথমত, ঝাঁকুনি একটি এক্সিকিউটেবল তৈরি করছে, ইউনিক্স শেয়ার্ড লাইব্রেরি নয়, যেখানে প্রতীক-ইন্টারপজিশন ঘটতে পারে। দ্বিতীয়ত, class{} ভিতরে সংজ্ঞা class{} সংজ্ঞা তাই সমস্ত অনুবাদ ইউনিটগুলির অবশ্যই একই সংজ্ঞা থাকতে হবে। inline কীওয়ার্ডের মতো।)

সুতরাং একটি সংকলক ud2 সংজ্ঞা হিসাবে কেবলমাত্র একটি ret বা ud2 (অবৈধ নির্দেশ) নির্গত করতে পারে কারণ main অনিবার্যভাবে উপরের দিকে শুরু হওয়া মৃত্যুদন্ডের পথটি অনির্ধারিত আচরণের মুখোমুখি হয়। (কোন অন-ইনলাইন কনস্ট্রাক্টরের মাধ্যমে পথ অনুসরণ করার সিদ্ধান্ত নিলে সংকলকটি সংকলনের সময় দেখতে পাবে))

ইউবির মুখোমুখি হওয়া যে কোনও প্রোগ্রাম তার সম্পূর্ণ অস্তিত্বের জন্য সম্পূর্ণ অপরিজ্ঞাত। কিন্তু কোনও ফাংশনের ভিতরে ইউবি বা if() শাখাগুলি যা আসলে কখনও চালিত হয় তা বাকি প্রোগ্রামটিকে দূষিত করে না। অনুশীলনে এর অর্থ হ'ল সংকলকরা অবৈধ নির্দেশনা নির্ধারণের সিদ্ধান্ত নিতে পারে, বা কোনও ret , বা কোনও কিছু নির্গত না করে এবং পরবর্তী ব্লক / ফাংশনে পড়তে পারে, সম্পূর্ণ বেসিক ব্লকের জন্য যা সংশ্লেষ সময় প্রমাণিত হতে পারে যা ইউবিকে ধারণ করে বা নেতৃত্ব দিতে পারে।

অনুশীলনে জিসিসি এবং ud2 প্রকৃতপক্ষে কখনও কখনও ud2 নির্গত করে, এমনকি মৃত্যুদন্ড কার্যকর করার পথে এমন কোড তৈরি করার চেষ্টা করেও যা কোনও অর্থহীন নয়। বা কোনও void ফাংশন শেষ হয়ে যাওয়ার মতো ক্ষেত্রে, কখনও কখনও জিসিসি একটি ret নির্দেশনা বাদ দেয়। আপনি যদি ভাবছিলেন যে "আমার ফাংশনটি RAX এ যা কিছু আবর্জনা রয়েছে তা দিয়েই ফিরে আসবে", আপনি খুব খারাপভাবে ভুল করছেন। আধুনিক সি ++ সংকলকরা আর কোনও পোর্টেবল সমাবেশের ভাষার মতো ভাষা ব্যবহার করে না। আপনার কার্যকারিতাটির অবিচ্ছিন্ন অ-ইনলাইনড সংস্করণটি কীভাবে asm এ দেখা যাবে সে সম্পর্কে অনুমান করা ছাড়াই আপনার প্রোগ্রামটি সত্যই বৈধ সি ++ হতে হবে।

আর একটি মজাদার উদাহরণ হ'ল এমএমপিড মেমরিটিতে স্বাক্ষরবিহীন অ্যাক্সেসটি কেন কখনও কখনও এএমডি 64 এ সেগফল্ট করে? । x86 আন-স্বাক্ষরিত পূর্ণসংখ্যার উপর দোষ দেয় না, তাই না? তাহলে কেন একটি বিভ্রান্তিকর uint16_t* সমস্যা হবে? কারণ alignof(uint16_t) == 2 , এবং এই অনুমানের লঙ্ঘন করলে alignof(uint16_t) == 2 সাথে অটো-ভেক্টরাইজিংয়ের সময় alignof(uint16_t) == 2 দিকে পরিচালিত হয়েছিল।

প্রতিটি সি প্রোগ্রামারকে অনির্ধারিত আচরণ # 1/3 সম্পর্কে কী জেনে রাখা উচিত তা দেখুন , একটি ঝনঝন বিকাশকারীর একটি নিবন্ধ।

মূল বক্তব্য: সংকলনের সময় সংকলকটি যদি ইউবিটিকে লক্ষ্য করে, এটি আপনার কোডের মাধ্যমে "ব্রেক" (বিস্ময়কর এএসএম নিঃসরণ) করতে পারে যা কোনও এবিআইকে লক্ষ্য করে এমনকি কোনও বিট-প্যাটার্ন বৈধ অবজেক্টের উপস্থাপনের জন্য bool

প্রোগ্রামার দ্বারা অনেক ভুলের প্রতি সম্পূর্ণ শত্রুতা প্রত্যাশা করুন, বিশেষত আধুনিক সংকলকরা যে বিষয়গুলি সম্পর্কে সতর্ক করে দিয়েছে। এজন্য আপনার -Wall ব্যবহার করা উচিত এবং সতর্কতাগুলি ঠিক করা উচিত। সি ++ একটি ব্যবহারকারী-বান্ধব ভাষা নয় এবং সি ++ এর কিছু এমন কিছু অনিরাপদ হতে পারে যদিও আপনি যে লক্ষ্যটির জন্য এটি সংকলন করছেন তার ছাঁটাই সুরক্ষিত থাকবে। (যেমন স্বাক্ষরিত ওভারফ্লো সি ++ এ ইউবি এবং সংকলকরা ধরে নেবে যে 2 এর পরিপূরক x86 এর জন্য সংকলন করার পরেও আপনি clang/gcc -fwrapv ব্যবহার না করে)

সংকলন-সময়-দৃশ্যমান ইউবি সর্বদা বিপজ্জনক, এবং এটি নিশ্চিত হওয়া সত্যিই কঠিন (লিঙ্ক-টাইম অপ্টিমাইজেশান সহ) যে আপনি সত্যিই সংকলকটি থেকে ইউবি লুকিয়ে রেখেছেন এবং এটি কী ধরণের এ্যাসেম তৈরি করবে সে সম্পর্কে যুক্তিযুক্ত হতে পারে।

অতিরিক্ত নাটকীয় হতে হবে না; প্রায়শই সংকলকগুলি আপনাকে কিছু জিনিস দিয়ে দূরে সরে যেতে দেয় এবং কোডটি নির্গত করে যেমন কোনও কিছু ইউবি হওয়ার পরেও আপনি প্রত্যাশা করেন। তবে ভবিষ্যতে সমস্যা হতে পারে যদি সংকলক কিছু অপ্টিমাইজেশন প্রয়োগ করে যা মান-সীমা সম্পর্কে আরও তথ্য অর্জন করে (উদাহরণস্বরূপ যে একটি পরিবর্তনশীল অ-নেতিবাচক, সম্ভবত এটি x86- এ শূন্য-এক্সটেনশন মুক্ত সাইন-এক্সটেনশনটিকে অনুকূলিতকরণের অনুমতি দেয়) 64)। উদাহরণস্বরূপ, বর্তমান tmp = a+INT_MIN , tmp = a+INT_MIN a<0 সর্বদা মিথ্যা হিসাবে অনুকূল করে না, কেবল সেই tmp সর্বদা নেতিবাচক থাকে। (কারণ INT_MIN + a=INT_MAX এই 2 এর পরিপূরক লক্ষ্যমাত্রায় নেতিবাচক এবং INT_MIN হতে পারে না))

সুতরাং জিসিসি / ক্ল্যাং বর্তমানে কোনও গণনার ইনপুটগুলির জন্য পরিসীমা তথ্য পেতে ব্যাকট্র্যাক করবে না, কেবলমাত্র স্বাক্ষরিত কোনও ওভারফ্লো অনুমানের ভিত্তিতে ফলাফলের ভিত্তিতে: গডবোল্টের উপর উদাহরণ । আমি জানি না এটি অপ্টিমাইজেশানটি ব্যবহারকারী-বন্ধুত্বের নামে বা কী কারণে ইচ্ছাকৃতভাবে "মিস" হয়েছে কিনা।

এছাড়াও নোট করুন যে বাস্তবায়নগুলি (ওরফে সংকলক) এমন আচরণের সংজ্ঞা দিতে মঞ্জুরি দেয় যা আইএসও সি ++ অপরিবর্তিত থাকে । উদাহরণস্বরূপ, সমস্ত সংকলক যারা ম্যানুয়াল _mm_add_ps(__m128, __m128) ভেক্টরাইজেশনের জন্য ইন্টেলের অন্তর্নিহিত (যেমন _mm_add_ps(__m128, __m128) ) সমর্থন করে তাদের অবশ্যই ভুল-প্রান্তিক পয়েন্টার গঠনের অনুমতি দিতে হবে, যা আপনি সিটি ++ এ _mm_add_ps(__m128, __m128) ইউবি। __m128i _mm_loadu_si128(const __m128i *) গ্রহণ করে __m128i _mm_loadu_si128(const __m128i *) লোডগুলি করে, কোনও __m128i* void* বা char* হার্ডওয়্যার ভেক্টর পয়েন্টার এবং সংশ্লিষ্ট ধরণের মধ্যে `পুনরায় ব্যাখ্যা_কাস্টিং কি একটি অপরিজ্ঞাত আচরণ?

জিএনইউ সি / সি ++ সাধারণ স্বাক্ষরিত-ওভারফ্লো ইউবি বিধিগুলি থেকে পৃথক করে বাম-স্থানান্তরিত নেতিবাচক স্বাক্ষরিত সংখ্যার (এমনকি -fwrapv ছাড়াই) পরিবর্তনেরও সংজ্ঞা দেয়। ( এটি আইএসও সি ++ এ ইউবি , যখন স্বাক্ষরিত সংখ্যার ডান শিফটগুলি বাস্তবায়ন-সংজ্ঞায়িত (যৌক্তিক বনাম গাণিতিক); ভাল মানের বাস্তবায়নগুলি এইচডাব্লুটিতে পাটিগণিত চয়ন করে যা পাটিগণিতের ডান শিফটগুলিতে থাকে তবে আইএসও সি ++ নির্দিষ্ট করে না)। এটি জিসিসি ম্যানুয়ালটির পূর্ণসংখ্যা বিভাগে বাস্তবায়ন-সংজ্ঞায়িত আচরণ সংজ্ঞায়িত করার সাথে সাথে সি স্ট্যান্ডার্ডগুলির জন্য এক উপায় বা অন্য কোনও সংজ্ঞা দেওয়ার জন্য বাস্তবায়ন প্রয়োজন বলে প্রমাণিত হয় is

বাস্তবায়িত মানের-বাস্তবায়ন সংক্রান্ত সমস্যাগুলি রয়েছে যা সংকলক বিকাশকারীরা যত্নশীল হন; তারা সাধারণত সংকলকগুলি ইচ্ছাকৃতভাবে বৈরী করার চেষ্টা করে না, তবে সি ++ এর সমস্ত ইউবি পাথরের (যেগুলি তারা সংজ্ঞায়িত করতে পছন্দ করে তা বাদ দিয়ে) আরও ভাল করার জন্য প্রায়শই বিভেদযোগ্য হতে পারে advantage

পাদটীকা 1 : উপরের 56 টি বিট আবর্জনা হতে পারে যা কলিকে অবশ্যই এড়ানো উচিত, যেমন রেজিস্টারের চেয়ে সংকীর্ণ প্রকারের জন্য usual

( অন্যান্য এবিআইগুলি এখানে আলাদা আলাদা পছন্দ করে । কারও কারও কাছে এমআইপিএস and৪ এবং পাওয়ারপিসি like৪ এর মতো ফাংশনগুলিতে পাস বা ফিরে আসার সময় একটি রেজিস্টার পূরণের জন্য সংক্ষিপ্ত পূর্ণসংখ্যার প্রকারের শূন্য হতে হবে বা সাইন-প্রসারিত হতে হবে x এই x86-64 উত্তরের শেষ বিভাগটি দেখুন যা পূর্ববর্তী আইএসএগুলির তুলনায় বনামের তুলনা করে ))

উদাহরণস্বরূপ, একজন কলার সম্ভবত a & 0x01010101 গণনা করেছেন এবং এটি bool_func(a&1) কল করার আগে এটি অন্য কোনও কিছুর জন্য ব্যবহার করেছেন। কলার &1 অপটিমাইজ করতে পারে কারণ এটি ইতিমধ্যে লো বাইটে and edi, 0x01010101 , and edi, 0x01010101 অংশ হিসাবে এটি করেছে এবং এটি জানে যে উচ্চ বাইটগুলি উপেক্ষা করার জন্য and edi, 0x01010101 প্রয়োজন।

বা যদি কোনও তৃতীয় তর্ক হিসাবে পাস করা হয়, তবে কোনও কলার কোড-সাইজের জন্য অপ্টিমাইজ করতে পারে এটি movzx edx, [mem] mov dl, [mem] লোড করে mov dl, [mem] movzx edx, [mem] পরিবর্তে movzx edx, [mem] , পুরানো উপর মিথ্যা নির্ভরতার দামে 1 বাইট সঞ্চয় করে আরডিএক্সের মান (বা সিপিইউ মডেলের উপর নির্ভর করে অন্যান্য আংশিক-নিবন্ধের প্রভাব) অথবা প্রথম mov dil, byte [r10] জন্য, movzx edi, byte [r10] mov dil, byte [r10] পরিবর্তে movzx edi, byte [r10] , কারণ উভয়ের জন্যই যাইহোক movzx edi, byte [r10] উপসর্গের প্রয়োজন।

এ কারণেই ঝনঝন sub eax, edi movzx eax, dil পরিবর্তে movzx eax, dil পরিবর্তে movzx eax, dil (পূর্ণসংখ্যার উপাখ্যানগুলির জন্য, ক্ল্যাং জিসি-র শিরোনামহীন আচরণের উপর নির্ভর করে শূন্য- অথবা সাইন-প্রসারিত সরু পূর্ণসংখ্যার 32 বিটের উপর নির্ভর করে a২ বিট অফসেটের জন্য পয়েন্টারে যোগ করার সময় একটি চিহ্ন বা শূন্য এক্সটেনশন প্রয়োজন কি? x86-64 এবিআই? সুতরাং আমি আগ্রহী ছিলাম যে এটি bool জন্য একই কাজ করে না))

পাদটীকা 2: শাখার পরে, আপনি কেবল একটি 4 বাইট মুভ-মিমিটেড, বা একটি 4 বাইট + 1-বাইট স্টোর পাবেন। দৈর্ঘ্য স্টোর প্রস্থ + অফসেটগুলিতে অন্তর্ভুক্ত।

ওটিওএইচ, গ্লিবিসি মেম্পপি দৈর্ঘ্যের উপর নির্ভর করে একটি ওভারল্যাপ সহ দুটি 4-বাইট লোড / স্টোর করবে so সুতরাং এটি সত্যিই বুলিয়ানটিতে শর্তযুক্ত শাখাগুলি থেকে সম্পূর্ণ জিনিস তৈরি করে। L(between_4_7): / L(between_4_7): ব্লক করুন । বা কমপক্ষে, মেমকপির ব্রাঞ্চিংয়ে বুলিয়ান উভয়ের জন্য একই আকারে একটি খণ্ড আকার নির্বাচন করুন।

যদি ইনলাইন করা থাকে তবে আপনি 2x মুভি-মিমিটেড + cmov এবং শর্তসাপেক্ষ অফসেট ব্যবহার করতে পারেন বা স্ট্রিং ডেটা মেমরিতে রেখে দিতে পারেন।

অথবা যদি ইন্টেল আইস লেকের জন্য টিউন করা হয় ( ফাস্ট শর্ট rep movsb এমওভি বৈশিষ্ট্য সহ ), একটি প্রকৃত rep movsb অনুকূল হতে পারে। rep movsb সেই বৈশিষ্ট্যটির সাহায্যে সিপিইউতে ছোট আকারের rep movsb ব্যবহার শুরু করতে পারে এবং প্রচুর শাখাগুলি সঞ্চয় করে।

ইউবি সনাক্তকরণের সরঞ্জাম এবং অবিচ্ছিন্ন মানগুলির ব্যবহার

-fsanitize=undefined এবং ক্ল্যাং-এ, আপনি রান-টাইম উপকরণ যুক্ত করতে -fsanitize=undefined দিয়ে সংকলন করতে পারেন যা রানটাইম সময়ে ঘটে যাওয়া -fsanitize=undefined সতর্কতা বা ত্রুটি দেখাবে। যদিও এটি এককীকরণযুক্ত ভেরিয়েবলগুলি ধরবে না। (কারণ এটি একটি "অবিচ্ছিন্ন" বিটের জন্য জায়গা তৈরি করতে ধরণের আকারকে বাড়ায় না)।

https://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan/ দেখুন

অবিচ্ছিন্ন তথ্য ব্যবহারের জন্য, ঝড় / এলএলভিএম-এ অ্যাড্রেস স্যানিটাইজার এবং মেমরি স্যানিটাইজার রয়েছে। https://github.com/google/sanitizers/wiki/MemorySanitizer clang -fsanitize=memory -fPIE -pie উদাহরণ দেখায় clang -fsanitize=memory -fPIE -pie সনাক্তকরণ clang -fsanitize=memory -fPIE -pie মেমরি পড়ছে। আপনি অপ্টিমাইজেশন ছাড়াই সংকলন করতে পারলে এটি সর্বোত্তমভাবে কাজ করতে পারে, সুতরাং ভেরিয়েবলগুলির সমস্ত পাঠগুলি আসলে asm এ মেমরি থেকে লোড হয়। তারা দেখায় যে এটি এমন -O2 ক্ষেত্রে ব্যবহৃত হচ্ছে যেখানে লোডটি অপ্টিমাইজ হবে না। আমি নিজে চেষ্টা করে দেখিনি। (কিছু ক্ষেত্রে, উদাহরণস্বরূপ কোনও অ্যারে যোগ করার আগে কোনও সংযোজককে সূচনা না করা, ঝনঝন -O3 এমন কোনও ভেক্টর রেজিস্টারে যে কোডটি আরম্ভ হয় না তার প্রেরণ করবে So সুতরাং অপ্টিমাইজেশনের সাথে, আপনার এমন কোনও ক্ষেত্রে থাকতে পারে যেখানে ইউবির সাথে কোনও স্মৃতি পড়েনি where -fsanitize=memory উত্পন্ন asm পরিবর্তন করে এবং এর জন্য একটি পরীক্ষার ফলাফল হতে পারে might)

এটি অস্বীকারহীন মেমরির অনুলিপি সহ, এবং এটির সাথে সাধারণ যুক্তি এবং গাণিতিক ক্রিয়াকলাপ সহ্য করবে। সাধারণভাবে, মেমরিস্যানিটাইজার নিঃশব্দে স্মৃতিতে অবিচ্ছিন্ন তথ্য প্রসারণ ট্র্যাক করে এবং যখন একটি কোড শাখা নেওয়া হয় (বা নেওয়া হয় না) একটি অবিশ্বাস্য মানের উপর নির্ভর করে একটি সতর্কতা প্রতিবেদন করে।

মেমরিস্যানিটাইজার ভালগ্রাইন্ডে (মেমেক চেক সরঞ্জাম) পাওয়া ফাংশনটির একটি উপসেট প্রয়োগ করে।

এটি এই ক্ষেত্রে কাজ করা উচিত কারণ অস্বীকারহীন মেমরির (গ্রন্থাগারের অভ্যন্তরে) গণনা করা length সাথে memcpy কল করার ফলে length উপর ভিত্তি করে একটি শাখার ফলস্বরূপ। এটি যদি পুরোপুরি cmov সংস্করণটিকে cmov যা কেবলমাত্র cmov , ইনডেক্সিং এবং দুটি স্টোর ব্যবহার করে, এটি কাজ নাও করতে পারে।

memcheck এই ধরণের সমস্যাটির সন্ধান করবে, আবার প্রোগ্রামটি memcheck অনুলিপি করে যদি অভিযোগ করে না। তবে এটি বলে যে এটি শনাক্তকরণের উপাত্তের উপর নির্ভর করে যে কোনও বাহ্যিক-দৃশ্যমান আচরণকে ধরার চেষ্টা করার জন্য "শর্তাধীন জাম্প বা চলাচল অবিঘ্নিত মান (গুলি) এর উপর নির্ভর করে" তা সনাক্ত করবে।

সম্ভবত কেবল একটি লোডকে পতাকাঙ্কিত না করার পিছনে ধারণাটি হ'ল স্ট্রাক্টগুলিতে প্যাডিং থাকতে পারে এবং পৃথক সদস্যরা কেবল একবারে কেবল একটি লেখা থাকলেও পুরো স্ট্রাক্টকে (প্যাডিং সহ) প্রশস্ত ভেক্টর লোড / স্টোর দিয়ে অনুলিপি করা কোনও ত্রুটি নয়। এএসএম স্তরে, কী প্যাডিং ছিল এবং মূলত কোনটির মূল অংশটি ছিল সে সম্পর্কে তথ্য হারিয়ে গেছে।


আপনার প্রশ্নের প্রচুর সংক্ষিপ্তসার করে, আপনি জিজ্ঞাসা করছেন যে সি ++ স্ট্যান্ডার্ড কোনও bool একটি bool অনুমান করার অনুমতি দেয় কি কেবল '0' বা '1' এর অভ্যন্তরীণ সংখ্যাগত উপস্থাপনা থাকতে পারে এবং bool এভাবে ব্যবহার করতে পারে?

মানটি একটি bool অভ্যন্তরীণ উপস্থাপনা সম্পর্কে কিছুই বলে না। এটি কেবল কোনও bool (বা বিপরীতে) একটি bool দেওয়ার সময় কী ঘটে তা সংজ্ঞায়িত করে। বেশিরভাগ ক্ষেত্রে, এই অবিচ্ছেদ্য রূপান্তরগুলির কারণে (এবং লোকেদের পরিবর্তে তাদের উপর নির্ভর করে যে সত্য), সংকলকটি 0 এবং 1 ব্যবহার করবে তবে এটি করতে হবে না (যদিও এটি এটি যে কোনও নিম্ন স্তরের এবিআই ব্যবহার করে তা সীমাবদ্ধতার সম্মান করতে হবে) )।

সুতরাং, সংকলক, যখন এটি একটি bool দেখবে বিবেচনা করার অধিকারী যে বলেছিল যে bool মধ্যে ' true ' বা ' false ' বিট নিদর্শন রয়েছে এবং যা কিছু মনে হয় তা করে। সুতরাং যদি truefalse মানগুলি যথাক্রমে 1 এবং 0 হয় তবে strlen সত্যই strlen 5 - <boolean value> অনুকূলিতকরণের জন্য অনুমোদিত। অন্যান্য মজাদার আচরণ সম্ভব!

এখানে যেমন বারবার বলা হয়েছে, অপরিজ্ঞাত আচরণের ফলাফল রয়েছে অনির্ধারিত। সহ তবে সীমাবদ্ধ নয় not

  • আপনার কোডটি যেমনটি আপনি প্রত্যাশা করেছিলেন তেমন কাজ করছে
  • আপনার কোড এলোমেলো সময়ে ব্যর্থ
  • আপনার কোডটি মোটেও চালিত হচ্ছে না।

অপরিশোধিত আচরণ সম্পর্কে প্রতিটি প্রোগ্রামারকে কী জানতে হবে দেখুন দেখুন


ফাংশনটি নিজেই সঠিক, তবে আপনার পরীক্ষামূলক প্রোগ্রামে, বিবৃতিটি যে ফাংশনটি বলে তা অনির্ধারিত ভেরিয়েবলের মান ব্যবহার করে অপরিজ্ঞাত আচরণের কারণ হয়।

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

অপরিজ্ঞাত আচরণের অর্থ হ'ল যে কোনও কিছু ঘটতে পারে, যার মধ্যে প্রোগ্রামটি অনির্ধারিত আচরণের সূত্রপাতকারী ইভেন্টের পরে কয়েকটি লাইন ক্র্যাশ করে।

বিশেষ দ্রষ্টব্য। "অপরিবর্তিত আচরণের কারণ _____ হতে পারে?" সর্বদা "হ্যাঁ" হয়। এটি আক্ষরিক অর্থেই সংজ্ঞায়িত আচরণের সংজ্ঞা।


সংকলকটিকে ধরে নিতে অনুমতি দেওয়া হয় যে আর্গুমেন্ট হিসাবে পাস করা একটি বুলিয়ান মান একটি বৈধ বুলিয়ান মান (অর্থাত্ একটি যা প্রাথমিক বা true বা false রূপান্তরিত হয়েছে)। true মানটি পূর্ণসংখ্যা 1 এর মতো হতে হবে না - সত্যই, true এবং false বিভিন্ন উপস্থাপনা হতে পারে - তবে পরামিতিটি অবশ্যই সেই দুটি মানের একটির কিছু বৈধ উপস্থাপনা হতে হবে, যেখানে "বৈধ উপস্থাপনা" বাস্তবায়ন সংজ্ঞায়িত হয়।

সুতরাং আপনি যদি কোনও bool সূচনা করতে ব্যর্থ হন বা আপনি যদি এটি কোনও ভিন্ন ধরণের কোনও পয়েন্টারের মাধ্যমে ওভাররাইটিংয়ে সফল হন তবে সংকলকটির অনুমানগুলি ভুল হবে এবং অপরিবর্তিত আচরণটি ঘটবে। আপনাকে সতর্ক করা হয়েছিল:

৫০) এই আন্তর্জাতিক স্ট্যান্ডার্ড দ্বারা "অপরিজ্ঞাত" হিসাবে বর্ণিত উপায়ে একটি বুলি মান ব্যবহার করা, যেমন একটি অনিবার্যতম স্বয়ংক্রিয় বস্তুর মান পরীক্ষা করে, এটি এমন আচরণ করতে পারে যে এটি সত্য বা মিথ্যা নয়। (§6.9.1 এর মৌলিক প্রকারের প্যারা 6-তে পাদটীকা)






abi