"সর্বদা ভেরিয়েবল সূচনা করা" কী গুরুত্বপূর্ণ বাগগুলি আড়াল করার দিকে পরিচালিত করে না?


35

সি ++ মূল নির্দেশিকাতে ES.20 বিধি রয়েছে: সর্বদা কোনও বস্তুর সূচনা করুন

ব্যবহৃত-পূর্বে-সেট ত্রুটি এবং তাদের সম্পর্কিত অপরিবর্তিত আচরণ এড়িয়ে চলুন। জটিল আরম্ভের বোধগম্যতা নিয়ে সমস্যাগুলি এড়ান। রিফ্যাক্টরিং সরল করুন।

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

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

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

এবং অবশেষে স্থিতিশীল বিশ্লেষক রয়েছে, যা প্রোগ্রামটি দেখতে পারে এবং বলতে পারে যে কার্যকর করার পথে একটি পঠন-পূর্ব-সেট রয়েছে।

সুতরাং আমাদের কাছে অনেক শক্তিশালী সরঞ্জাম রয়েছে তবে আমরা যদি ভেরিয়েবলটি শুরু করি - স্যানিটাইজাররা কিছুই খুঁজে পান না

int bytes_read = 0;
my_read(buffer, &bytes_read); // err_t my_read(buffer_t, int*);
// bytes_read is not changed on read error.
// It's a bug of "my_read", but detection is suppressed by initialization.
buffer.shrink(bytes_read); // Uninitialized bytes_read could be detected here.

// Another bug: use empty buffer after read error.
use(buffer);

আরও একটি নিয়ম রয়েছে - যদি প্রোগ্রাম এক্সিকিউশনটির সাথে বাগের মুখোমুখি হয়, প্রোগ্রামটি যত তাড়াতাড়ি সম্ভব মারা যায়। এটিকে বাঁচিয়ে রাখার দরকার নেই, কেবল ক্রাশ হবে, ক্র্যাশডাম্প লিখবে, ইঞ্জিনিয়ারদের তদন্তের জন্য দেবে।
অযৌক্তিকভাবে ভেরিয়েবলগুলি আরম্ভ করা বিপরীতভাবে করে - প্রোগ্রামটি জীবিত রাখা হচ্ছে, যখন এটি ইতিমধ্যে অন্যথায় কোনও সেগমেন্টেশন ফল্ট পাবে।


10
যদিও আমি মনে করি এটি একটি ভাল প্রশ্ন, তবে আমি আপনার উদাহরণটি বুঝতে পারি না। যদি কোনও পঠন ত্রুটি দেখা দেয় এবং bytes_readপরিবর্তিত হয় না (তাই শূন্য রাখা থাকে) তবে এটি কেন বাগ হওয়ার কথা? যতক্ষণ না bytes_read!=0পরে স্পষ্টতই এটি প্রত্যাশা করে না ততক্ষণ প্রোগ্রামটি বুদ্ধিমান পদ্ধতিতে চালিয়ে যেতে পারে । সুতরাং এটি জরিমানা স্যানিটাইজাররা অভিযোগ করবেন না। অন্যদিকে, যখন bytes_readআগে শুরু করা হয় না, প্রোগ্রামটি বুদ্ধিমান পদ্ধতিতে চালিয়ে যেতে সক্ষম হবে না, সুতরাং আরম্ভ না করা bytes_readআসলে একটি বাগটি প্রবর্তন করে যা আগে ছিল না।
ডক ব্রাউন

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

1
এই কোড err_tদ্বারা ফিরে আসা উপেক্ষা করার কোন কারণ আছে my_read()? উদাহরণে কোথাও যদি বাগ থাকে তবে তা।
ব্লারফ্লা

1
এটি সহজ: কেবল অর্থবোধক হলে চলকগুলি সূচনা করুন। যদি তা না হয় তবে না। যদিও আমি এটি করতে "ডামি" ডেটা ব্যবহার করা খারাপ, যদিও এটি বাগগুলি আড়াল করে agree
পিটার বি

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

উত্তর:


44

আপনার যুক্তি বেশ কয়েকটি অ্যাকাউন্টে ভুল হয়েছে:

  1. বিভাজন ত্রুটিগুলি ঘটবে নিশ্চিত থেকে দূরে। অনির্ধারিত আচরণে অবিচ্ছিন্ন পরিবর্তনশীল ফলাফলগুলি ব্যবহার করা । বিভাগের ত্রুটিগুলি এমন একটি উপায় যা এই ধরনের আচরণ নিজেই প্রকাশ করতে পারে তবে স্বাভাবিকভাবে চালানো প্রদর্শিত হবে ঠিক ততটাই সম্ভাবনা।
  2. সংকলকরা নির্বিঘ্নিত মেমরিটি কখনই সংজ্ঞায়িত প্যাটার্ন (0xCD এর মতো) দিয়ে পূরণ করে না। এটি এমন কিছু যা অবিচ্ছিন্ন ভেরিয়েবলগুলি ব্যবহৃত হয় এমন জায়গাগুলি সন্ধানে আপনাকে কিছু সহায়তা করতে ডিবাগাররা আপনাকে সহায়তা করে। আপনি যদি কোনও ডিবাগারের বাইরে এই জাতীয় প্রোগ্রাম চালনা করেন তবে চলকটিতে সম্পূর্ণ র্যান্ডম আবর্জনা থাকবে। এটি সমানভাবে সম্ভবত: এর মতো একটি কাউন্টারেরও bytes_readএর মূল্য 10রয়েছে বলে মান 0xcdcdcdcd
  3. এমনকি আপনি যদি এমন কোনও ডিবাগারে ছুটে চলেছেন যা অবিচ্ছিন্ন মেমরিটিকে একটি নির্দিষ্ট প্যাটার্নে সেট করে, তারা কেবল স্টার্টআপে এটি করে। এর অর্থ এই যে প্রক্রিয়াটি কেবল স্থির (এবং সম্ভবত হিপ-বরাদ্দ) ভেরিয়েবলের জন্য নির্ভরযোগ্যভাবে কাজ করে। স্বয়ংক্রিয় ভেরিয়েবলগুলির জন্য, যা স্ট্যাকের জন্য বরাদ্দ পাওয়া যায় বা কেবল একটি রেজিস্টারে লাইভ হয়, সম্ভাবনা বেশি যে ভেরিয়েবলটি এমন কোনও স্থানে সংরক্ষণ করা হয়েছিল যা আগে ব্যবহৃত হয়েছিল, তাই টেল-টেল মেমরি প্যাটার্নটি ইতিমধ্যে ওভাররাইট করা হয়েছে।

সবসময় পরিবর্তনশীল সূচনা করার দিকনির্দেশের পিছনে ধারণাটি এই দুটি পরিস্থিতি সক্ষম করে তোলা

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

  2. চলকটিতে একটি সংজ্ঞায়িত মান রয়েছে যা আপনি পরে পরীক্ষা করতে পারবেন, কোনও ফাংশনটির মতো my_readমান আপডেট হয়েছে কিনা তা জানাতে । আরম্ভ না করে, আপনি বলতে পারবেন না যে bytes_readআসলে একটি বৈধ মান আছে কিনা , কারণ আপনি জানেন না যে এটি কী মান দিয়ে শুরু হয়েছিল।


8
1) এটি সম্ভাব্যতা সম্পর্কে 1% বনাম 99% এর মতো। 2 এবং 3) ভিসি ++ স্থানীয় ভেরিয়েবলগুলির জন্যও এই জাতীয় সূচনা কোড উত্পন্ন করে। 3) স্ট্যাটিক (গ্লোবাল) ভেরিয়েবলগুলি সর্বদা 0 দিয়ে শুরু করা হয়
অ্যাবিক্স

5
@ অ্যাকেক্স: ১) আমার অভিজ্ঞতায় সম্ভাবনা হ'ল% 80% "অবিলম্বে সুস্পষ্ট আচরণগত পার্থক্য নেই", 10% "ভুল কাজ করে", 10% "সেগফল্ট"। (2) এবং (3) হিসাবে: ভিসি ++ এটি কেবল ডিবাগ বিল্ডগুলিতেই করে। এটির উপর নির্ভর করা একটি মারাত্মক খারাপ ধারণা কারণ এটি নির্বাচিতভাবে প্রকাশনা বিল্ডগুলি বিরতি দেয় এবং আপনার প্রচুর পরীক্ষায় প্রদর্শিত হয় না।
খ্রিস্টান আইচিংগার

8
আমি মনে করি "দিকনির্দেশের পিছনে ধারণা" এই উত্তরের সবচেয়ে গুরুত্বপূর্ণ অংশ। দিকনির্দেশনা একেবারে আপনাকে প্রতিটি চলক সংক্রান্ত ঘোষণা অনুসরণ করতে বলছে না = 0;। পরামর্শের অভিপ্রায়টি সেই স্থানে ভেরিয়েবল ঘোষণা করা হয় যেখানে এর জন্য আপনার একটি কার্যকর মূল্য হবে এবং অবিলম্বে এই মানটি নির্ধারণ করুন। এটি তাত্ক্ষণিকভাবে নিম্নলিখিত ES21 এবং ES22 বিধিগুলিতে স্পষ্টভাবে পরিষ্কার করা হয়েছে। এই তিনটিই এক সাথে কাজ করা হিসাবে বোঝা উচিত; স্বতন্ত্র সম্পর্কযুক্ত নিয়ম হিসাবে নয়।
গ্র্যান্ডঅপেনার

1
@ গ্র্যান্ডওপেনার অবিকল ভেরিয়েবলটি যে স্থানে ঘোষিত হয়েছে সেই স্থানে যদি নির্ধারিত অর্থবহ মান না থাকে তবে ভেরিয়েবলের ক্ষেত্রটি সম্ভবত ভুল।
কেভিন ক্রামউইদে

5
"সংকলকরা কখনই পূরণ করে না" তা কি সর্বদা হওয়া উচিত নয় ?
কোডসইনচাউস

25

আপনি লিখেছেন "এই নিয়মটি বাগগুলি সন্ধান করতে সহায়তা করে না, এটি কেবল তাদের আড়াল করে" - ভাল, নিয়মের লক্ষ্যটি বাগগুলি অনুসন্ধানে সহায়তা করা নয়, তবে এড়ানো এড়ানো । এবং যখন কোনও বাগ এড়ানো হয়, তখন কিছুই লুকানো থাকে না।

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

তবে ভিন্ন ভিন্ন দৃষ্টিভঙ্গি রয়েছে, যা অন্তত সমানভাবে বৈধ: ত্রুটিপূর্ণ আচরণটি কেবল আগেই আরম্ভ না করা এবং পরে কল করার সংমিশ্রণ থেকে উদ্ভূত হয় (প্রত্যাশার সাথে তার পরে আরম্ভ হয়)। এটি এমন একটি পরিস্থিতি যা প্রায়শই বাস্তব বিশ্বের উপাদানগুলিতে ঘটবে যখন কোনও ফাংশনের জন্য লিখিত ধারণাটি 100% পরিষ্কার নয় বা ত্রুটির ক্ষেত্রে আচরণ সম্পর্কে ভুলও নয়। যাইহোক, যতক্ষণ না ডাক দেওয়ার আগে শূন্যে সূচনা করা হয় ততক্ষণ প্রোগ্রামটি ঠিক একইভাবে আচরণ করে যেমন আদ্যক্ষরটি ভিতরে করা হয়েছিল , সুতরাং এটি সঠিকভাবে আচরণ করে, এই সংমিশ্রণে প্রোগ্রামটিতে কোনও ত্রুটি নেই।bytes_readmy_readbytes_readmy_readbytes_readmy_read

সুতরাং এটি থেকে আমার প্রস্তাবনাটি হ'ল: অ-আরম্ভের পদ্ধতিটি যদি কেবলমাত্র ব্যবহার করুন

  • কোনও ফাংশন বা কোড ব্লক একটি নির্দিষ্ট পরামিতি সূচনা করে তবে আপনি পরীক্ষা করতে চান
  • আপনি 100% নিশ্চিত যে অংশে ফাংশনটির একটি চুক্তি রয়েছে যেখানে প্যারামিটারটির কোনও মান নির্ধারণ না করা অবশ্যই ভুল
  • আপনি 100% নিশ্চিত যে পরিবেশ এটি ধরতে পারে

নির্দিষ্ট শর্তাদি পরিবেশের জন্য আপনি সাধারণত শর্তাদি পরীক্ষার কোডে সাজিয়ে নিতে পারেন।

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


4
এটি পড়ার সময় আমি ঠিক এটিই ভাবছিলাম। এটি গালির নীচে জিনিসগুলি ঝাড়ফুঁক করছে না, এটি ডাস্টবিনে ঝাড়িয়ে দিচ্ছে!
কর্সিকা

22

সর্বদা আপনার ভেরিয়েবলগুলি আরম্ভ করুন

আপনি যে পরিস্থিতিগুলি বিবেচনা করছেন তার মধ্যে পার্থক্য হ'ল সূচনা ব্যতিরেকে ক্ষেত্রে সংজ্ঞাবিহীন আচরণের ফলাফল হয় , আপনি যেখানে সূচনা করার সময় নিয়েছিলেন সেই ক্ষেত্রে একটি ভাল সংজ্ঞায়িত এবং নির্বিচারবাদী ত্রুটি তৈরি হয়। এই দু'টি ক্ষেত্রেই যথেষ্ট পার্থক্য করার বিষয়টি আমি চাপ দিতে পারি না।

একটি অনুমানমূলক উদাহরণ বিবেচনা করুন যা অনুমানকৃত সিমুলেশন প্রোগ্রামে অনুমান কর্মীর ক্ষেত্রে ঘটেছে। এই কল্পিত দলটি অনুমানকভাবে একটি উত্পাদক সংকেত তৈরির চেষ্টা করে যা দেখানোর জন্য যে তারা যে পণ্যটিকে অনুমানকভাবে বিক্রি করছিল তা পূরণের প্রয়োজনীয়তাগুলি দেখায়।

ঠিক আছে, আমি ইনজেকশন শব্দটি বন্ধ করব। আমি মনে করি আপনি পয়েন্টটি পেয়ে গেছেন ;-)

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

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

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

এবং এই সতর্কতা শট। এই বুলেটটি আপনার নাক জুড়ে চরেছে। আসল সমস্যাটি আপনি কল্পনাও না করে অনেক বেশি দূরে প্রতারণাপূর্ণ।

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

বা এটি কিছু করতে পারে না

ইউবির জন্য কয়লা খনিতে আমার ক্যানারি একটি এসকিউএল ইঞ্জিনের একটি ঘটনা যা আমি পড়েছিলাম। এটি লিঙ্ক না করার জন্য আমাকে ক্ষমা করুন, আমি আর্টিকেলটি আবার খুঁজে পেতে ব্যর্থ হয়েছি। এসকিউএল ইঞ্জিনে একটি বাফার ওভাররন সমস্যা ছিল যখন আপনি কোনও ফাংশনে বড় বাফার আকারটি পেরিয়েছিলেন তবে কেবলমাত্র ডেবিয়ানের একটি নির্দিষ্ট সংস্করণে। ত্রুটিটি যথাযথভাবে লগ হয়েছে এবং অন্বেষণ করেছে। মজার অংশটি ছিল: বাফারকে ওভাররন পরীক্ষা করা হয়েছিল । জায়গায় বাফারকে ওভাররন করার জন্য কোড ছিল। এটি এমন কিছু দেখায়:

// move the pointers properly to copy data into a ring buffer.
char* putIntoRingBuffer(char* begin, char* end, char* get, char*put, char* newData, unsigned int dataLength)
{
    // If dataLength is very large, we might overflow the pointer
    // arithmetic, and end up with some very small pointer number,
    // causing us to fail to realize we were trying to write past the
    // end.  Check this before we continue
    if (put + dataLength < put)
    {
        RaiseError("Buffer overflow risk detected");
        return 0;
    }
    ...
    // typical ring-buffer pointer manipulation followed...
}

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

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

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

"বুদ্ধিমান" ইউবি ব্যবহারের ফলে একটি বড় এসকিউএল পণ্যটি বাফারকে ছাড়িয়ে যেতে পারে যে এটি এড়াতে লিখিত কোড ছিল!

অনির্ধারিত আচরণের উপর কখনই নির্ভর করবেন না। কখনো।


অনির্ধারিত আচরণের উপর একটি মজাদার পড়ার জন্য, সফটওয়্যার.ইনটেল.টেন / en-us/blogs/2013/01/06/… এটি কীভাবে খারাপ হতে পারে সে সম্পর্কে একটি আশ্চর্যজনকভাবে লেখা একটি পোস্ট। তবে, সেই নির্দিষ্ট পোস্টটি পারমাণবিক ক্রিয়াকলাপগুলিতে রয়েছে, যা বেশিরভাগের জন্য খুব বিভ্রান্তিকর, তাই আমি এটি ইউবির প্রাইমারের হিসাবে সুপারিশ করা এড়াতে এবং এটি কীভাবে ভুল হতে পারে তা এড়িয়ে চলি avoid
আমটন কর্ট - মনিকা

1
আমি চাই সি এর নির্ধারিত মানগুলি বা অবিচ্ছিন্ন মান, বা অনির্ধারিত মানগুলিতে অনিচ্ছাকৃত, অ-ট্র্যাপিং অনির্দিষ্ট মান বা অনির্ধারিত মানগুলির জন্য একটি মূল্যবান বা তাদের একটি অ্যারে সেট করার জন্য অন্তর্নিহিত রয়েছে বা নির্ধারিত মানগুলিকে একা রেখে অবিচ্ছিন্ন অবলম্বনগুলিকে (অ-ট্র্যাপিং অনির্দিষ্ট বা অনির্ধারিত) পরিণত করে। কম্পাইলাররা দরকারী অপ্টিমাইজেশানগুলিকে সহায়তা করার জন্য এই জাতীয় নির্দেশিকা ব্যবহার করতে পারে এবং স্পার-ম্যাট্রিক্স কৌশলগুলির মতো জিনিসগুলি ব্যবহার করার সময় ব্রেকিং "অপ্টিমাইজেশন" অবরুদ্ধ করতে গিয়ে প্রোগ্রামাররা তাদের অকেজো কোডটি লিখতে না পারত to
সুপারক্যাট

@ সুপের্যাট এটি একটি দুর্দান্ত বৈশিষ্ট্য হবে, ধরে নিলে আপনি প্ল্যাটফর্মকে টার্গেট করছেন যেখানে এটি একটি বৈধ সমাধান। জ্ঞাত সমস্যার উদাহরণগুলির মধ্যে একটি হ'ল মেমরি ধরণগুলি তৈরি করার ক্ষমতা যা কেবল মেমরির ধরণের জন্যই অবৈধ নয়, তবে সাধারণ উপায়ে অর্জন করা অসম্ভব। boolসুস্পষ্ট সমস্যাগুলির মধ্যে একটি দুর্দান্ত উদাহরণ, তবে আপনি x86 বা এআরএম বা এমআইপিএসের মতো খুব সহায়ক প্ল্যাটফর্মের উপর কাজ করছেন এমন ধারণা না করেই তারা অন্য কোথাও প্রদর্শিত হবে যেখানে এই সমস্ত সমস্যাগুলি ওপকোডের সময় সমাধান হওয়ার সম্ভাবনা রয়েছে।
কর্ট অ্যামোন - মোনিকা

মামলার বিবেচনা করুন যেখানে কোনও অপ্টিমাইজার প্রমাণ করতে পারে যে switchপূর্ণসংখ্যার অঙ্কের আকারের কারণে কোনওটির জন্য ব্যবহৃত মান 8 এর চেয়ে কম হয়, সুতরাং তারা দ্রুত নির্দেশাবলী ব্যবহার করতে পারে যা অনুমান করেছিল যে কোনও "বড়" মান আসার ঝুঁকি নেই। হঠাৎ একটি অনির্ধারিত মান (যা সংকলকের নিয়ম ব্যবহার করে কখনই নির্মাণ করা যায় না) প্রদর্শিত হয়, অপ্রত্যাশিত কিছু করে, এবং হঠাৎ করে আপনি একটি জাম্প টেবিলের শেষের দিকে প্রচুর ঝাঁপিয়ে পড়ে। এখানে অনির্ধারিত ফলাফলের অনুমতি দেওয়ার অর্থ প্রোগ্রামটিতে প্রতিটি সুইচ স্টেটমেন্টের ক্ষেত্রে এই ঘটনাগুলিকে সমর্থন করতে অতিরিক্ত ফাঁদ থাকতে হবে যা "কখনই ঘটে না"।
কর্ট অ্যামোন - মোনিকা

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

5

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

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

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


5

না, এটি বাগগুলি লুকায় না। পরিবর্তে এটি আচরণকে নির্বিচারে এমন করে তোলে যে কোনও ব্যবহারকারী যদি কোনও ত্রুটির মুখোমুখি হন তবে কোনও বিকাশকারী এটি পুনরুত্পাদন করতে পারে।


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

4

টিএল; ডিআর: এই প্রোগ্রামটিকে সঠিক করার দুটি উপায় রয়েছে, আপনার ভেরিয়েবলগুলি আরম্ভ করুন এবং প্রার্থনা করুন। কেবলমাত্র একটি ধারাবাহিকভাবে ফলাফল সরবরাহ করে।


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

আপনি যদি এই নিবন্ধগুলি পড়তে ইচ্ছুক না হন তবে একটি টিএল; ডিআর হ'ল:

অপরিজ্ঞাত আচরণটি বিকাশকারী এবং সংকলকের মধ্যে একটি সামাজিক চুক্তি; সংকলক অন্ধ বিশ্বাসের সাথে ধরে নিয়েছে যে এর ব্যবহারকারীর কখনও কখনও অপরিবর্তিত আচরণের উপর নির্ভর করবে না।

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

সত্য, কিন্তু, যে অনির্ধারিত আচরণ প্রভাবিত সংকলন নিজে দীর্ঘ আগে আপনি এমনকি প্রোগ্রাম ব্যবহার করার জন্য (instrumented বা না, একটি ডিবাগার মধ্যে বা না হোক) প্রচেষ্টা এবং একদম তার আচরণ পরিবর্তন করতে পারেন।

আমি উপরের ২ য় অংশে উদাহরণটি পেয়েছি:

void contains_null_check(int *P) {
  int dead = *P;
  if (P == 0)
    return;
  *P = 4;
}

রূপান্তরিত হয়:

void contains_null_check(int *P) {
  *P = 4;
}

কারণ এটি স্পষ্টত যেটি চেক হওয়ার আগেই এটির সাথে সম্মানহীন Pনয় cannot0


এটি কীভাবে আপনার উদাহরণের সাথে প্রযোজ্য?

int bytes_read = 0;
my_read(buffer, &bytes_read); // err_t my_read(buffer_t, int*);
// bytes_read is not changed on read error.
// It's a bug of "my_read", but detection is suppressed by initialization.
buffer.shrink(bytes_read); // Uninitialized bytes_read could be detected here.

ঠিক আছে, আপনি এই ধারণা নিয়ে সাধারণ ভুল করেছেন যে অপরিজ্ঞাত আচরণটি রান- টাইমের ত্রুটির কারণ হতে পারে। এটা নাও পারে।

আসুন আমরা কল্পনা করি যে এর সংজ্ঞাটি my_readহ'ল:

err_t my_read(buffer_t buffer, int* bytes_read) {
    err_t result = {};
    int blocks_read = 0;
    if (!(result = low_level_read(buffer, &blocks_read))) { return result; }
    *bytes_read = blocks_read * BLOCK_SIZE;
    return result;
}

এবং ইনলাইনিং সহ একটি ভাল সংকলক হিসাবে প্রত্যাশা হিসাবে এগিয়ে যান:

int bytes_read; // UNINITIALIZED

// start inlining my_read

err_t result = {};
int blocks_read = 0;
if (!(result = low_level_read(buffer, &blocks_read))) {
    // nothing
} else {
    bytes_read = blocks_reads * BLOCK_SIZE;
}

// end of inlining my_read

buffer.shrink(bytes_read);

তারপরে, একটি ভাল সংকলক হিসাবে প্রত্যাশা হিসাবে, আমরা অকেজো শাখা অপ্টিমাইজ:

  1. কোনও পরিবর্তনশীল অবিচ্ছিন্ন ব্যবহার করা উচিত নয়
  2. bytes_readresultনা হলে অবিচ্ছিন্ন ব্যবহার করা হবে0
  3. বিকাশকারী প্রতিশ্রুতি দিচ্ছেন যা resultকখনই হবে না 0!

তাই resultকখনও হয় না 0:

int bytes_read; // UNINITIALIZED
err_t result = {};
int blocks_read = 0;
result = low_level_read(buffer, &blocks_read);

bytes_read = blocks_reads * BLOCK_SIZE;
buffer.shrink(bytes_read);

ওহ, resultকখনও ব্যবহৃত হয় না:

int bytes_read; // UNINITIALIZED
int blocks_read = 0;
low_level_read(buffer, &blocks_read);

bytes_read = blocks_reads * BLOCK_SIZE;
buffer.shrink(bytes_read);

ওহ, আমরা এই ঘোষণাটি স্থগিত করতে পারি bytes_read:

int blocks_read = 0;
low_level_read(buffer, &blocks_read);

int bytes_read = blocks_reads * BLOCK_SIZE;
buffer.shrink(bytes_read);

এবং আমরা এখানে আছি, আসলটির একটি কঠোরভাবে নিশ্চিতকরণ রূপান্তর, এবং কোনও ডিবাগার একটি অবিচ্ছিন্ন ভেরিয়েবলকে আটকাবে না কারণ সেখানে কিছুই নেই।

আমি সেই রাস্তায় নেমেছি, যখন প্রত্যাশিত আচরণ এবং সমাবেশের মিল না হয় তখন বিষয়টি বোঝা সত্যিই মজাদার নয়।


কখনও কখনও আমি মনে করি সংকলকরা যখন কোনও ইউবি পাথ সম্পাদন করে তখন উত্স ফাইলগুলি মুছতে প্রোগ্রামটি পাওয়া উচিত। প্রোগ্রামাররা তখন তাদের শেষ ব্যবহারকারীটির জন্য ইউবি এর অর্থ কী তা শিখবে ....
mattnz

1

আসুন আপনার উদাহরণ কোডটি নিবিড়ভাবে দেখুন:

int bytes_read = 0;
my_read(buffer, &bytes_read); // err_t my_read(buffer_t, int*);
// bytes_read is not changed on read error.
// It's a bug of "my_read", but detection is suppressed by initialization.
buffer.shrink(bytes_read); // Uninitialized bytes_read could be detected here.

// Another bug: use empty buffer after read error.
use(buffer);

এটি একটি ভাল উদাহরণ। যদি আমরা এর মতো কোনও ত্রুটিটি অনুমান করি তবে আমরা লাইনটি সন্নিবেশ করতে assert(bytes_read > 0);এবং রান-টাইমে এই বাগটিটি ধরতে পারি, এটি অনিবার্যতম পরিবর্তনশীল দ্বারা সম্ভব নয় possible

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

আমরা যদি আরম্ভ না করে থাকি bytes_readতবে এতে আবর্জনা রয়েছে। এটি অগত্যা প্রতিবার একই আবর্জনা ধারণ করে না। আমরা লাইন পেরিয়ে my_read(buffer, &bytes_read);। এখন, এটি যদি আগের চেয়ে আলাদা মান হয় তবে আমরা সম্ভবত আমাদের বাগটি পুনরুত্পাদন করতে সক্ষম হব না! এটি পরের বার একই ইনপুটটিতে সম্পূর্ণ দুর্ঘটনার দ্বারা কাজ করতে পারে। যদি এটি ধারাবাহিকভাবে শূন্য হয়, আমরা ধারাবাহিক আচরণ পাই।

আমরা মানটি পরীক্ষা করি, সম্ভবত একই রানের ব্যাকট্র্যাসেও। যদি এটি শূন্য হয় তবে আমরা দেখতে পাচ্ছি যে কিছু ভুল আছে; bytes_readসাফল্যের শূন্য হওয়া উচিত নয়। (বা এটি যদি হতে পারে তবে আমরা এটি -1 এ শুরু করতে চাই)) আমরা সম্ভবত বাগটি এখানে ধরতে পারি। যদি bytes_readএকটি শ্রদ্ধেয় মান হয়, যদিও, এটি ঠিক ভুল হতে থাকে, আমরা কি এটি এক নজরে দেখব?

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


1

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

ওপি যা বলছে তা হ'ল এমন সরঞ্জাম রয়েছে যা আপনি এই বাগটি খুঁজে পেতে সহায়তা করবেন you যদি আপনি মানটি আরম্ভ করেন না, তবে তারপরে আপনি যেভাবেই এটি ব্যবহার করেন, এমন স্থিতিশীল এবং গতিশীল বিশ্লেষক রয়েছে যা আপনাকে বলবে যে আপনার একটি বাগ রয়েছে। একটি স্ট্যাটিক বিশ্লেষক আপনাকে এমনকি প্রোগ্রামটি পরীক্ষা করা শুরু করার আগে বলবে। অন্যদিকে, আপনি যদি অন্ধভাবে মানটি আরম্ভ করেন তবে বিশ্লেষকরা তা বলতে পারবেন না যে আপনি সেই প্রাথমিক মানটি ব্যবহার করার পরিকল্পনা করেন নি, এবং তাই আপনার বাগটি সনাক্ত করা যায় না। আপনি ভাগ্যবান যদি এটি নিরীহ বা কেবল প্রোগ্রামটি ক্র্যাশ করে; আপনি যদি দুর্ভাগ্য হন তবে এটি নিঃশব্দে ডেটাটিকে দূষিত করে।

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


0

আপনার প্রশ্নের উত্তরের একটি প্রোগ্রামের ভিতরে উপস্থিত বিভিন্ন ধরণের ভেরিয়েবলগুলিতে বিভক্ত হওয়া দরকার:


স্থানীয় পরিবর্তনশীল

সাধারণত ঘোষনাটি ঠিক সেই স্থানে হওয়া উচিত যেখানে ভেরিয়েবল প্রথমে এর মান পায়। পুরানো স্টাইল সি এর মতো ভেরিয়েবলগুলি পূর্বনির্ধারিত করবেন না:

//Bad: predeclared variables
int foo = 0;
double bar = 0.0;
long* baz = NULL;

bar = getBar();
foo = (int)bar;
baz = malloc(foo);


//Correct: declaration and initialization at the same place
double bar = getBar();
int foo = (int)bar;
long* baz = malloc(foo);

এটি প্রারম্ভিককরণের প্রয়োজনের 99% সরিয়ে দেয়, ভেরিয়েবলগুলি বন্ধ থেকে তাদের চূড়ান্ত মানটি রাখে। ব্যতিক্রম কয়েকটি যেখানে প্রাথমিক অবস্থার উপর নির্ভর করে কিছু শর্ত:

Base* ptr;
if(foo()) {
    ptr = new Derived1();
} else {
    ptr = new Derived2();
}

আমি বিশ্বাস করি যে এই কেসগুলি এই জাতীয় লেখা ভাল ধারণা:

Base* ptr = nullptr;
if(foo()) {
    ptr = new Derived1();
} else {
    ptr = new Derived2();
}
assert(ptr);

আই। ই। স্পষ্টভাবে দৃ as়ভাবে দাবি করুন যে আপনার পরিবর্তনশীলটির কিছু বুদ্ধিমান আরম্ভ করা হয়েছে is


সদস্য ভেরিয়েবল

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


বাফার

আমি এখানে অন্যান্য উত্তরগুলির সাথে একমত নই। লোকেরা যখন ভেরিয়েবলগুলি সূচনা করার বিষয়ে ধর্মীয় হয়, তারা প্রায়শই এই জাতীয় বাফারটি শুরু করে:

char buffer[30];
memset(buffer, 0, sizeof(buffer));

char* buffer2 = calloc(30);

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

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


গ্লোবাল ভেরিয়েবল

আপনার গ্লোবাল ভেরিয়েবলগুলি থাকতে পারে না যা আরম্ভ করা হয়নি (কমপক্ষে সি / সি ++ ইত্যাদিতে), তাই নিশ্চিত হয়ে নিন যে এই আরম্ভটি আপনি চান তা is


লক্ষ্য করুন যে আপনি টার্নারি অপারেটরের সাথে শর্তসাপেক্ষ সূচনা লিখতে পারেন, যেমন Base& b = foo() ? new Derived1 : new Derived2;
ডেভিস্লার

@ লোরেহেড যা সাধারণ কেসগুলির জন্য কাজ করতে পারে তবে এটি আরও জটিল ক্ষেত্রে কাজ করবে না: আপনার যদি তিন বা ততোধিক ঘটনা ঘটে থাকে তবে আপনি এটি করতে চান না এবং আপনার নির্মাতারা কেবল পঠনযোগ্যতার জন্য তিন বা ততোধিক যুক্তি গ্রহণ করেন কারণ। এবং এটি এমন কোনও গণনাও বিবেচনা করছে না যা করা দরকার হতে পারে, যেমন একটি লুপে আরম্ভের একটি শাখার পক্ষে যুক্তি সন্ধান করার মতো।
মাস্টার

আরো জটিল ক্ষেত্রে, আপনি একটি কারখানা ফাংশনে আরম্ভের কোড মোড়ানো পারে: Base &b = base_factory(which);। আপনার যদি কোডটি একাধিকবার কল করতে হয় বা এটি আপনাকে ফলাফলটিকে একটি ধ্রুবক হতে দেয় তবে এটি সর্বাধিক কার্যকর।
ডেভিস্লোর

@ লোরেহেড এটি সত্য, এবং প্রয়োজনীয় যুক্তিটি সহজ না হলে অবশ্যই যাওয়ার উপায়। তবুও, আমি বিশ্বাস করি যে এর মাঝে একটি ছোট ধূসর অঞ্চল রয়েছে যেখানে ?:পিআইটিএ হয়ে সূচনা হয় এবং একটি কারখানার ফাংশনটি এখনও ওভারকিল হয়। এই কেসগুলি খুব কম এবং এর মধ্যে খুব বেশি, তবে এগুলির উপস্থিতি রয়েছে।
17

-2

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

"প্রাথমিককরণ" এবং "একটি মান সেট করুন" এর মধ্যে একটি যৌক্তিক পার্থক্য রয়েছে। যদি আমি ডলার এবং ইউরোর মধ্যে রূপান্তর হারটি খুঁজে পেতে এবং "ডাবল রেট = 0.0" লিখি; তারপরে ভেরিয়েবলের মান সেট থাকে তবে এটি আরম্ভ হয় না। এখানে সঞ্চিত 0.0 এর সঠিক ফলাফলের সাথে কিছুই করার নেই। এই পরিস্থিতিতে, যদি কোনও বাগের কারণে আপনি কখনই সঠিক রূপান্তর হার সংরক্ষণ করেন না, সংকলকটিতে আপনাকে বলার সুযোগ নেই। আপনি যদি সবেমাত্র "ডাবল রেট" লিখেছিলেন; এবং কখনই অর্থবহ রূপান্তর হার সংরক্ষণ করেনি, সংকলক আপনাকে বলবে।

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

কোনও পরিবর্তনশীল আরম্ভ করবেন না কারণ সংকলক আপনাকে বলতে পারে এটি আরম্ভ না করেই এটি ব্যবহৃত হয় is আবার, আপনি সমস্যাগুলি লুকিয়ে রাখছেন।

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

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

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


এটি অতিমাত্রায় ভাল বলে মনে হয়, তবে অবিবেচনাযুক্ত-মান সতর্কতার যথার্থতার উপর খুব বেশি নির্ভর করে। এগুলি পুরোপুরি সঠিকভাবে পাওয়া সঠিকভাবে থামানো সমস্যার সমতুল্য, এবং উত্পাদন সংকলকগুলি মিথ্যা নেতিবাচক সমস্যায় ভুগতে পারে এবং ভোগ করতে পারে (অর্থাত্ তাদের যখন হওয়া উচিত তখন তারা একটি অবিচ্ছিন্ন ভেরিয়েবল সনাক্ত করে না ); উদাহরণস্বরূপ দেখুন জিসিসি বাগ 18501 , যা এখন দশ বছরেরও বেশি সময় ধরে অনন্য হয়ে গেছে।
zwol

আপনি জিসিসি সম্পর্কে যা বলবেন তা কেবল বলা হয়েছে is বাকীটি অপ্রাসঙ্গিক।
gnasher729

এটি জিসিসির জন্য দুঃখজনক, তবে কেন আপনি বাকিটা প্রাসঙ্গিক তা বুঝতে না পারলে আপনার নিজের শিক্ষিত করা দরকার।
zwol
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.