সিতে সাধারণ অপরিবর্তিত আচরণ সম্পর্কে জিজ্ঞাসা করার সময়, লোকেরা মাঝে মধ্যে কঠোর আলিয়াজিং বিধি উল্লেখ করে।
তারা কী সম্পর্কে কথা বলছে?
সিতে সাধারণ অপরিবর্তিত আচরণ সম্পর্কে জিজ্ঞাসা করার সময়, লোকেরা মাঝে মধ্যে কঠোর আলিয়াজিং বিধি উল্লেখ করে।
তারা কী সম্পর্কে কথা বলছে?
উত্তর:
একটা প্রচলিত অবস্থা যেখানে আপনি কঠোর এলিয়াসিং সমস্যার সম্মুখীন হন যখন শব্দ (একটি পয়েন্টার মত আপনার সিস্টেম আকার একটি বাফার সম্মুখের একটি struct (ক ডিভাইস / নেটওয়ার্ক বার্তা মত) ওভারলেয়িং হয় uint32_t
s অথবা uint16_t
গুলি)। আপনি যখন এই জাতীয় বাফারের উপর কোনও কাঠামোকে আবৃত করেন বা পয়েন্টার castালাইয়ের মাধ্যমে কোনও স্টাফের উপর কোনও বাফার আপনি সহজেই কঠোর আলিয়াজিং বিধি লঙ্ঘন করতে পারেন।
সুতরাং এই ধরণের সেটআপে, আমি যদি কোনও কিছুর কাছে একটি বার্তা পাঠাতে চাই তবে আমার দুটি বেমানান পয়েন্টার একই মেমরির অংশের দিকে ইঙ্গিত করতে হবে। আমি তখন নির্লজ্জভাবে এই জাতীয় কিছু (কোড সহ sizeof(int) == 2
) কোড করতে পারি :
typedef struct Msg
{
unsigned int a;
unsigned int b;
} Msg;
void SendWord(uint32_t);
int main(void)
{
// Get a 32-bit buffer from the system
uint32_t* buff = malloc(sizeof(Msg));
// Alias that buffer through message
Msg* msg = (Msg*)(buff);
// Send a bunch of messages
for (int i =0; i < 10; ++i)
{
msg->a = i;
msg->b = i+1;
SendWord(buff[0]);
SendWord(buff[1]);
}
}
কঠোর আলিয়াজিং বিধিটি এই সেটআপটিকে অবৈধ করে তোলে: সিআই 2011 6.5 অনুচ্ছেদ 7 1 এর দ্বারা অনুমোদিত এমন কোনও বস্তু যে সামঞ্জস্যপূর্ণ ধরণের নয় বা অন্য কোনও ধরণের নয় এমন একটি পয়েন্টারকে ডিফারেন্স করা অনির্ধারিত আচরণ। দুর্ভাগ্যবশত, আপনি করতে পারেন এখনও কোডটির এই পথ, হয়তো কিছু সতর্কবার্তা পেতে, এটা কম্পাইল জরিমানা, শুধুমাত্র অদ্ভুত অপ্রত্যাশিত আচরণের যখন আপনি কোড রান আছে।
(জিসিসি এলিয়াসিং সতর্কতা দেওয়ার ক্ষমতায় কিছুটা অসঙ্গত দেখা যায়, কখনও কখনও আমাদের বন্ধুত্বপূর্ণ সতর্কতা দেয় এবং কখনও কখনও তা দেয় না))
এই আচরণটি কেন অপরিবর্তিত তা দেখতে, আমাদের কঠোর আলিয়াজিং নিয়ম সংকলকটি কী কিনে তা নিয়ে চিন্তা করতে হবে। মূলত, এই নিয়মটি সহ, লুপটির buff
প্রতিটি রানের বিষয়বস্তু রিফ্রেশ করার জন্য নির্দেশাবলী সন্নিবেশ করার বিষয়ে ভাবার দরকার নেই । পরিবর্তে, এলিয়াসিং সম্পর্কে কিছু বিরক্তিকরভাবে অবিচ্ছিন্ন অনুমানের সাথে অনুকূলকরণ করার সময়, লুপটি চালানোর আগে একবারে সেগুলি নির্দেশাবলী, লোড buff[0]
এবং buff[1
] সিপিইউ রেজিস্টারে বাদ দিতে পারে এবং লুপটির শরীরে গতি বাড়ায়। কঠোর আলিয়াজিং প্রবর্তনের আগে, সংকলকটি প্যারানোইয়ায় buff
থাকতে হয়েছিল যে যে কোনও সময় যে কোনও জায়গায় যে কোনও সময় যে কোনও বিষয়বস্তু পরিবর্তিত হতে পারে। সুতরাং একটি অতিরিক্ত পারফরম্যান্স প্রান্ত পেতে এবং বেশিরভাগ লোকেরা টাইপ-পয়েন্ট পয়েন্টার না ধরে ধরে কঠোর আলিয়াজিং বিধি চালু করা হয়েছিল।
মনে রাখবেন, যদি আপনি মনে করেন যে উদাহরণটি সংবিধানযুক্ত, আপনি যদি অন্য কোনও ফাংশনটিতে বাফারটি পাঠাচ্ছেন তবে আপনার জন্য প্রেরণ করছেন, যদি আপনার পরিবর্তে থাকে।
void SendMessage(uint32_t* buff, size_t size32)
{
for (int i = 0; i < size32; ++i)
{
SendWord(buff[i]);
}
}
এবং এই সুবিধাজনক ফাংশনটির সুবিধা নিতে আমাদের পূর্বের লুপটি আবারও লিখুন
for (int i = 0; i < 10; ++i)
{
msg->a = i;
msg->b = i+1;
SendMessage(buff, 2);
}
সংকলকটি সেন্ডমেসেজ ইনলাইন করার চেষ্টা করতে সক্ষম হতে পারে বা যথেষ্ট স্মার্ট হতে পারে এবং এটি আবার বাফ লোড বা না লোড করার সিদ্ধান্ত নিতে পারে বা নাও পারে। যদি SendMessage
আলাদাভাবে সংকলিত অন্য কোনও এপিআইয়ের অংশ হয়, তবে সম্ভবত এটি বাফের সামগ্রী লোড করার জন্য নির্দেশনা রয়েছে। তারপরে আবার, সম্ভবত আপনি সি ++ এ রয়েছেন এবং এটি এমন কিছু টেম্পলেটেড শিরোলেখ কেবলমাত্র বাস্তবায়ন যা সংকলক মনে করে এটি ইনলাইন করতে পারে। অথবা এটি আপনার নিজের সুবিধার জন্য আপনার .c ফাইলে লিখেছেন এমন কিছু। যাইহোক, অপরিজ্ঞাত আচরণ এখনও হতে পারে। এমনকি যখন আমরা হুডের নীচে কী ঘটছে সে সম্পর্কে কিছুটা জানার পরেও এটি বিধি লঙ্ঘন তাই কোনও সঠিক সংজ্ঞায়িত আচরণের গ্যারান্টি নেই। সুতরাং কেবল এমন কোনও ফাংশন মোড়ানোর মাধ্যমে যা আমাদের শব্দটি সীমিত বাফারে নেয় তা প্রয়োজনীয়ভাবে সহায়তা করে না।
সুতরাং আমি কিভাবে এই কাছাকাছি পেতে পারি?
একটি ইউনিয়ন ব্যবহার করুন। বেশিরভাগ সংকলক কঠোর আলিয়াজিংয়ের অভিযোগ না করেই এটি সমর্থন করে। এটি C99 এ অনুমোদিত এবং স্পষ্টভাবে C11 এ অনুমোদিত।
union {
Msg msg;
unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)];
};
আপনি আপনার সংকলকটিতে কঠোর আলিয়াজিং অক্ষম করতে পারেন ( চ [no-] কঠোর-এলিয়াসিং জিসিসি))
আপনি char*
আপনার সিস্টেমের শব্দের পরিবর্তে এলিয়াসিংয়ের জন্য ব্যবহার করতে পারেন । নিয়মগুলি char*
(সহ signed char
এবং unsigned char
) এর জন্য ব্যতিক্রমের অনুমতি দেয় । এটি সর্বদা ধরে নেওয়া হয় যে char*
অন্য ধরণের নাম রাখে। তবে এটি অন্যভাবে কাজ করবে না: আপনার কাঠামোটি অক্ষরের একটি বাফারটিকে অন্যরকমভাবে রাখবে এমন কোনও ধারণা নেই।
সূচনা সাবধান
একে অপরের উপর দুই প্রকারের ওভারলেলে এটি কেবলমাত্র একটি সম্ভাব্য মাইনফিল্ড। আপনার আধ্যাত্মিকতা , শব্দের প্রান্তিককরণ এবং সঠিকভাবে প্যাকিং স্ট্রাক্টগুলির মাধ্যমে প্রান্তিককরণের সমস্যাগুলি কীভাবে মোকাবেলা করা উচিত সে সম্পর্কেও শিখতে হবে।
1 সি 2011 6.5 7 প্রকারের মধ্যে একটি লভ্যালুকে অ্যাক্সেসের অনুমতি দেয় সেগুলি হ'ল:
unsigned char*
ব্যবহৃত হতে পারে char*
? আমি অন্তর্নিহিত ধরণের unsigned char
পরিবর্তে এর চেয়ে বেশি ব্যবহার করার প্রবণতা রাখি কারণ আমার বাইটগুলি স্বাক্ষরিত নয় এবং আমি স্বাক্ষরিত আচরণের অদ্ভুততা চাই না (উল্লেখযোগ্যভাবে উপচে পড়া char
byte
unsigned char *
করা ঠিক আছে।
uint32_t* buff = malloc(sizeof(Msg));
এবং পরবর্তী ইউনিয়ন unsigned int asBuffer[sizeof(Msg)];
বাফার ঘোষণার বিভিন্ন মাপ থাকবে এবং দুটিও সঠিক নয়। malloc
কল ফণা অধীন 4 বাইট প্রান্তিককরণ উপর নির্ভর করা হয় (এটা করতে না) এবং ইউনিয়ন 4 বার বড় তুলনায় এটি করা প্রয়োজন হতে হবে ... আমি বুঝতে পারি যে এটা স্বচ্ছতার জন্য কিন্তু এটা বাগ আমাকে কেউ-the- কম ...
আমি যে সর্বোত্তম ব্যাখ্যাটি পেয়েছি তা হ'ল মাইক অ্যাক্টন, স্ট্যান্ডার্ড এলিয়াসিং বোঝা । এটি পিএস 3 বিকাশের দিকে কিছুটা ফোকাস করেছে, তবে এটি মূলত কেবল জিসিসি।
নিবন্ধ থেকে:
"স্ট্রাইক অ্যালাইজিং হ'ল একটি অনুমান যা সি (বা সি ++) সংকলক দ্বারা তৈরি করা হয় যে বিভিন্ন ধরণের বস্তুর প্রতি পয়েন্টারগুলি ডিপ্রিফারেন্সিং কখনও একই মেমরি অবস্থানের (যেমন একে অপরকে ওরফে।) উল্লেখ করে না" "
সুতরাং মূলত যদি আপনার কোনও int*
স্মৃতিযুক্ত একটি স্মৃতিযুক্ত থাকে int
এবং তারপরে আপনি float*
সেই স্মৃতিতে একটি নির্দেশ করেন এবং এটি float
নিয়ম ভঙ্গ করে হিসাবে ব্যবহার করেন। যদি আপনার কোড এটি সম্মান না করে তবে সংকলকের অপ্টিমাইজারটি সম্ভবত আপনার কোডটি ভেঙে দেবে।
নিয়মের ব্যতিক্রম ক char*
, যা কোনও প্রকারের দিকে নির্দেশ করার অনুমতিপ্রাপ্ত is
এটি হ'ল কঠোর আলিয়াজিং নিয়ম, সি ++ ০৩ স্ট্যান্ডার্ডের ৩.১০ বিভাগে পাওয়া গেছে (অন্যান্য উত্তরগুলি ভাল ব্যাখ্যা সরবরাহ করে তবে কোনওটিই বিধিটি সরবরাহ করে না):
যদি কোনও প্রোগ্রাম নিম্নলিখিত ধরণের একটি ব্যতীত অন্য কোনও লভ্যালুয়ের মাধ্যমে কোনও অবজেক্টের সঞ্চিত মান অ্যাক্সেস করার চেষ্টা করে তবে আচরণটি সংজ্ঞায়িত হয়:
- বস্তুর গতিশীল প্রকার,
- বস্তুর গতিশীল ধরণের একটি সিভি-যোগ্য সংস্করণ,
- এমন এক ধরণের যা স্বাক্ষরিত বা স্বাক্ষরযুক্ত প্রকার যা অবজেক্টটির গতিশীল প্রকারের সাথে সম্পর্কিত,
- এমন এক ধরণের যা স্বাক্ষরিত বা স্বাক্ষরযুক্ত প্রকার যা বস্তুর গতিশীল প্রকারের সিভি-যোগ্য সংস্করণের সাথে সম্পর্কিত,
- একটি সামগ্রিক বা ইউনিয়ন প্রকার যা এর সদস্যদের মধ্যে পূর্বোক্ত ধরণের একটি অন্তর্ভুক্ত করে (অন্তর্ভুক্ত, পুনরাবৃত্তভাবে, একটি সাবগ্রেগেটের সদস্য বা সমন্বিত ইউনিয়নের সদস্য সহ),
- একটি প্রকার যা (সম্ভবত সিভি-কোয়ালিটিভড) অবজেক্টের গতিশীল প্রকারের বেস শ্রেণীর ধরণ,
- একটি
char
বাunsigned char
টাইপ।
সি ++ 11 এবং সি ++ 14 শব্দগুচ্ছ (পরিবর্তনগুলি জোর দিয়েছিল):
যদি কোনও প্রোগ্রাম নিম্নলিখিত ধরণের একটি ব্যতীত অন্য কোনও গ্লুভের মাধ্যমে কোনও অবজেক্টের সঞ্চিত মান অ্যাক্সেস করার চেষ্টা করে তবে আচরণটি সংজ্ঞায়িত:
- বস্তুর গতিশীল প্রকার,
- বস্তুর গতিশীল ধরণের একটি সিভি-যোগ্য সংস্করণ,
- বস্তুর গতিশীল ধরণের সাথে একই ধরণের (4.4-তে সংজ্ঞায়িত),
- এমন এক ধরণের যা স্বাক্ষরিত বা স্বাক্ষরযুক্ত প্রকার যা অবজেক্টটির গতিশীল প্রকারের সাথে সম্পর্কিত,
- এমন এক ধরণের যা স্বাক্ষরিত বা স্বাক্ষরযুক্ত প্রকার যা বস্তুর গতিশীল প্রকারের সিভি-যোগ্য সংস্করণের সাথে সম্পর্কিত,
- একটি সামগ্রিক বা ইউনিয়ন প্রকার যা এর উপাদান বা অ স্থিতিশীল ডেটা সদস্যদের মধ্যে পূর্বোক্ত ধরণের একটি অন্তর্ভুক্ত করে (অন্তর্ভুক্ত, পুনরাবৃত্তভাবে, একটি উপজাতি বা অন্তর্ভুক্ত ইউনিয়নের একটি উপাদান বা অ স্থির ডেটা সদস্য সহ),
- একটি প্রকার যা (সম্ভবত সিভি-কোয়ালিটিভড) অবজেক্টের গতিশীল প্রকারের বেস শ্রেণীর ধরণ,
- একটি
char
বাunsigned char
টাইপ।
দুটি পরিবর্তন ছোট ছিল: লভালুর পরিবর্তে গ্লাভ্যু এবং সমষ্টি / ইউনিয়ন মামলার ব্যাখ্যা।
তৃতীয় পরিবর্তনটি আরও শক্তিশালী গ্যারান্টি দেয় (শক্তিশালী আলিয়াজিং নিয়ম শিথিল করে): অনুরূপ ধরণের নতুন ধারণা যা এখন ওরফে নিরাপদ।
এছাড়াও সি শব্দবন্ধ ( সি 99; আইএসও / আইসিসি 9899: 1999 6.5 / 7; ঠিক একই শব্দটি আইএসও / আইইসি 9899: 2011 §6.5 ¶7 এ ব্যবহৃত হয়):
একটি অবজেক্টের তার সঞ্চিত মানটি কেবলমাত্র একটি মূল্যবান এক্সপ্রেশন দ্বারা অ্যাক্সেস করতে হবে যা নিম্নলিখিত ধরণের 73৩ ) বা 88) :
- বস্তুর কার্যকর ধরণের সাথে সামঞ্জস্যপূর্ণ এক প্রকার,
- বস্তুর কার্যকর ধরণের সাথে সামঞ্জস্যপূর্ণ প্রকারের একটি কোয়ালিড সংস্করণ,
- এমন এক ধরণের যা বস্তুর কার্যকর ধরণের সাথে সম্পর্কিত স্বাক্ষরযুক্ত বা স্বাক্ষরযুক্ত প্রকার,
- এমন এক ধরণের যা বস্তুর কার্যকর ধরণের কোনও গুণমান সংস্করণের সাথে স্বাক্ষরযুক্ত বা স্বাক্ষরযুক্ত প্রকার,
- একটি সামগ্রিক বা ইউনিয়ন প্রকার যা এর সদস্যদের মধ্যে পূর্বোক্ত ধরণের একটি অন্তর্ভুক্ত করে (অন্তর্ভুক্ত, পুনরাবৃত্তভাবে, একটি সাবগ্রেগেটের সদস্য বা সমন্বিত ইউনিয়নের সদস্য সহ), বা
- একটি অক্ষর টাইপ।
)৩) বা ৮৮) এই তালিকার উদ্দেশ্যটি সেই পরিস্থিতিতে নির্দিষ্ট করা যা কোনও বস্তু অ্যালাইজড হতে পারে বা নাও করতে পারে।
wow(&u->s1,&u->s2)
পয়েন্টারটি সংশোধন করার জন্য ব্যবহার করা হলেও আইনি হতে হবে u
, এবং এটি বেশিরভাগ অপ্টিমাইজেশানকে অস্বীকার করবে এলিয়াসিং বিধিটি সুবিধার্থে ডিজাইন করা হয়েছিল।
এটি আমার "স্ট্রাক্ট আলিয়াজিং বিধি কী এবং আমরা কেন যত্ন নিই?" থেকে উদ্ধৃত লেখা.
সি এবং সি ++ এ্যালিয়াসিংয়ের সাথে আমাদের কী স্ট্রাকচারের ধরণের মাধ্যমে সঞ্চিত মানগুলি অ্যাক্সেসের অনুমতি দেওয়া হচ্ছে তা করতে হবে। সি এবং সি ++ উভয় ক্ষেত্রে স্ট্যান্ডার্ড নির্দিষ্ট করে যে কোন প্রকারের এক্সট্রাক্টের জন্য উপন্যাসের জন্য কোন ধরনের প্রকারের মঞ্জুরি রয়েছে। সংকলক এবং অপ্টিমাইজারকে ধরে নেওয়া যায় যে আমরা আলিয়াসিং বিধিগুলি কঠোরভাবে অনুসরণ করি, সুতরাং এই শব্দটি কঠোর আলিয়াজিং নিয়ম । যদি আমরা কোনও প্রকারের অনুমোদিত না হয়ে এমন কোনও ব্যবহার করে কোনও মান অ্যাক্সেস করার চেষ্টা করি তবে এটি এটিকে সংজ্ঞায়িত আচরণ ( ইউবি ) হিসাবে শ্রেণীবদ্ধ করা হয় । একবার আমরা অনির্ধারিত আচরণ করলে সমস্ত বেট বন্ধ হয়ে যায়, আমাদের প্রোগ্রামের ফলাফল আর নির্ভরযোগ্য হয় না।
দুর্ভাগ্যক্রমে কড়া আলিয়াস লঙ্ঘনের কারণে আমরা প্রায়শই আমাদের প্রত্যাশার ফলাফলগুলি পেয়ে যাব, সম্ভাবনাটি নতুন অপ্টিমাইজেশান সহ সংকলকটির ভবিষ্যতের সংস্করণটি কোডটিকে ভঙ্গ করবে যা আমরা ভেবেছিলাম। এটি অনাকাঙ্ক্ষিত এবং কঠোর এলিয়াসিং বিধি এবং সেগুলি কীভাবে লঙ্ঘন করা এড়ানো যায় তা বোঝা একটি উপযুক্ত লক্ষ্য।
আমরা কেন যত্ন নিই সে সম্পর্কে আরও জানার জন্য, আমরা কঠোর আলিয়াজিং বিধি লঙ্ঘন করার সময় যে বিষয়গুলি সামনে আসে সেগুলি নিয়ে আলোচনা করব, টাইপ পেনিংয়ে ব্যবহৃত সাধারণ কৌশলগুলি প্রায়শই কঠোর আলিয়াজিং বিধি লঙ্ঘন করে এবং কীভাবে সঠিকভাবে শাস্তি টাইপ করতে হয় সেগুলি নিয়ে আলোচনা করব।
আসুন কয়েকটি উদাহরণগুলি দেখুন, তারপরে আমরা মানক (গুলি) ঠিক কী বলেছে সে সম্পর্কে কথা বলতে পারি, আরও কয়েকটি উদাহরণ পরীক্ষা করে দেখুন এবং তারপরে কীভাবে কঠোরভাবে এলিয়াসিং এড়ানো যায় এবং কীভাবে আমরা মিস করেছি তা লঙ্ঘন করতে পারি। এখানে এমন একটি উদাহরণ রয়েছে যা অবাক হওয়ার মতো নয় ( সরাসরি উদাহরণ ):
int x = 10;
int *ip = &x;
std::cout << *ip << "\n";
*ip = 12;
std::cout << x << "\n";
আমাদের কাছে একটি অন্তর্নিহিত * কোনও ইন্টি দ্বারা দখলকৃত মেমরিটির দিকে ইঙ্গিত করছে এবং এটি একটি বৈধ আলিয়াসিং। অপ্টিমাইজারটি ধরে নিতে হবে যে আইপি এর মাধ্যমে অ্যাসাইনমেন্টগুলি এক্স দ্বারা দখল করা মান আপডেট করতে পারে ।
পরবর্তী উদাহরণটি অ্যালিজিং দেখায় যা অপরিজ্ঞাত আচরণের দিকে পরিচালিত করে ( সরাসরি উদাহরণ ):
int foo( float *f, int *i ) {
*i = 1;
*f = 0.f;
return *i;
}
int main() {
int x = 0;
std::cout << x << "\n"; // Expect 0
x = foo(reinterpret_cast<float*>(&x), &x);
std::cout << x << "\n"; // Expect 0?
}
ফাংশন ফুতে আমরা একটি ইনট * এবং একটি ফ্লোট * নিই, এই উদাহরণে আমরা foo কল করি এবং উভয় পরামিতি একই মেমরি অবস্থানের দিকে নির্দেশ করি যা এই উদাহরণে কোনও int থাকে । মনে রাখবেন, reinterpret_cast অভিব্যক্তি চিকিত্সা হিসাবে যদি এটি টাইপ তার টেমপ্লেট প্যারামিটার দ্বারা specificed ছিল কম্পাইলার কহন হয়। এক্ষেত্রে আমরা এটিকে এক্সপ্রেশন এবং এক্স এর সাথে আচরণ করতে বলছি যেন এটিতে টাইপ * রয়েছে । আমরা naively দ্বিতীয় ফল আশা করতে পারে cout হতে 0 কিন্তু অপ্টিমাইজেশান এর মাধ্যমে ব্যবহার সক্রিয় -O2 উভয় জিসিসি এবং ঝনঝন নিম্নলিখিত ফল:
0
1
যা প্রত্যাশিত নাও হতে পারে তবে পুরোপুরি বৈধ কারণ আমরা অনির্ধারিত আচরণটি শুরু করেছি inv একটি ভাসাটি বৈধভাবে কোনও ইনট অবজেক্ট ওরফে করতে পারে না । অতএব অপ্টিমাইজারটি ধ্রুব 1 টি সঞ্চিত করতে পারে যখন নির্ধারণের সময় আমি ফিরতি মান হব যেহেতু চ এর মাধ্যমে কোনও স্টোর বৈধভাবে কোনও ইনট অবজেক্টকে প্রভাবিত করতে পারে না । কম্পাইলার এক্সপ্লোরার কোডটি প্লাগ করে দেখায় এটি ঠিক ঘটছে ( সরাসরি উদাহরণ ):
foo(float*, int*): # @foo(float*, int*)
mov dword ptr [rsi], 1
mov dword ptr [rdi], 0
mov eax, 1
ret
ব্যবহার অপটিমাইজার প্রকার ভিত্তিক ওরফে বিশ্লেষণ (TBAA) অনুমান 1 ফেরত পাঠানো হবে এবং সরাসরি রেজিস্টার মধ্যে ধ্রুবক রয়েছে যার মান চলে আসে EAX ফেরত মান বহন করে যা। টিবিএএ লোড এবং স্টোরগুলি অনুকূলকরণের জন্য কী ধরণের উপন্যাসের অনুমতি দেয় সে সম্পর্কে ভাষা সংক্রান্ত নিয়ম ব্যবহার করে। এই ক্ষেত্রে TBAA জানে যে একটি ভাসা করতে পারেন ওরফে এবং int- এ এবং লোড দূরে সেরা অনুকূল রূপ আমি ।
মানকটি ঠিক কী বলে যে আমাদের অনুমতি দেওয়া হয় এবং তা করার অনুমতি দেওয়া হয় না? স্ট্যান্ডার্ড ভাষা সোজা নয়, সুতরাং প্রতিটি আইটেমের জন্য আমি কোডের উদাহরণ দেওয়ার চেষ্টা করব যা অর্থটি প্রকাশ করে।
C11 মান বিভাগে নিম্নলিখিত বলছেন 6.5 প্রকাশ 7 অনুচ্ছেদ :
একটি অবজেক্টের তার সঞ্চিত মানটি কেবলমাত্র একটি মূল্যবান এক্সপ্রেশন দ্বারা অ্যাক্সেস করতে হবে যার নিম্নলিখিত ধরণের একটি রয়েছে: ৮৮) - কোনও প্রকারের কার্যকর ধরণের সাথে সামঞ্জস্যপূর্ণ,
int x = 1;
int *p = &x;
printf("%d\n", *p); // *p gives us an lvalue expression of type int which is compatible with int
- বস্তুর কার্যকর ধরণের সাথে সামঞ্জস্যপূর্ণ কোনও ধরণের একটি যোগ্য সংস্করণ,
int x = 1;
const int *p = &x;
printf("%d\n", *p); // *p gives us an lvalue expression of type const int which is compatible with int
- এমন এক ধরণের যা বস্তুর কার্যকর ধরণের সাথে সম্পর্কিত স্বাক্ষরযুক্ত বা স্বাক্ষরযুক্ত প্রকার,
int x = 1;
unsigned int *p = (unsigned int*)&x;
printf("%u\n", *p ); // *p gives us an lvalue expression of type unsigned int which corresponds to
// the effective type of the object
জিসিসি / ঝনঝন একটি এক্সটেনশন রয়েছে এবং এছাড়াও যে বরাদ্দ দেয় স্বাক্ষরবিহীন int- * থেকে int- * যদিও তারা সামঞ্জস্যপূর্ণ ধরনের নয়।
- এমন এক ধরণের যা বস্তুর কার্যকর ধরণের কার্যকর সংস্করণের সাথে সম্পর্কিত স্বাক্ষরযুক্ত বা স্বাক্ষরযুক্ত প্রকার,
int x = 1;
const unsigned int *p = (const unsigned int*)&x;
printf("%u\n", *p ); // *p gives us an lvalue expression of type const unsigned int which is a unsigned type
// that corresponds with to a qualified verison of the effective type of the object
- একটি সামগ্রিক বা ইউনিয়ন প্রকার যা এর সদস্যদের মধ্যে পূর্বোক্ত ধরণের একটিকে অন্তর্ভুক্ত করে (অন্তর্ভুক্ত, পুনরাবৃত্তভাবে, একটি উপশ্রেণী বা অন্তর্ভুক্ত ইউনিয়নের সদস্য সহ), বা
struct foo {
int x;
};
void foobar( struct foo *fp, int *ip ); // struct foo is an aggregate that includes int among its members so it can
// can alias with *ip
foo f;
foobar( &f, &f.x );
- একটি চরিত্রের ধরণ।
int x = 65;
char *p = (char *)&x;
printf("%c\n", *p ); // *p gives us an lvalue expression of type char which is a character type.
// The results are not portable due to endianness issues.
[বেসিক.লভাল] অনুচ্ছেদে 11 অনুচ্ছেদে সি ++ 17 খসড়া মান বলেছেন:
একটি প্রোগ্রাম প্রচেষ্টা নিম্নলিখিত প্রকারের আচরণ অনির্দিষ্ট হয় এক ছাড়া অন্য একটি glvalue মাধ্যমে একটি বস্তুর সংরক্ষিত মান অ্যাক্সেস করতে পারেন: 63 (11.1) - বস্তুর গতিশীল ধরন,
void *p = malloc( sizeof(int) ); // We have allocated storage but not started the lifetime of an object
int *ip = new (p) int{0}; // Placement new changes the dynamic type of the object to int
std::cout << *ip << "\n"; // *ip gives us a glvalue expression of type int which matches the dynamic type
// of the allocated object
(১১.২) - বস্তুর গতিশীল ধরণের একটি সিভি-যোগ্য সংস্করণ,
int x = 1;
const int *cip = &x;
std::cout << *cip << "\n"; // *cip gives us a glvalue expression of type const int which is a cv-qualified
// version of the dynamic type of x
(১১.৩) - বস্তুর গতিশীল ধরণের সাথে একই ধরণের (.5.৫ সংজ্ঞায়িত),
(১১.৪) - এমন এক ধরণের যা বস্তুর গতিশীল ধরণের সাথে সম্পর্কিত স্বাক্ষরযুক্ত বা স্বাক্ষরযুক্ত প্রকার,
// Both si and ui are signed or unsigned types corresponding to each others dynamic types
// We can see from this godbolt(https://godbolt.org/g/KowGXB) the optimizer assumes aliasing.
signed int foo( signed int &si, unsigned int &ui ) {
si = 1;
ui = 2;
return si;
}
(১১.৫) - এমন এক ধরণের যা বস্তুর গতিশীল প্রকারের সিভি-যোগ্য সংস্করণের সাথে সম্পর্কিত স্বাক্ষরিত বা স্বাক্ষরযুক্ত প্রকার,
signed int foo( const signed int &si1, int &si2); // Hard to show this one assumes aliasing
(১১..6) - একটি সামগ্রিক বা ইউনিয়ন প্রকার যা এর উপাদান বা ননস্ট্যাটিক ডেটা সদস্যদের মধ্যে পূর্ব বর্ণিত ধরণের একটি অন্তর্ভুক্ত করে (অন্তর্ভুক্ত বা সংযুক্ত ইউনিয়নের উপাদান বা অ-স্থিতিশীল ডেটা সদস্য সহ, পুনরাবৃত্তভাবে),
struct foo {
int x;
};
// Compiler Explorer example(https://godbolt.org/g/z2wJTC) shows aliasing assumption
int foobar( foo &fp, int &ip ) {
fp.x = 1;
ip = 2;
return fp.x;
}
foo f;
foobar( f, f.x );
(১১.7) - এমন এক ধরণের যা (সম্ভবত সিভি-কোয়ালিটিভড) অবজেক্টের ডায়নামিক টাইপের বেস ক্লাস টাইপ,
struct foo { int x ; };
struct bar : public foo {};
int foobar( foo &f, bar &b ) {
f.x = 1;
b.x = 2;
return f.x;
}
(১১.৮) - একটি চর, স্বাক্ষরবিহীন চর, বা স্টাড :: বাইট প্রকার।
int foo( std::byte &b, uint32_t &ui ) {
b = static_cast<std::byte>('a');
ui = 0xFFFFFFFF;
return std::to_integer<int>( b ); // b gives us a glvalue expression of type std::byte which can alias
// an object of type uint32_t
}
লক্ষণীয় স্বাক্ষরিত চরটি উপরের তালিকায় অন্তর্ভুক্ত নয়, এটি সি থেকে একটি উল্লেখযোগ্য পার্থক্য যা একটি চরিত্রের ধরণ বলে ।
আমরা এই মুহুর্তে পৌঁছেছি এবং আমরা ভাবতে পারি, আমরা কেন তার উপাধি চাইব? উত্তরটি সাধারণত পাং টাইপ করা হয় , প্রায়শই ব্যবহৃত পদ্ধতিগুলি কঠোরভাবে আলিয়াজিং বিধি লঙ্ঘন করে।
কখনও কখনও আমরা টাইপ সিস্টেমটি অবরুদ্ধ করতে চাই এবং একটি অবজেক্টকে আলাদা ধরণের হিসাবে ব্যাখ্যা করতে চাই। এটিকে মেমরির একটি বিভাগকে অন্য ধরণের হিসাবে পুনরায় ব্যাখ্যা করতে টাইপ পেনিং বলে । টাইপ পেনিং সেই কাজগুলির জন্য দরকারী যা কোনও অবজেক্টের অন্তর্নিহিত উপস্থাপনাটি দেখতে, পরিবহন বা পরিচালনা করতে অ্যাক্সেস চায়। যে ধরণের অঞ্চলগুলিতে আমরা টাইপ পেনিং ব্যবহার করা হচ্ছে তা হ'ল সংকলক, সিরিয়ালাইজেশন, নেটওয়ার্কিং কোড ইত্যাদি…
Ditionতিহ্যগতভাবে এটি সামগ্রীর ঠিকানা গ্রহণ করে, আমরা যে ধরণের পুনরায় ব্যাখ্যা করতে চাই এবং তারপরে মানটি অ্যাক্সেস করতে চাই বা অন্য শব্দে আলিয়াজ করে এটি ব্যবহার করি তার বিন্দুতে কাস্টিংয়ের মাধ্যমে এটি সম্পন্ন হয়েছে। উদাহরণ স্বরূপ:
int x = 1 ;
// In C
float *fp = (float*)&x ; // Not a valid aliasing
// In C++
float *fp = reinterpret_cast<float*>(&x) ; // Not a valid aliasing
printf( "%f\n", *fp ) ;
যেমনটি আমরা আগে দেখেছি এটি কোনও বৈধ আলিয়াজিং নয়, তাই আমরা অনির্ধারিত আচরণের জন্য প্রার্থনা করছি। তবে traditionতিহ্যগতভাবে সংকলকগণ কঠোর আলিয়াজিং বিধিগুলির সুবিধা নেন নি এবং সাধারণত এই ধরণের কোডটি কেবলমাত্র কাজ করেছিল, দুর্ভাগ্যক্রমে বিকাশকারীরা জিনিসগুলি এভাবে করায় অভ্যস্ত হয়ে পড়েছে। টাইপ পেনিংয়ের জন্য একটি সাধারণ বিকল্প পদ্ধতিটি ইউনিয়নগুলির মাধ্যমে হয়, যা সিতে বৈধ তবে সি ++ এ অপরিবর্তিত আচরণ ( সরাসরি উদাহরণ দেখুন ):
union u1
{
int n;
float f;
} ;
union u1 u;
u.f = 1.0f;
printf( "%d\n”, u.n ); // UB in C++ n is not the active member
এটি সি ++ তে বৈধ নয় এবং কেউ কেউ ইউনিয়নগুলির উদ্দেশ্যটি কেবলমাত্র বৈকল্পিক ধরণের প্রয়োগের জন্য বিবেচনা করে এবং ইউনিয়নগুলি টাইপ পেনিংয়ের জন্য ব্যবহার করা একটি অপব্যবহার বলে মনে করে।
সি এবং সি ++ উভয় প্রকারের পাণিংয়ের স্ট্যান্ডার্ড পদ্ধতি হ'ল ম্যাকপি । এটি সামান্য ভারী হাতের মতো মনে হতে পারে তবে অপ্টিমাইজারের উচিত টাইপ পেনিংয়ের জন্য মেমকি ব্যবহার করা উচিত এবং এটি অপ্টিমাইজ করা উচিত এবং সরানো রেজিস্ট্রেশন করার জন্য একটি রেজিস্টার তৈরি করা উচিত। উদাহরণস্বরূপ যদি আমরা জানতে পারি যে int64_t ডাবল হিসাবে একই আকারের হয় :
static_assert( sizeof( double ) == sizeof( int64_t ) ); // C++17 does not require a message
আমরা মেমকি ব্যবহার করতে পারি :
void func1( double d ) {
std::int64_t n;
std::memcpy(&n, &d, sizeof d);
//...
অবশ্যই যথেষ্ট অপ্টিমাইজেশান স্তরে কোন ভদ্র আধুনিক কম্পাইলার পূর্বে উল্লিখিত অভিন্ন কোড জেনারেট করে reinterpret_cast পদ্ধতি বা ইউনিয়ন জন্য পদ্ধতি টাইপ punning । উত্পন্ন কোড পরীক্ষা করে আমরা দেখতে পাই এটি কেবল রেজিস্টার রেজিস্ট্রেশন ব্যবহার করে ( লাইভ সংকলক এক্সপ্লোরার উদাহরণ )।
সি ++ ২০-এ আমরা বিট_কাস্ট ( প্রস্তাবের লিঙ্কে উপলব্ধ বাস্তবায়ন ) পেতে পারি যা টাইপ-পুণের একটি সহজ এবং নিরাপদ উপায় দেয় এবং পাশাপাশি কনস্টেক্সপ্রপেক্টে ব্যবহারের যোগ্য হয়।
নিম্নলিখিত কিভাবে ব্যবহার করতে একটি উদাহরণ bit_cast একটি শ্লেষ টাইপ করতে স্বাক্ষরবিহীন int- এ থেকে ভাসা , ( এটা লাইভ দেখতে পাবেন ):
std::cout << bit_cast<float>(0x447a0000) << "\n" ; //assuming sizeof(float) == sizeof(unsigned int)
কেস যেখানে ইন করার এবং থেকে ধরনের একই আকার থাকে না, এটা কোন মধ্যবর্তী struct15 ব্যবহার করতে আমাদের প্রয়োজন। আমরা একটি struct একটি ধারণকারী ব্যবহার করবে যাও sizeof (স্বাক্ষরবিহীন int- এ) চরিত্র অ্যারে ( অনুমান 4 বাইট স্বাক্ষরবিহীন int- এ ) হতে থেকে ধরন ও স্বাক্ষরবিহীন int- এ যেমন করতে টাইপ .:
struct uint_chars {
unsigned char arr[sizeof( unsigned int )] = {} ; // Assume sizeof( unsigned int ) == 4
};
// Assume len is a multiple of 4
int bar( unsigned char *p, size_t len ) {
int result = 0;
for( size_t index = 0; index < len; index += sizeof(unsigned int) ) {
uint_chars f;
std::memcpy( f.arr, &p[index], sizeof(unsigned int));
unsigned int result = bit_cast<unsigned int>(f);
result += foo( result );
}
return result ;
}
দুর্ভাগ্যজনক যে আমাদের এই মাঝারি ধরনের প্রয়োজন তবে এটি বিটকাস্টের বর্তমান সীমাবদ্ধতা ।
সি ++ তে কড়া আলিয়াজিং ধরার জন্য আমাদের কাছে প্রচুর ভাল সরঞ্জাম নেই, আমাদের কাছে যে সরঞ্জামগুলি রয়েছে তা কঠোরভাবে আলিয়াসিং লঙ্ঘনের কয়েকটি মামলা এবং মিসিলাইনযুক্ত লোড এবং স্টোরগুলির কিছু কেস ধরবে।
জি.সি.সি. ফ্ল্যাগ -স্ট্রিট-এলিয়াসিং এবং -স্ট্রিট- এলিয়জিং ব্যবহার করে কিছু মামলা ধরা যেতে পারে যদিও মিথ্যা পজিটিভ / নেগেটিভ ছাড়াই নয়। উদাহরণস্বরূপ, নিম্নলিখিত কেসগুলি জিসিসিতে একটি সতর্কতা উত্পন্ন করবে ( এটি সরাসরি দেখুন ):
int a = 1;
short j;
float f = 1.f; // Originally not initialized but tis-kernel caught
// it was being accessed w/ an indeterminate value below
printf("%i\n", j = *(reinterpret_cast<short*>(&a)));
printf("%i\n", j = *(reinterpret_cast<int*>(&f)));
যদিও এটি এই অতিরিক্ত কেসটি ধরবে না ( এটি সরাসরি দেখুন ):
int *p;
p=&a;
printf("%i\n", j = *(reinterpret_cast<short*>(p)));
যদিও ঝাঁকুনি এই পতাকাগুলির অনুমতি দেয় এটি স্পষ্টতই সতর্কতাগুলি প্রয়োগ করে না।
আমাদের কাছে আর একটি সরঞ্জাম উপলব্ধ রয়েছে যেটি হ'ল আসান যা ভুল পথে চালিত লোড এবং স্টোরগুলি ধরতে পারে। যদিও এগুলি সরাসরি কড়া আলিয়াসিং লঙ্ঘন নয় তবে এগুলি কঠোরভাবে এলিয়াসিং লঙ্ঘনের একটি সাধারণ ফলাফল। উদাহরণস্বরূপ যখন -fsanitize = ঠিকানা ব্যবহার করে ঝাঁকুনির সাথে নির্মিত তখন নিম্নলিখিত কেসগুলি রানটাইম ত্রুটি তৈরি করে
int *x = new int[2]; // 8 bytes: [0,7].
int *u = (int*)((char*)x + 6); // regardless of alignment of x this will not be an aligned address
*u = 1; // Access to range [6-9]
printf( "%d\n", *u ); // Access to range [6-9]
আমি সর্বশেষ সরঞ্জামটি সুপারিশ করব সি ++ নির্দিষ্ট এবং কঠোরভাবে কোনও সরঞ্জাম নয় বরং কোডিং অনুশীলন, সি-স্টাইলের কাস্টের অনুমতি দেবেন না। জিসিসি এবং ক্লাং উভয়ই সি-স্টাইলের কাস্টগুলির জন্য -ভোল্ড-স্টাইল- কাস্ট ব্যবহার করে ডায়াগনস্টিক তৈরি করবে । এটি পুনরায় সংজ্ঞা_কাস্ট ব্যবহার করতে কোনও অপরিজ্ঞাত ধরণের পাগুলিকে বাধ্য করবে, সাধারণভাবে পুনরায় সংজ্ঞা_কাস্ট কাছাকাছি কোড পর্যালোচনার জন্য একটি পতাকা হওয়া উচিত। নিরীক্ষা সম্পাদনের জন্য পুনরায় ব্যাখ্যা_কাস্টের জন্য আপনার কোড বেসটি অনুসন্ধান করা আরও সহজ।
সি এর জন্য আমাদের কাছে সমস্ত সরঞ্জাম ইতিমধ্যে আচ্ছাদিত রয়েছে এবং আমাদের টিস-ইন্টারপ্রেটারও রয়েছে, একটি স্ট্যাটিক বিশ্লেষক যা সি ভাষার বৃহত উপসেটের জন্য একটি প্রোগ্রামকে নিখরচায় বিশ্লেষণ করে। পূর্ববর্তী উদাহরণের সি ভার্শন দেওয়া যেখানে -স্ট্রিক্ট-এলিয়াসিং ব্যবহার করে একটি কেস মিস হয় ( এটি সরাসরি দেখুন )
int a = 1;
short j;
float f = 1.0 ;
printf("%i\n", j = *((short*)&a));
printf("%i\n", j = *((int*)&f));
int *p;
p=&a;
printf("%i\n", j = *((short*)p));
টিস-ইন্টারপেটর তিনটিই ধরতে সক্ষম, নিম্নলিখিত উদাহরণটি টিস-কার্নালকে টিআইস-ইন্টারপ্রেটার হিসাবে আহ্বান করে (আউটপুটটি ব্রিভিটির জন্য সম্পাদিত হয়):
./bin/tis-kernel -sa example1.c
...
example1.c:9:[sa] warning: The pointer (short *)(& a) has type short *. It violates strict aliasing
rules by accessing a cell with effective type int.
...
example1.c:10:[sa] warning: The pointer (int *)(& f) has type int *. It violates strict aliasing rules by
accessing a cell with effective type float.
Callstack: main
...
example1.c:15:[sa] warning: The pointer (short *)p has type short *. It violates strict aliasing rules by
accessing a cell with effective type int.
অবশেষে টাইসান রয়েছে যা বর্তমানে বিকাশে রয়েছে। এই স্যানিটাইজার একটি ছায়া মেমরি বিভাগে টাইপ চেকিংয়ের তথ্য যুক্ত করে এবং অ্যালাইজিং বিধি লঙ্ঘন করে কিনা তা দেখার জন্য অ্যাক্সেসগুলি পরীক্ষা করে। সরঞ্জামটি সম্ভাব্যভাবে সমস্ত এলিয়াসিং লঙ্ঘন ধরতে সক্ষম হওয়া উচিত তবে এতে একটি বড় রান-টাইম ওভারহেড থাকতে পারে।
reinterpret_cast
করতে পারেন বা এর cout
অর্থ কী হতে পারে তা জানেন না। (সি ++ উল্লেখ করা ঠিক আছে তবে মূল প্রশ্নটি সি এবং আইআইইউসি সম্পর্কে ছিল এই উদাহরণগুলি যথাযথভাবে সিটিতে লেখা যেতে পারে)
কঠোর আলিয়াজিং কেবল পয়েন্টারগুলিকেই উল্লেখ করে না, এটি রেফারেন্সগুলিকেও প্রভাবিত করে, আমি এটি সম্পর্কে একটি বিকাশকারী বিকাশকারী উইকিটির জন্য একটি কাগজ লিখেছিলাম এবং এটি এত ভালভাবে গৃহীত হয়েছিল যে আমি এটিকে আমার পরামর্শ ওয়েবসাইটের একটি পৃষ্ঠায় পরিণত করেছি। এটি সম্পূর্ণরূপে ব্যাখ্যা করে যে এটি কী, এটি কেন মানুষকে এত বিভ্রান্ত করে এবং এটি সম্পর্কে কী করা উচিত। স্ট্রাইক আলিয়াসিং হোয়াইট পেপার । বিশেষত এটি ব্যাখ্যা করে যে ইউনিয়নগুলি কেন সি ++ এর জন্য ঝুঁকিপূর্ণ আচরণ, এবং মেমকি ব্যবহার কেন সি এবং সি ++ উভয়ই একমাত্র স্থিরযোগ্য পোর্টেবল। আশা করি এটি সহায়ক।
ডগ টি ইতিমধ্যে যা লিখেছিল তার সংযোজন হিসাবে, এখানে একটি সাধারণ পরীক্ষার কেস যা সম্ভবত এটি জিসিসি দিয়ে ট্রিগার করে:
check.c
#include <stdio.h>
void check(short *h,long *k)
{
*h=5;
*k=6;
if (*h == 5)
printf("strict aliasing problem\n");
}
int main(void)
{
long k[1];
check((short *)k,k);
return 0;
}
সংকলন gcc -O2 -o check check.c
। সাধারণত (বেশিরভাগ জিসিসি সংস্করণ সহ আমি চেষ্টা করেছি) এই ফলাফলটি "কঠোর আলিয়াজিং সমস্যা" হয়, কারণ সংকলক ধরে নিয়েছে যে "এইচ" "চেক" ফাংশনে "কে" এর মতো ঠিকানা হতে পারে না। যে কারণে সংকলক if (*h == 5)
দূরে অপ্টিমাইজ করে এবং সর্বদা প্রিন্টফকে কল করে।
যারা এখানে আগ্রহী তাদের জন্য x64 এসেম্বলারের কোড, জিসিসি 4.6.3 দ্বারা উত্পাদিত, এক্স 64 এর জন্য উবুন্টু 12.04.2 এ চলছে:
movw $5, (%rdi)
movq $6, (%rsi)
movl $.LC0, %edi
jmp puts
সুতরাং যদি শর্তটি পুরোপুরি এসেম্বেলার কোড থেকে চলে যায়।
long long*
এবং int64_t
* এর সত্য )। এক আশা করতে পারে যে একটি বিবেকী কম্পাইলার স্বীকার করা উচিত যে একটি long long*
এবং int64_t*
একই স্টোরেজ যদি তারা অভিন্নরুপে সঞ্চিত অ্যাক্সেস করতে পারে, কিন্তু এই ধরনের চিকিত্সা কেতাদুরস্ত এখন আর নেই।
পয়েন্টার কাস্টের মাধ্যমে টাইপ পেনিং (ইউনিয়ন ব্যবহারের বিপরীতে) কঠোর আলিয়াজিং ভাঙার একটি বড় উদাহরণ।
fpsync()
এফপি হিসাবে লেখার এবং ইন্ট বা বিপরীতভাবে পড়ার মধ্যে [পৃথক পূর্ণসংখ্যার এবং এফপিইউ পাইপলাইন এবং ক্যাশের সাথে বাস্তবায়নের ক্ষেত্রে কার্যকর হয় code , এই জাতীয় নির্দেশিকা ব্যয়বহুল হতে পারে, তবে প্রতিটি ইউনিয়ন অ্যাক্সেসে সংকলকটি এ জাতীয় সিঙ্ক্রোনাইজেশন সম্পাদনের মতো ব্যয়বহুল নয়]। বা কোনও বাস্তবায়ন নির্দিষ্ট করে দিতে পারে যে সাধারণ প্রাথমিক সিকোয়েন্সগুলি ব্যবহার করে ফলাফল ছাড়া ফলাফল কখনই ব্যবহারযোগ্য হবে না।
সি 89 এর যুক্তি অনুসারে, স্ট্যান্ডার্ডের লেখকরা এই সংকলকদের কোডের মতো কোডের প্রয়োজন পড়েনি:
int x;
int test(double *p)
{
x=5;
*p = 1.0;
return x;
}
মান পুনরায় লোড করা প্রয়োজন হবে x
নিয়োগ এবং বিনিময়ে বিবৃতি মধ্যে, যাতে সম্ভাবনা জন্য অনুমতি p
শক্তি বিন্দু x
, এবং নিয়োগ *p
শক্তি পরিণামে মান পরিবর্তন x
। কোনও সংকলক উপরের মতো পরিস্থিতিতে এলিয়াস হবে না এমন ধারণা করার অধিকারী হওয়া উচিত এই ধারণাটি বিতর্কিত ছিল না।
দুর্ভাগ্যক্রমে, সি 89 এর লেখকরা তাদের নিয়মটি এমনভাবে লিখেছিলেন যে, যদি আক্ষরিক অর্থে পাঠ করা হয় তবে নীচের ফাংশনটি এমনকি অনির্ধারিত আচরণকেও আহ্বান জানায়:
void test(void)
{
struct S {int x;} s;
s.x = 1;
}
কারণ এটি কোনও ধরণের int
অবজেক্টটিতে অ্যাক্সেস পেতে টাইপের একটি লভ্যালু ব্যবহার করে struct S
এবং int
এটি অ্যাক্সেস ব্যবহার করে এমন ধরণের মধ্যে নয় struct S
। যেহেতু স্ট্রাক্ট এবং ইউনিয়নগুলির অ-চরিত্র-ধরণের সদস্যদের সমস্ত ব্যবহারকে অপরিজ্ঞাত আচরণ হিসাবে বিবেচনা করা অযৌক্তিক হবে, প্রায় প্রত্যেকেই স্বীকার করেছেন যে কমপক্ষে এমন কিছু পরিস্থিতিতে রয়েছে যেখানে অন্য ধরণের কোনও অবজেক্ট অ্যাক্সেস করার জন্য এক ধরণের লভ্যালু ব্যবহার করা যেতে পারে । দুর্ভাগ্যক্রমে, সি স্ট্যান্ডার্ড কমিটি এই পরিস্থিতিতে কী তা নির্ধারণ করতে ব্যর্থ হয়েছে।
বেশিরভাগ সমস্যা হ'ল ত্রুটি প্রতিবেদন # 028 এর ফলাফল, যা এই জাতীয় প্রোগ্রামের আচরণ সম্পর্কে জিজ্ঞাসা করেছিল:
int test(int *ip, double *dp)
{
*ip = 1;
*dp = 1.23;
return *ip;
}
int test2(void)
{
union U { int i; double d; } u;
return test(&u.i, &u.d);
}
ত্রুটি প্রতিবেদন # 28 তে উল্লেখ করা হয়েছে যে প্রোগ্রামটি "ডাবল" টাইপের ইউনিয়ন সদস্যকে লেখার এবং "ইনট" টাইপের কোনও একটি পড়ার ক্রমটি বাস্তবায়ন-সংজ্ঞায়িত আচরণকে আহ্বান করে। এ জাতীয় যুক্তি অযৌক্তিক, তবে কার্যকর ধরণের নিয়মের ভিত্তি তৈরি করে যা মূল সমস্যাটি সমাধান করার জন্য কিছুই না করে অযথা ভাষা জটিল করে তোলে।
মূল সমস্যাটি সমাধানের সর্বোত্তম উপায়টি হ'ল নিয়মের উদ্দেশ্য সম্পর্কে পাদটীকাটি আচরণ করা এবং নিয়মটি বাস্তবায়নযোগ্য নয় যা প্রকৃতপক্ষে এলিয়াসগুলি ব্যবহার করে বিবাদী প্রবেশাধিকারের সাথে জড়িত সেগুলি ব্যতীত নিয়মটিকে প্রয়োগ করা যায় না। এরকম কিছু দেওয়া হয়েছে:
void inc_int(int *p) { *p = 3; }
int test(void)
{
int *p;
struct S { int x; } s;
s.x = 1;
p = &s.x;
inc_int(p);
return s.x;
}
এর মধ্যে কোনও বিরোধ নেই inc_int
কারণ *p
স্টোরেজটিতে অ্যাক্সেস করা সমস্ত অ্যাক্সেসগুলি ধরণের লভ্য দিয়ে সম্পন্ন হয় int
এবং এতে কোনও বিরোধ নেই test
কারণ p
দৃশ্যমান একটি থেকে উদ্ভূত struct S
হয় এবং পরবর্তী সময় s
ব্যবহার করা হয়, সেই স্টোরেজে সমস্ত প্রবেশাধিকার যা কখনও তৈরি হবে will p
ইতিমধ্যে ঘটেছে মাধ্যমে ।
কোডটি যদি কিছুটা পরিবর্তন করা হয় ...
void inc_int(int *p) { *p = 3; }
int test(void)
{
int *p;
struct S { int x; } s;
p = &s.x;
s.x = 1; // !!*!!
*p += 1;
return s.x;
}
এখানে, চিহ্নিত লাইনে p
অ্যাক্সেসের মধ্যে একটি এলিয়াসিং দ্বন্দ্ব রয়েছে s.x
কারণ মৃত্যুর সময় সেই একই সময়ে অন্য রেফারেন্স উপস্থিত রয়েছে যা একই স্টোরেজ অ্যাক্সেস করতে ব্যবহৃত হবে ।
হ্যাড ডিফেক্ট রিপোর্ট 028 বলেছিল যে দুটি উদাহরণের তৈরি এবং ব্যবহারের মধ্যে ওভারল্যাপের কারণে মূল উদাহরণটি ইউবিকে ডেকেছে, এটি "কার্যকর ধরণের" বা এই জাতীয় জটিলতা যুক্ত না করে বিষয়গুলিকে অনেক বেশি পরিষ্কার করে দিত।
অনেক উত্তর পড়ার পরে, আমি কিছু যুক্ত করার প্রয়োজনীয়তা অনুভব করছি:
কঠোর আলিয়াজিং (যা আমি কিছুক্ষণের মধ্যে বর্ণনা করব) গুরুত্বপূর্ণ কারণ :
মেমোরি অ্যাক্সেস ব্যয়বহুল (পারফরম্যান্স ওয়াইস) হতে পারে, এজন্য শারীরিক স্মৃতিতে আবার লেখার আগে সিপিইউ রেজিস্টারগুলিতে ডেটা ম্যানিপুলেট করা হয় ।
যদি দুটি পৃথক সিপিইউ রেজিস্টারগুলিতে ডেটা একই মেমরি স্পেসে লেখা থাকে, আমরা সিটি কোড দিলে কোন ডেটা "বেঁচে থাকবে" তা অনুমান করতে পারি না we
সমাবেশে, যেখানে আমরা ম্যানুয়ালি সিপিইউ নিবন্ধগুলি লোডিং এবং আনলোডিং কোড করি, আমরা জানব কোন ডেটা অক্ষত রয়েছে। তবে সি (কৃতজ্ঞতার সাথে) এই বিবরণটি দূরে রেখে দেয়।
যেহেতু দুটি পয়েন্টার স্মৃতিতে একই অবস্থানকে নির্দেশ করতে পারে, এর ফলে জটিল কোড হতে পারে যা সম্ভাব্য সংঘর্ষগুলি পরিচালনা করে ।
এই অতিরিক্ত কোডটি ধীর এবং পারফরম্যান্সকে ব্যথা দেয় কারণ এটি অতিরিক্ত মেমরি রিড / রাইটিং অপারেশন করে যা ধীরে ধীরে এবং (সম্ভবত) অপ্রয়োজনীয়।
কঠোর এলিয়াসিং নিয়ম আমাদের অপ্রয়োজনীয় মেশিন কোড এড়াতে পারবেন মামলা যেখানে এটি মধ্যে হওয়া উচিত অনুমান করা যে দুটি পয়েন্টার একই মেমরি ব্লক নির্দেশ করে না নিরাপদ (এছাড়াও দেখুন restrict
শব্দ)।
স্ট্রাইক এলিয়াসিং এটি বলে মনে করে নিরাপদ যে বিভিন্ন ধরণের পয়েন্টারগুলি মেমরির বিভিন্ন স্থানে নির্দেশ করে।
কোনও সংকলক যদি লক্ষ্য করে যে দুটি পয়েন্টার বিভিন্ন ধরণের (উদাহরণস্বরূপ, একটি int *
এবং ক float *
) নির্দেশ করে তবে এটি মেমরি ঠিকানাটি পৃথক বলে ধরে নেবে এবং এটি মেশিনের ঠিকানা সংঘর্ষের বিরুদ্ধে রক্ষা করবে না , যার ফলে দ্রুত মেশিন কোড হয়।
উদাহরণস্বরূপ :
নিম্নলিখিত ফাংশন ধরে নেওয়া যাক:
void merge_two_ints(int *a, int *b) {
*b += *a;
*a += *b;
}
কেসটি পরিচালনা করার জন্য a == b
(উভয় পয়েন্টার একই মেমোরিটির দিকে নির্দেশ করে), আমাদের সিপিইউ রেজিস্টারগুলিতে মেমরি থেকে ডেটা লোড করার উপায়টি অর্ডার করে পরীক্ষা করতে হবে, যাতে কোডটি এইভাবে শেষ হতে পারে:
লোড a
এবং b
মেমরি থেকে।
যোগ a
করুন b
।
সংরক্ষণ b
এবং পুনরায় লোড করুন a
।
(সিপিইউ রেজিস্টার থেকে মেমরিতে সংরক্ষণ করুন এবং মেমরি থেকে সিপিইউ রেজিস্টারে লোড করুন)।
যোগ b
করুন a
।
a
মেমোরিতে (সিপিইউ রেজিস্টার থেকে) সংরক্ষণ করুন ।
পদক্ষেপ 3 খুব ধীর কারণ এটি শারীরিক স্মৃতি অ্যাক্সেস প্রয়োজন। যাইহোক, এটা দৃষ্টান্ত যেখানে রক্ষা করার জন্য এর প্রয়োজন a
এবং b
একই মেমরির ঠিকানায় বিন্দু।
কঠোর অ্যালাইজিং আমাদের এই সংস্থাপককে এই মেমরির ঠিকানাগুলি পৃথকভাবে পৃথক করার মাধ্যমে বলার মাধ্যমে এটি রোধ করতে সহায়তা করবে (যা এই ক্ষেত্রে, এমনকি আরও অপ্টিমাইজেশনের অনুমতি দেবে যা পয়েন্টারগুলি একটি মেমরি ঠিকানা ভাগ করে নিলে আরও কার্যকর করা যায় না)।
এটি দুটি উপায় নির্দেশককে বিভিন্ন ধরণের নির্দেশ করে বলা যেতে পারে। অর্থাৎ,
void merge_two_numbers(int *a, long *b) {...}
restrict
কীওয়ার্ড ব্যবহার করে । অর্থাৎ,
void merge_two_ints(int * restrict a, int * restrict b) {...}
এখন, কঠোর আলিয়াজিং বিধিটিকে সন্তুষ্ট করে, পদক্ষেপ 3 এড়ানো যায় এবং কোডটি উল্লেখযোগ্যভাবে দ্রুত চলবে।
আসলে, restrict
কীওয়ার্ডটি যুক্ত করে পুরো ফাংশনটি এতে অনুকূলিত করা যায়:
লোড a
এবং b
মেমরি থেকে।
যোগ a
করুন b
।
a
এবং উভয়ই ফলাফল সংরক্ষণ করুন b
।
সম্ভাব্য সংঘর্ষের কারণে (যেখানে a
এবং b
দ্বিগুণ হওয়ার পরিবর্তে তিনগুণ হবে) এর আগে এই অপ্টিমাইজেশনটি করা যায়নি ।
b
(এটিকে পুনরায় লোড করছি না) এবং পুনরায় লোড করছি a
। আমি আশা করি এটি এখন আরও পরিষ্কার হয়ে গেছে।
restrict
, তবে আমি মনে করি যে পরবর্তীকালে বেশিরভাগ ক্ষেত্রে আরও কার্যকর হতে পারে এবং কিছু প্রতিবন্ধকতাগুলি হ্রাস করা register
এটিকে কিছু ক্ষেত্রে পূরণ করতে দেয় যেখানে সহায়তা restrict
দেয় না। আমি নিশ্চিত নই যে সমস্ত ক্ষেত্রে স্ট্যান্ডার্ডকে সম্পূর্ণরূপে বর্ণনা করার মতো "গুরুত্বপূর্ণ" ছিল যেখানে প্রোগ্রামাররা কমপিউন্ডারদের এলিয়াসিংয়ের প্রমাণগুলি সনাক্ত করার প্রত্যাশা করা উচিত, যেখানে সুনির্দিষ্ট প্রমাণ উপস্থিত না থাকলেও কম্পাইলারদের অবশ্যই এলিয়াসিংয়ের অনুমান করা উচিত ।
restrict
কীওয়ার্ডটি কেবল অপারেশনগুলির গতিই নয়, তাদের সংখ্যাও হ্রাস করে, যা অর্থবহ হতে পারে ... আমি বলতে চাইছি, সর্বোপরি, দ্রুততম অপারেশন
কঠোর আলিয়াসিং একই পাতায় বিভিন্ন পয়েন্টার প্রকারের অনুমতি দিচ্ছে না।
এই নিবন্ধটি আপনাকে পুরো বিশদে বিষয়টি বুঝতে সহায়তা করবে।
int
এবং একটি স্ট্রাক্ট যা একটি রয়েছে int
)।
প্রযুক্তিগতভাবে সি ++ এ, কঠোর আলিয়াজিং বিধি সম্ভবত কখনও প্রযোজ্য নয়।
ইন্ডিয়ারেশনের সংজ্ঞাটি ( * অপারেটর ) নোট করুন :
ইউনারী * অপারেটর সঞ্চালিত অপ্রত্যক্ষ্যতার: অভিব্যক্তি যা তা প্রয়োগ করা একটি বস্তু টাইপ একটি পয়েন্টার, অথবা একটি ফাংশন টাইপ একটি পয়েন্টার হইবেন এবং ফলাফলের একটি lvalue বস্তুর উল্লেখ করা হয় বা ফাংশন যা অভিব্যক্তি পয়েন্ট ।
আঠালো সংজ্ঞা থেকে
একটি আঠালো এমন একটি অভিব্যক্তি যার মূল্যায়ন কোনও বস্তুর পরিচয় নির্ধারণ করে, (... স্নিপ)
সুতরাং যে কোনও সংজ্ঞায়িত প্রোগ্রামের ট্রেসগুলিতে, একটি গ্লাভ্যু একটি অবজেক্টকে বোঝায়। সুতরাং তথাকথিত কঠোর aliasing নিয়ম প্রয়োগ হয় না, কখনও। ডিজাইনাররা যা চেয়েছিলেন এটি হতে পারে না।
int foo;
, ল্যাভেলু এক্সপ্রেশন দ্বারা কী অ্যাক্সেস করা যায় *(char*)&foo
? এটি কি কোনও ধরণের জিনিস char
? সেই বস্তুটি কি একই সাথে অস্তিত্বে আসে foo
? লিখিতরূপে foo
সেই ধরণের পূর্বোক্ত বস্তুর সঞ্চিত মান পরিবর্তন করতে হবে char
? যদি তাই হয়, এমন কোনও নিয়ম রয়েছে যা ধরণের char
কোনও অবলম্বনের সাহায্যে টাইপের কোনও অবজেক্টের সঞ্চিত মান অ্যাক্সেস করার অনুমতি দেয় int
?
int i;
প্রতিটি অক্ষরের in addition to one of type
int ? I see no way to apply a consistent definition of "object" which would allow for operations on both
* (চর *) এবং i` এবং i
। এর চারটি অবজেক্ট তৈরি হয় । অবশেষে, স্ট্যান্ডার্ডের এমন কিছুই নেই যা এমন কোনও volatile
যোগ্য পয়েন্টারকে হার্ডওয়্যার রেজিস্টারগুলিতে অ্যাক্সেস করার অনুমতি দেয় যা "অবজেক্ট" এর সংজ্ঞা পূরণ করে না।
c
এবং এর সাথেc++faq
।