সি তে, ধনুর্বন্ধক একটি স্ট্যাক ফ্রেম হিসাবে কাজ করে?


153

যদি আমি কোঁকড়া ধনুর্বন্ধনী একটি নতুন সেট মধ্যে একটি পরিবর্তনশীল তৈরি, যে পরিবর্তনশীল বন্ধনী বন্ধনী উপরের স্ট্যাক বন্ধ পপ, বা এটি ফাংশন শেষ না হওয়া পর্যন্ত স্থির হয়? উদাহরণ স্বরূপ:

void foo() {
   int c[100];
   {
       int d[200];
   }
   //code that takes a while
   return;
}

হবে dসময় মেমরি গ্রহণ হতে code that takes a whileঅধ্যায়?


8
আপনি কি (1) মান অনুযায়ী (2) বাস্তবায়নের মধ্যে সর্বজনীন অনুশীলন, বা (3) বাস্তবায়নের মধ্যে সাধারণ অনুশীলন বলতে চান?
ডেভিড থর্নলি

উত্তর:


83

না, ধনুর্বন্ধনী স্ট্যাক ফ্রেম হিসাবে কাজ করে না। সি-তে, ব্রেসগুলি কেবল একটি নামকরণের স্কোপকে বোঝায়, তবে নিয়ন্ত্রণটি শেষ হয়ে গেলে কোনও কিছুই ধ্বংস হয় না এবং স্ট্যাকের বাইরেও কিছু যায় না।

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

এছাড়াও নোট করুন যে স্থানীয় ভেরিয়েবলগুলি কোনও স্ট্যাক স্পেস ব্যবহার করতে পারে না: সেগুলি সিপিইউ রেজিস্টারগুলিতে বা অন্য কোনও সহায়ক স্টোরেজ স্থানে রাখা হতে পারে বা পুরোপুরি অপ্টিমাইজ করা যেতে পারে।

সুতরাং, dঅ্যারে, তাত্ত্বিকভাবে, পুরো ফাংশনের জন্য মেমরি গ্রাস করতে পারে। যাইহোক, সংকলক এটিকে অপ্টিমাইজ করতে পারে, বা অন্যান্য স্থানীয় ভেরিয়েবলগুলির সাথে এর মেমরি ভাগ করে নিতে পারে যার ব্যবহারের সময়কাল ওভারপাল হয় না।


9
তা কি বাস্তবায়ন-নির্দিষ্ট নয়?
অবাকর

54
সি ++ এ, কোনও অবজেক্টের ডেস্ট্রাক্টর তার সুযোগের শেষে ডেকে আনে। মেমরিটি পুনরায় দাবি করা হয় কিনা তা বাস্তবায়ন-নির্দিষ্ট সমস্যা।
ক্রিস্টোফার জনসন

8
@ pm100: ধ্বংসকারীদের ডাকা হবে। এটি সেই বস্তুগুলি যে দখল করেছে সেই স্মৃতি সম্পর্কে কিছুই বলে না।
ডোনাল ফেলো 3'10

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

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

39

চলকটি আসলে স্মৃতি গ্রহণ করার সময়টি স্পষ্টতই সংকলক-নির্ভর (এবং অনেক সংকলক অভ্যন্তরীণ ব্লকগুলি প্রবেশ করে ফাংশনগুলির মধ্যে প্রস্থান করা হলে স্ট্যাক পয়েন্টারটি সামঞ্জস্য করে না)।

যাইহোক, একটি ঘনিষ্ঠভাবে সম্পর্কিত তবে সম্ভবত আরও আকর্ষণীয় প্রশ্ন হ'ল যে প্রোগ্রামটিকে অভ্যন্তরীণ সুযোগের বাইরে (তবে ধারণকৃত ফাংশনটির মধ্যে) that অভ্যন্তরীণ বস্তুটি অ্যাক্সেস করার অনুমতি দেওয়া হয়েছে, অর্থাত:

void foo() {
   int c[100];
   int *p;

   {
       int d[200];
       p = d;
   }

   /* Can I access p[0] here? */

   return;
}

(অন্য কথায়: কম্পাইলার হয় অনুমতি ডি-এলোকেট করতে d, এমনকি যদি বাস্তবে সবচেয়ে না?)।

এর জবাব হলো, কম্পাইলার হয় হয় ডি-এলোকেট করার অনুমতি দেওয়া d, এবং অ্যাক্সেস p[0]যাতে মন্তব্যটি ইঙ্গিত অনির্ধারিত আচরণ (প্রোগ্রাম হয় না অ্যাক্সেস করতে ভেতরের পরিধি ভেতরের বস্তুর বাহিরে অনুমতি দেওয়া হয়েছে)। সি স্ট্যান্ডার্ডের প্রাসঙ্গিক অংশটি 6.2.4p5:

এই জাতীয় কোনও অবজেক্টের জন্য [যার স্বয়ংক্রিয় স্টোরেজ সময়কাল রয়েছে] যার কোনও পরিবর্তনশীল দৈর্ঘ্যের অ্যারে প্রকার নেই, তার জীবনকাল ব্লকের প্রবেশের সময় থেকে প্রসারিত হয় যার সাথে এটি সম্পর্কিত হয় যতক্ষণ না block ব্লকের কার্য সম্পাদন কোনওভাবে শেষ হয় । (একটি বদ্ধ ব্লক প্রবেশ করা বা একটি ফাংশন কল করা স্থগিত হয়ে যায়, তবে শেষ হয় না, বর্তমান ব্লকের সম্পাদন। অবজেক্টের প্রাথমিক মান অনির্দিষ্ট। যদি অবজেক্টটির জন্য একটি সূচনা নির্দিষ্ট করা থাকে তবে এটি প্রতিবার ব্লকের সম্পাদনে ঘোষণাপত্রটি সম্পাদিত হয়; অন্যথায়, প্রতিবার ঘোষণার সময় মানটি অনির্দিষ্ট হয়ে যায়।


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

20

আপনার প্রশ্নের দ্ব্যর্থহীনভাবে উত্তর দেওয়া যথেষ্ট পরিষ্কার নয়।

একদিকে, সংকলকরা সাধারণত নেস্টেড ব্লক স্কোপের জন্য কোনও স্থানীয় মেমরি বরাদ্দ-বিলোপ না করে। স্থানীয় মেমোরি সাধারণত ফাংশন এন্ট্রি এ একবার বরাদ্দ করা হয় এবং ফাংশন প্রস্থান এ মুক্তি দেওয়া হয়।

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

void foo()
{
  {
    int d[100];
  }
  {
    double e[20];
  }
}

উভয় অ্যারে সাধারণত একই মেমরি অঞ্চল দখল করবে, যার অর্থ fooহল যে ফাংশন দ্বারা প্রয়োজনীয় স্থানীয় সঞ্চয়স্থানের মোট পরিমাণটি দুটি অ্যারের বৃহত্তম জন্য প্রয়োজনীয় যা তা একই সাথে উভয়ের জন্য নয়।

পরবর্তী dপ্রশ্নটি আপনার প্রশ্নের প্রসঙ্গে ফাংশন শেষ হওয়া অবধি স্মৃতি দখল অব্যাহত রাখার যোগ্যতা অর্জন করে কিনা তা আপনার সিদ্ধান্তের জন্য।


6

এটি বাস্তবায়ন নির্ভর। আমি জিসিসি ৪.৩.৪ কী করে তা পরীক্ষা করার জন্য একটি ছোট প্রোগ্রাম লিখেছিলাম এবং এটি ফাংশনের শুরুতে স্ট্যাকের সমস্ত জায়গার বরাদ্দ করে। আপনি জিএসসি -S পতাকা ব্যবহার করে যে সমাবেশটি তৈরি করে তা পরীক্ষা করতে পারেন।


3

না, ডি [] রুটিনের বাকী অংশের জন্য স্ট্যাকের মধ্যে থাকবে না । তবে বরাদ্দ () আলাদা।

সম্পাদনা করুন: ক্রিস্টোফার জনসন (এবং সাইমন এবং ড্যানিয়েল) ঠিক বলেছেন , এবং আমার প্রাথমিক প্রতিক্রিয়াটি ভুল ছিল । জিসিসি ৪.৩.৪.২ সাথে সিওয়াইগুইন, কোড:

void foo(int[]);
void bar(void);
void foobar(int); 

void foobar(int flag) {
    if (flag) {
        int big[100000000];
        foo(big);
    }
    bar();
}

দেয়:

_foobar:
    pushl   %ebp
    movl    %esp, %ebp
    movl    $400000008, %eax
    call    __alloca
    cmpl    $0, 8(%ebp)
    je      L2
    leal    -400000000(%ebp), %eax
    movl    %eax, (%esp)
    call    _foo
L2:
    call    _bar
    leave
    ret

শিখুন এবং বাচুন! এবং একটি দ্রুত পরীক্ষা দেখা যাচ্ছে যে অ্যান্ড্রেটি একাধিক বরাদ্দ সম্পর্কেও সঠিক।

অনেক পরে যুক্ত করা হয়েছে : উপরের পরীক্ষায় দেখা যাচ্ছে যে সিসি ডকুমেন্টেশন একেবারেই সঠিক নয়। বছরের পর বছর ধরে এটি বলেছে (জোর দেওয়া হয়েছে):

"একটি পরিবর্তনশীল দৈর্ঘ্যের অ্যারের জন্য স্থান হয় deallocated অ্যারের নাম যত তাড়াতাড়ি সুযোগ প্রান্ত ।"


অপ্টিমাইজেশান অক্ষম সহ সংকলন অপরিহার্য কোড আপনি কী পাবেন তা আপনাকে অবশ্যই দেখাবে না। এই ক্ষেত্রে, আচরণটি একই রকম (ফাংশনের শুরুতে বরাদ্দ করুন, এবং ফাংশনটি ছাড়ার সময় কেবল বিনামূল্যে): Godbolt.org/g/M112AQ । তবে নন-সাইগউইন জিসিসি কোনও allocaফাংশন কল করে না । আমি সত্যিই অবাক হয়েছি যে সাইগউইন জিসিসি এটি করবে। এটি এমনকি কোনও ভেরিয়েবল-দৈর্ঘ্যের অ্যারে নয়, তাই আপনি কেন এনেছেন তা IDK।
পিটার কর্ডেস

2

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


1

আপনার ভেরিয়েবলটি dসাধারণত স্ট্যাক থেকে পপড হয় না। কোঁকড়া ধনুর্বন্ধনী বুনন একটি স্ট্যাক ফ্রেম বোঝায় না। অন্যথায়, আপনি এই জাতীয় কিছু করতে সক্ষম হবেন না:

char var = getch();
    {
        char next_var = var + 1;
        use_variable(next_char);
    }

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

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

আপডেট: সি স্পিকে কী বলতে হবে তা এখানে । স্বয়ংক্রিয় সঞ্চয়ের সময়কাল সহ বিভাগগুলি সম্পর্কে (বিভাগ 6.4.2):

যে কোনও অবজেক্টে ভেরিয়েবলের দৈর্ঘ্যের অ্যারে টাইপ নেই, তার ব্লকটিতে প্রবেশের সময় থেকে তার জীবনকাল প্রসারিত হয় যার সাথে এটি যুক্ত থাকে যতক্ষণ না of ব্লকের কার্য সম্পাদন কোনওভাবে শেষ না হয়।

একই বিভাগটি "আজীবন" শব্দটিকে (জোর দেওয়া খনি) হিসাবে সংজ্ঞায়িত করে:

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

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

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

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


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

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

0

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


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

0

ইতিমধ্যে মান সম্পর্কে অনেক তথ্য দেওয়া হয়েছে যা নির্দেশ করে যে এটি প্রকৃতপক্ষে বাস্তবায়ন নির্দিষ্ট।

সুতরাং, একটি পরীক্ষা আগ্রহী হতে পারে। যদি আমরা নিম্নলিখিত কোডটি চেষ্টা করি:

#include <stdio.h>
int main() {
    int* x;
    int* y;
    {
        int a;
        x = &a;
        printf("%p\n", (void*) x);
    }
    {
        int b;
        y = &b;
        printf("%p\n", (void*) y);
    }
}

জিসিসি ব্যবহার করে আমরা এখানে দু'বার একই ঠিকানা পেয়েছি : কলিরো

তবে আমরা যদি নিম্নলিখিত কোডটি চেষ্টা করি:

#include <stdio.h>
int main() {
    int* x;
    int* y;
    {
        int a;
        x = &a;
    }
    {
        int b;
        y = &b;
    }
    printf("%p\n", (void*) x);
    printf("%p\n", (void*) y);
}

জিসিসি ব্যবহার করে আমরা এখানে দুটি পৃথক ঠিকানা পাই : কলিরো

সুতরাং, আপনি কী নিশ্চিত তা নিশ্চিত হতে পারবেন না।

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