সি ++ এ সঠিক স্ট্যাক এবং গাদা ব্যবহার?


122

আমি কিছুক্ষণ প্রোগ্রামিং করছি তবে এটি বেশিরভাগ জাভা এবং সি # হয়েছে। আমি আসলে আমার নিজের থেকে মেমরি পরিচালনা করতে হয়নি। আমি সম্প্রতি সি ++ এ প্রোগ্রামিং শুরু করেছি এবং কখন স্ট্যাকের মধ্যে জিনিসগুলি সংরক্ষণ করা উচিত এবং কখন সেগুলি স্তূপে সংরক্ষণ করব তা নিয়ে আমি একটু বিভ্রান্ত হয়ে পড়েছি।

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


উত্তর:


242

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

class Thingy;

Thingy* foo( ) 
{
  int a; // this int lives on the stack
  Thingy B; // this thingy lives on the stack and will be deleted when we return from foo
  Thingy *pointerToB = &B; // this points to an address on the stack
  Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap.
                                     // pointerToC contains its address.

  // this is safe: C lives on the heap and outlives foo().
  // Whoever you pass this to must remember to delete it!
  return pointerToC;

  // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
  // whoever uses this returned pointer will probably cause a crash!
  return pointerToB;
}

স্ট্যাকটি কী তা বোঝার জন্য এটি অন্য প্রান্ত থেকে আসুন - উচ্চ স্তরের ভাষার ক্ষেত্রে স্ট্যাকটি কী করে তা বোঝার চেষ্টা না করে "কল স্ট্যাক" এবং "কলিং কনভেনশন" দেখুন এবং দেখুন আপনি যখন কোনও ফাংশন কল করেন তখন মেশিনটি সত্যিই তা করে। কম্পিউটার মেমরি ঠিক ঠিক একটি সিরিজ; "হিপ" এবং "স্ট্যাক" সংকলকের আবিষ্কার।


7
পরিবর্তনশীল আকারের তথ্যগুলি সাধারণত গাদা হয়ে যায় তা যুক্ত করা নিরাপদ হবে। কেবলমাত্র ব্যতিক্রমগুলি সম্পর্কে আমি সচেতন সেগুলি হ'ল সিএল-তে ভিএলএর (যার সীমিত সমর্থন রয়েছে) এবং বরাদ্দ () ফাংশন যা প্রায়শই সি প্রোগ্রামাররাও ভুল বোঝে।
ড্যান অলসন 8

10
ভাল ব্যাখ্যা, যদিও ঘন ঘন বরাদ্দ এবং / বা অবনতির সাথে বহুবিশ্লেষিত দৃশ্যে, গাদা হ'ল বিতর্ক একটি বিষয়, এইভাবে কর্মক্ষমতা প্রভাবিত করে। তবুও, ব্যাপ্তি প্রায় সবসময় সিদ্ধান্ত গ্রহণকারী ফ্যাক্টর।
পিটারচেন

18
অবশ্যই, এবং নতুন / ম্যালোক () নিজেই একটি ধীর গতি সম্পন্ন অপারেশন, এবং স্ট্যাকটি একটি স্বেচ্ছাসেবক হিপ লাইনের চেয়ে ডিসকাশে থাকার সম্ভাবনা বেশি। এগুলি বাস্তব বিবেচ্য বিষয়, তবে সাধারণত আজীবন প্রশ্নে গৌণ।
ক্র্যাশওয়ার্কস

1
এটি কি সত্য "কম্পিউটারের মেমরি কেবল ঠিকানাগুলির একটি সিরিজ;" হিপ "এবং" স্ট্যাক "সংকলনের আবিষ্কার" ?? আমি অনেক জায়গায় পড়েছি যে স্ট্যাকটি আমাদের কম্পিউটারের স্মৃতির একটি বিশেষ অঞ্চল।
ভিনিথ চিত্তেটি

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

42

আমি বলব:

আপনি যদি পারেন তবে এটি স্ট্যাকের উপরে সঞ্চয় করুন।

আপনার যদি প্রয়োজন হয় তবে এটি গাদাতে সঞ্চয় করুন।

সুতরাং, গাদা থেকে স্ট্যাকটি পছন্দ করুন। আপনি স্ট্যাকের উপর কিছু সঞ্চয় করতে পারবেন না এমন কয়েকটি সম্ভাব্য কারণ হ'ল:

  • এটি খুব বড় - ৩২-বিট ওএসের মাল্টিথ্রেডেড প্রোগ্রামগুলিতে স্ট্যাকটির একটি ছোট এবং স্থির (কমপক্ষে থ্রেড-তৈরির সময়) কম পরিমাণে (সাধারণত মাত্র কয়েক মেগ) থাকে যাতে আপনি ক্লান্তিকর ঠিকানা ছাড়াই প্রচুর থ্রেড তৈরি করতে পারেন স্পেস। 64৪-বিট প্রোগ্রাম, বা একক থ্রেডেড (লিনাক্স যাইহোক) প্রোগ্রামগুলির জন্য, এটি কোনও বড় সমস্যা নয় 32২-বিট লিনাক্সের অধীনে, একক থ্রেডেড প্রোগ্রামগুলি সাধারণত গতিশীল স্ট্যাকগুলি ব্যবহার করে যা গাদা শীর্ষে না পৌঁছানো অবধি বাড়তে থাকবে।
  • আপনার এটির মূল স্ট্যাক ফ্রেমের আওতার বাইরে অ্যাক্সেস করা দরকার - এটি সত্যই প্রধান কারণ।

বোধগম্য সংকলক সহ, এটি গাদাতে স্থির-অ-স্থিত আকারের অবজেক্টগুলি বরাদ্দ করা সম্ভব (সাধারণত অ্যারে যার আকার সংকলনের সময় জানা যায় না)।


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

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

1
ড্যান: আমি 32 জিবি লিনাক্সের নীচে স্ট্যাকের উপরে 2 টি জিগ (হ্যাঁ, জিআইজিএস এর মতো) রেখেছি। স্ট্যাক সীমাগুলি ওএস নির্ভর।
মিঃআরআই

6
শ্রীঃ নিন্টেন্ডো ডিএস স্ট্যাকটি 16 কিলোবাইট। কিছু স্ট্যাক সীমা হার্ডওয়্যার নির্ভর।
পিঁপড়া

পিঁপড়া: সমস্ত স্ট্যাকগুলি হার্ডওয়্যার নির্ভর, ওএস নির্ভর, এবং সংকলক নির্ভর।
উইলিয়ামি

24

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

std::vector<int> v(10);

কোনও ফাংশনের মূল অংশে, vectorএটি স্ট্যাকের দশটি পূর্ণসংখ্যার একটি (গতিশীল অ্যারে) ঘোষণা করে । কিন্তু দ্বারা পরিচালিত স্টোরেজ vectorস্ট্যাকের মধ্যে নেই।

আহ, তবে (অন্যান্য উত্তরগুলি বোঝায়) সেই স্টোরেজটির জীবনকাল vectorনিজের জীবনকালের সাথে আবদ্ধ , যা এখানে স্ট্যাক-ভিত্তিক, তাই এটি কীভাবে প্রয়োগ করা যায় তাতে কোন পার্থক্য নেই - আমরা কেবল এটি স্ট্যাক-ভিত্তিক অবজেক্ট হিসাবে বিবেচনা করতে পারি মান শব্দার্থক সঙ্গে।

তাই না। ধরুন ফাংশনটি ছিল:

void GetSomeNumbers(std::vector<int> &result)
{
    std::vector<int> v(10);

    // fill v with numbers

    result.swap(v);
}

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

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

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

আপনাকে অনুকূলিতকরণে সহায়তা করার জন্য আপনাকে কেবল একটি বিশেষ জ্ঞান বজায় রাখতে হবে: যেখানে সম্ভব, এর পরিবর্তে অন্যটিকে একটি ভেরিয়েবল নির্ধারণের পরিবর্তে:

a = b;

এগুলিকে এভাবে বদলে দিন:

a.swap(b);

কারণ এটি অনেক দ্রুত এবং এটি ব্যতিক্রম ছুঁড়ে না। একমাত্র প্রয়োজন হ'ল আপনাকে bএকই মান ধরে রাখা চালিয়ে যাওয়ার দরকার হবে না (এটি aপরিবর্তে এর মান পাবে যা ট্র্যাশে ফেলা হবে a = b)।

ক্ষতিটি হ'ল এই পদ্ধতির আপনাকে আসল প্রত্যাবর্তনের মানের পরিবর্তে আউটপুট প্যারামিটারগুলির মাধ্যমে ফাংশন থেকে মানগুলি ফেরত দিতে বাধ্য করে। কিন্তু তারা যে সঙ্গে সি ++ 0x মধ্যে স্থাপন করছি rvalue রেফারেন্স

সবার মধ্যে সবচেয়ে জটিল পরিস্থিতিতে আপনি এই ধারণাটি সাধারণ shared_ptrচরমের দিকে নিয়ে যান এবং একটি স্মার্ট পয়েন্টার শ্রেণি ব্যবহার করবেন যা ইতিমধ্যে টিআর 1-এ রয়েছে। (যদিও আমি যুক্তি দিচ্ছি যে আপনার যদি এটির প্রয়োজন মনে হয় তবে আপনি সম্ভবত স্ট্যান্ডার্ড সি ++ এর প্রয়োগযোগ্যতার মিষ্টি স্পটের বাইরে চলে গিয়েছেন))


6

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


5

অন্যান্য উত্তরগুলিতে যুক্ত করতে, এটি কমপক্ষে কিছুটা হলেও পারফরম্যান্স সম্পর্কেও হতে পারে। এটি আপনার জন্য প্রাসঙ্গিক না হলে আপনার এই সম্পর্কে উদ্বিগ্ন হওয়া উচিত নয় তবে:

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

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


হিপ এবং স্ট্যাক উভয়ই পৃষ্ঠাযুক্ত ভার্চুয়াল মেমরি। নতুন স্মৃতিতে মানচিত্রের তুলনায় গাদা অনুসন্ধানের সময়টি তুলনামূলকভাবে দ্রুত। 32 বিট লিনাক্সের অধীনে, আমি আমার স্ট্যাকের উপর> 2 জিগ লাগাতে পারি। ম্যাকসের অধীনে, আমি মনে করি স্ট্যাকটি 65 মেগের মধ্যে সীমাবদ্ধ।
মিঃআরআই

3

স্ট্যাকটি আরও দক্ষ এবং পরিচালনা করা স্কোপড ডেটাতে সহজ।

তবে কয়েকটি কেবি-র চেয়ে বড় যে কোনও কিছুর জন্য গাদা ব্যবহার করা উচিত (এটি সি ++ তে সহজ, কেবল একটি তৈরি করুনboost::scoped_ptr তে বরাদ্দ মেমোরিটির পয়েন্টার ধরে রাখতে কেবল স্ট্যাকের উপর করুন)।

পুনরাবৃত্তির একটি অ্যালগরিদম বিবেচনা করুন যা নিজেকে কল করে রাখে। মোট স্ট্যাকের ব্যবহার সীমাবদ্ধ করা বা অনুমান করা খুব কঠিন! যেখানে গাদা, বরাদ্দকারী ( malloc()বা new) স্মরণ আউট অফ মেমরি ফিরে NULLবা মাধ্যমে নির্দেশ করতে পারেthrow আইএনএস ।

উত্স : লিনাক্স কার্নেল যার স্ট্যাক 8KB এর চেয়ে বড় নয়!


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


1

আপনার ভেরিয়েবলটি কীভাবে বরাদ্দ করা হয়েছে তার উপর নির্ভর করে গাদা বা স্ট্যাকের জন্য বরাদ্দ করার সিদ্ধান্তটি আপনার জন্য তৈরি করা হয়। যদি আপনি "নতুন" কল ব্যবহার করে গতিশীল কিছু বরাদ্দ করেন তবে আপনি গাদা থেকে বরাদ্দ দিচ্ছেন। আপনি যদি কোনও বৈশ্বিক পরিবর্তনশীল হিসাবে বা কোনও ফাংশনে একটি পরামিতি হিসাবে কিছু বরাদ্দ করেন তবে এটি স্ট্যাকের জন্য বরাদ্দ করা হয়।


4
আমার সন্দেহ হয় তিনি কখন জিজ্ঞাসা করছিলেন কখন কীভাবে জিনিসগুলি স্তূপে রাখা হয়, কীভাবে নয়।
স্টিভ রোয়ে

0

আমার মতে দুটি সিদ্ধান্ত নেওয়া কারণ রয়েছে

1) Scope of variable
2) Performance.

আমি বেশিরভাগ ক্ষেত্রে স্ট্যাক ব্যবহার করতে পছন্দ করব তবে আপনার যদি বহিরাগত সুযোগের পরিবর্তনশীল অ্যাক্সেসের প্রয়োজন হয় তবে আপনি গাদা ব্যবহার করতে পারেন।

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


0

সম্ভবত এটি বেশ ভাল উত্তর দেওয়া হয়েছে। নিম্ন স্তরের বিশদ সম্পর্কে গভীর ধারণা পেতে আমি আপনাকে নিচের সিরিজের নিবন্ধগুলিতে নির্দেশ করতে চাই। অ্যালেক্স ডার্বির একটি ধারাবাহিক নিবন্ধ রয়েছে, যেখানে তিনি আপনাকে একটি ডিবাগারের সাহায্যে নিয়ে যান। স্ট্যাক সম্পর্কে এখানে পার্ট 3। http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/


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