উত্স কোডের শেষে সংজ্ঞাটি লেখা হয়, তখন সি ভাষায় ডেটা এবং ফাংশনগুলির * ঘোষণা * কেন প্রয়োজনীয়?


15

নিম্নলিখিত "সি" কোডটি বিবেচনা করুন:

#include<stdio.h>
main()
{   
  printf("func:%d",Func_i());   
}

Func_i()
{
  int i=3;
  return i;
}

Func_i()উত্স কোডের শেষে সংজ্ঞায়িত করা হয়েছে এবং এটির ব্যবহারের আগে কোনও ঘোষণা সরবরাহ করা হয় না main()। খুব সময় যখন কম্পাইলার সূচিত এ Func_i()মধ্যে main(), তা থেকে বেরিয়ে আসে main()এবং আউট খুঁজে বের করে Func_i()। কম্পাইলার একরকম দ্বারা ফিরে মান খুঁজে বের করে Func_i()এবং তা দেয় printf()। আমি আরও জানতে পারি যে কম্পাইলার খুঁজে পাচ্ছি না রিটার্ন টাইপ এর Func_i()। এটা করে ডিফল্ট (অনুমান?) লাগে রিটার্ন টাইপ এর Func_i()হতে int। যদি কোডটি থাকে float Func_i()তবে সংকলকটি ত্রুটি দেয়: এর জন্য দ্বন্দ্বমূলক প্রকারFunc_i()

উপরের আলোচনা থেকে আমরা দেখতে পাই যে:

  1. সংকলক দ্বারা প্রত্যাশিত মানটি খুঁজে পেতে পারে Func_i()

    • যদি সংকলকটি Func_i()বেরিয়ে এসে main()উত্স কোডটি অনুসন্ধান করে ফিরে পাওয়া মানটি খুঁজে পেতে পারে , তবে কেন এটি স্পষ্টভাবে উল্লিখিত ফানক_আই () এর ধরণটি খুঁজে পাবে না ।
  2. সংকলকটি অবশ্যই জেনে রাখতে হবে যে Func_i()এটি টাইপ ফ্লোট - এ কারণেই এটি বিরোধী প্রকারের ত্রুটি দেয়।

  • যদি সংকলক জানে যে Func_iএটি টাইপ ফ্লোটের, তবে কেন এটি এখনও Func_i()টাইপ ইন্টের মতো বলে ধরে নেওয়া হয় এবং বিবাদী প্রকারের ত্রুটি দেয়? কেন এটি জোর করে Func_i()প্রকারের ভাসমান হতে পারে না।

আমি পরিবর্তনশীল ঘোষণার সাথে একই সন্দেহ করেছি । নিম্নলিখিত "সি" কোডটি বিবেচনা করুন:

#include<stdio.h>
main()
{
  /* [extern int Data_i;]--omitted the declaration */
  printf("func:%d and Var:%d",Func_i(),Data_i);
}

 Func_i()
{
  int i=3;
  return i;
}
int Data_i=4;

সংকলক ত্রুটি দেয়: 'ডেটা_আই' অঘোষিত (এই ফাংশনে প্রথম ব্যবহার)।

  • যখন কম্পাইলার দেখেন Func_i(), এটা () মান Func_ দ্বারা ফিরে এটি সোর্স কোড যায় নিচে। সংকলক কেন ডেটা ভেরিয়েবলের জন্য একই কাজ করতে পারে না?

সম্পাদনা:

সংকলক, এসেম্বলার, প্রসেসর ইত্যাদির অভ্যন্তরীণ কাজের বিবরণ আমার জানা নেই আমার প্রশ্নের প্রাথমিক ধারণাটি হ'ল আমি যদি ব্যবহারের পরে সোর্স কোডটিতে ফাংশনের রিটার্ন-মানটি লিখি (লিখি) তবে সেই ফাংশনটির পরে "সি" ভাষা কম্পিউটারকে কোনও ত্রুটি না করেই সেই মানটি খুঁজে পেতে দেয়। এখন কেন কম্পিউটার একই ধরণের সন্ধান করতে পারে না। Func_i () এর রিটার্ন মান পাওয়া যাওয়ায় ডেটা_আইয়ের ধরণটি কেন খুঁজে পাওয়া যাবে না। এমনকি যদি আমি extern data-type identifier;বিবৃতিটি ব্যবহার করি তবে আমি সেই শনাক্তকারী (ফাংশন / ভেরিয়েবল) দ্বারা ফিরিয়ে দেওয়া মানটি বলছি না। কম্পিউটার যদি সেই মানটি খুঁজে পেতে পারে তবে কেন এটি টাইপটি খুঁজে পাচ্ছে না। কেন আমাদের মোটামুটি ফরওয়ার্ড ডিক্লেয়ারেশন দরকার?

ধন্যবাদ.


7
সংকলক Func_i দ্বারা ফিরে আসা মানটি "সন্ধান" করে না। এটি কার্যকর করার সময় করা হয়।
জেমস ম্যাকলিউড

26
আমি ডাউনটা করি নি, তবে প্রশ্নটি কম্পাইলাররা কীভাবে কাজ করে তার কয়েকটি গুরুতর ভুল বোঝাবুঝির ভিত্তিতে এবং মন্তব্যগুলিতে আপনার প্রতিক্রিয়াগুলি বোঝায় যে আপনার এখনও অতিক্রম করার কিছু ধারণাগত বাধা রয়েছে।
জেমস ম্যাকলিউড

4
নোট করুন যে প্রথম নমুনা কোডটি গত পনের বছর ধরে মান-অনুসারে কোডটি বৈধ নয়; C99 ফাংশন সংজ্ঞা এবং অবৈধের অন্তর্নিহিত ঘোষণায় কোনও রিটার্নের ধরণের অনুপস্থিতি তৈরি করে Func_i। সুস্পষ্টভাবে অপরিজ্ঞাত ভেরিয়েবলগুলি ঘোষণার নিয়ম ছিল না, তাই দ্বিতীয় খণ্ডটি সর্বদা ত্রুটিযুক্ত ছিল। (হ্যাঁ, সংকলকগণ প্রথম নমুনাটি এখনও গ্রহণ করেন কারণ এটি বৈধ ছিল, যদিও opালু, C89 / C90 এর অধীনে))
জোনাথন লেফলার

19
@ ব্যবহারকারী31782: নীচের প্রশ্নের সাথে প্রশ্ন: X ভাষাটি কেন Y করবে / প্রয়োজন? কারণ এটি ডিজাইনারদের পছন্দ। আপনি তর্ক করছেন বলে মনে হচ্ছে যে সবচেয়ে সফল ভাষার একটির ডিজাইনাররা যে পছন্দগুলি সেগুলির প্রেক্ষাপটে তৈরি করা হয়েছে সেগুলি বোঝার চেষ্টা না করে দশক আগে তার আগে বিভিন্ন পছন্দ করা উচিত ছিল। আপনার প্রশ্নের উত্তর: কেন আমাদের এগিয়ে ঘোষণা দরকার? দেওয়া হয়েছে: কারণ সি একটি পাসের সংকলক ব্যবহার করে। আপনার বেশিরভাগ ফলোআপ প্রশ্নের সহজ উত্তর হ'ল কারণ এটি তখন কোনও পাসের সংকলক হবে না।
মিঃমিন্দর

4
@ ইউজার ৩82 You৮২ আপনি কম্পাইলার এবং প্রসেসরগুলি আসলে কীভাবে কাজ করে তার বোঝার জন্য আপনি সত্যই ড্রাগনের বইটি পড়তে চান - সমস্ত প্রয়োজনীয় জ্ঞানকে একটি একক উত্তরের (অথবা এটিতেও 100) ছড়িয়ে দেওয়া অসম্ভব। সংকলকগুলির আগ্রহ সহ যে কারও জন্য দুর্দান্ত বই।
ভু

উত্তর:


26

কারণ সি একটি একক-পাস , স্ট্যাটিকালি-টাইপড , দুর্বল টাইপযুক্ত , সংকলিত ভাষা।

  1. সিঙ্গল-পাসের অর্থ কম্পাইলার কোনও ফাংশন বা ভেরিয়েবলের সংজ্ঞা দেখতে অপেক্ষা করে না। যেহেতু সংকলকটি সামনের দিকে তাকাবে না, সুতরাং কোনও ফাংশন ঘোষণার আগে অবশ্যই ফাংশনটি ব্যবহারের আগে উপস্থিত হওয়া উচিত, অন্যথায় সংকলকটি জানেন না যে এর ধরণের স্বাক্ষর কী। যাইহোক, ফাংশনটির সংজ্ঞাটি একই ফাইলের পরে বা সম্পূর্ণ আলাদাভাবে একটি পৃথক ফাইলেও হতে পারে। পয়েন্ট # 4 দেখুন।

    একমাত্র ব্যতিক্রম historicalতিহাসিক নিদর্শন যা অঘোষিত ফাংশন এবং ভেরিয়েবলগুলি "ইনট" টাইপের বলে মনে করা হয়। আধুনিক অনুশীলন হ'ল সর্বদা ফাংশন এবং ভেরিয়েবলগুলি স্পষ্টভাবে ঘোষণা করে অন্তর্নিহিত টাইপিং এড়ানো।

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

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

  4. সংকলিত মানে হ'ল মানব-পঠনযোগ্য উত্স কোড বিশ্লেষণ এবং এটিকে মেশিন-পঠনযোগ্য নির্দেশিকায় রূপান্তর করার প্রক্রিয়াটি প্রোগ্রামটি চালুর আগে পুরোপুরি সম্পন্ন হয়। সংকলক যখন একটি ফাংশন সংকলন করছে, তখন কোনও প্রদত্ত উত্স ফাইলে এটি আরও কীসের মুখোমুখি হবে সে সম্পর্কে কোনও জ্ঞান নেই। তবে, একবার সংকলন (এবং সমাবেশ, লিঙ্কিং, ইত্যাদি) শেষ হয়ে গেলে, সমাপ্ত এক্সিকিউটেবলের প্রতিটি ফাংশনটিতে সঞ্চালনের সময় কল করবে এমন ফাংশনগুলিতে সংখ্যাসূচক পয়েন্টার রয়েছে। এজন্য মূল () উত্স ফাইলে আরও একটি ফাংশন কল করতে পারে। মূল () আসলে চলার সময়টিতে এতে ফানক_আই () এর ঠিকানার একটি পয়েন্টার থাকবে।

    মেশিন কোড খুব, খুব নির্দিষ্ট। দুটি পূর্ণসংখ্যা (3 + 2) যুক্ত করার কোড দুটি ভাসমান (3.0 + 2.0) যুক্ত করার জন্য একের থেকে আলাদা। এগুলি উভয়ই একটি ভাসা (3 + 2.0) এর সাথে যুক্ত করা থেকে আলাদা different সংকলক একটি ফাংশনের প্রতিটি পয়েন্টের জন্য নির্ধারণ করে যে সেই মুহুর্তে সঠিক অপারেশনটি কী করা দরকার এবং সেই কোডটি উত্পন্ন করে যা সেই সঠিক ক্রিয়াকলাপটি সম্পাদন করে। এটি হয়ে গেলে, ফাংশনটি পুনরায় সংশোধন না করে এটি পরিবর্তন করা যায় না।

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

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

আমি অবশ্যই বেশিরভাগ বিব্রতকর বিবরণ রেখে গেছি। আসল প্রক্রিয়া অনেক বেশি জটিল। আমি আশা করি যে আপনার প্রশ্নের উত্তর দেওয়ার জন্য আমি যথেষ্ট উচ্চ-স্তরের ওভারভিউ সরবরাহ করেছি।

অতিরিক্তভাবে, দয়া করে মনে রাখবেন, আমি উপরে বিশেষভাবে যা লিখেছি তা সি এর জন্য প্রযোজ্য

অন্যান্য ভাষায়, সংকলকটি উত্স কোডের মাধ্যমে একাধিক পাস করতে পারে এবং তাই সংকলক পূর্বনির্ধারিত না হয়ে Func_i () এর সংজ্ঞা গ্রহণ করতে পারে।

অন্যান্য ভাষায়, ফাংশন এবং / বা ভেরিয়েবলগুলি গতিশীলভাবে টাইপ করা যেতে পারে, সুতরাং একটি একক ভেরিয়েবল ধরে রাখতে পারে, বা একটি একক ফাংশনটি পাস করা বা ফিরে আসতে পারে, একটি পূর্ণসংখ্যা, একটি ভাসা, একটি স্ট্রিং, একটি অ্যারে, বা একটি অবজেক্ট বিভিন্ন সময়ে।

অন্য ভাষায়, টাইপিং আরও শক্ত হতে পারে, ভাসমান-পয়েন্ট থেকে পূর্ণসংখ্যায় স্পষ্টভাবে নির্দিষ্ট করার জন্য রূপান্তর প্রয়োজন requ এখনও অন্য ভাষায়, টাইপিং দুর্বল হতে পারে, স্ট্রিং "3.0" থেকে "ফ্লোট 3.0"-তে রূপান্তর 3 এর সাথে স্বয়ংক্রিয়ভাবে চালিত হতে পারে।

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


1
সর্বকোষিক উত্তরের জন্য আপনাকে ধন্যবাদ। আপনার এবং নিকির উত্তরটি আমি জানতে চেয়েছিলাম। যেমন Func_()+1: এখানে সংকলন সময়ে কম্পাইলার আছে ধরণ জানতে Func_i()তাই হিসাবে উপযুক্ত মেশিন কোড তৈরি করতে। সম্ভবত পারেন তাই নয় কি সম্ভব হ্যান্ডেল করতে সমাবেশ জন্য Func_()+1রান সময়ে টাইপ কল করে, অথবা এটি সম্ভব কিন্তু এমনটি রান সময়ে ধীর প্রোগ্রাম করতে হবে। আমি মনে করি, এটি আপাতত আমার পক্ষে যথেষ্ট।
ব্যবহারকারী 106313

1
সি এর সুস্পষ্টভাবে ঘোষিত ক্রিয়াকলাপগুলির গুরুত্বপূর্ণ বিশদ: এগুলি ধরণের বলে ধরে নেওয়া হয় int func(...)... অর্থাৎ তারা একটি ভিন্নতর আর্গুমেন্টের তালিকা গ্রহণ করে। এর অর্থ যদি আপনি কোনও ফাংশনটিকে এটি হিসাবে সংজ্ঞায়িত করেন int putc(char)তবে এটি ঘোষণা করতে ভুলে যান, তবে পরিবর্তে এটি হিসাবে ডাকা হবে int putc(int)(কারণ চরিত্রটি একটি বৈকল্পিক যুক্তি তালিকার মধ্য দিয়ে গেছে বলে প্রচারিত হয় int)। সুতরাং ওপির উদাহরণটি কাজ করতে গিয়েছিল কারণ এর স্বাক্ষরটি অন্তর্নিহিত ঘোষণার সাথে মিলেছে, তবে এই আচরণটি কেন নিরুৎসাহিত করা হয়েছে তা বোধগম্য (এবং উপযুক্ত সতর্কতা যুক্ত করা হয়েছে)।
uliwitness

37

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

int Func_i();

সংকলকটিকে সহায়তা করতে শীর্ষে বা একটি শিরোনাম ফাইলে।

আপনার উদাহরণগুলিতে, আপনি সি ভাষার দুটি সন্দেহজনক বৈশিষ্ট্য ব্যবহার করেন যা এড়ানো উচিত:

  1. যদি কোনও ফাংশনটি সঠিকভাবে ঘোষণার আগে ব্যবহার করা হয় তবে এটি একটি "অন্তর্নিহিত ঘোষণা" হিসাবে ব্যবহৃত হয়। সংকলকটি ফাংশনের স্বাক্ষরটি বের করার জন্য তাত্ক্ষণিক প্রসঙ্গটি ব্যবহার করে। সংকলক আসল ঘোষণাটি কী তা নির্ধারণ করার জন্য বাকী কোডের মাধ্যমে স্ক্যান করবে না।

  2. কোনও প্রকার ব্যতীত যদি কিছু ঘোষণা করা হয় তবে টাইপটি তা গ্রহণ করা হবে int। এটি যেমন স্ট্যাটিক ভেরিয়েবল বা ফাংশন রিটার্ন টাইপের ক্ষেত্রে।

সুতরাং printf("func:%d",Func_i()), আমরা একটি অন্তর্নিহিত ঘোষণা আছে int Func_i()। সংকলক যখন ফাংশন সংজ্ঞাতে পৌঁছে যায় তখন এটি Func_i() { ... }ধরণের সাথে সামঞ্জস্য হয়। তবে আপনি যদি float Func_i() { ... }এই মুহুর্তে লিখে থাকেন তবে আপনার অনর্থক ঘোষিত হয়েছে int Func_i()এবং স্পষ্টভাবে ঘোষণা করা হয়েছে float Func_i()। যেহেতু দুটি ঘোষণা মেলে না, সংকলকটি আপনাকে একটি ত্রুটি দেয়।

কিছু ভুল ধারণা মুছে ফেলা হচ্ছে

  • সংকলক দ্বারা প্রদত্ত মানটি খুঁজে পায় না Func_i। সুস্পষ্ট ধরণের অনুপস্থিতির অর্থ ফেরত প্রকারটি intপূর্বনির্ধারিত। এমনকি যদি আপনি এটি করেন:

    Func_i() {
        float f = 42.3;
        return f;
    }

    তারপরে টাইপটি হবে int Func_i()এবং ফেরতের মানটি নিঃশব্দে কেটে যাবে!

  • সংকলকটি শেষ পর্যন্ত প্রকৃত প্রকারটি Func_iজানতে পারে তবে অন্তর্নিহিত ঘোষণার সময় এটি প্রকৃত প্রকারটি জানে না। পরে যখন এটি আসল ঘোষণায় পৌঁছায় কেবল তখনই এটি সনাক্ত করতে পারে যে স্পষ্টভাবে ঘোষিত প্রকারটি সঠিক ছিল কি না। তবে সেই মুহুর্তে, ফাংশন কলের জন্য সমাবেশটি ইতিমধ্যে লেখা হয়ে থাকতে পারে এবং সি সংকলনের মডেলটিতে পরিবর্তন করা যাবে না।


3
@ ব্যবহারকারী31782: কোডের ক্রম সংকলন সময়ে গুরুত্বপূর্ণ, তবে রান সময় নয়। প্রোগ্রামটি চলাকালীন সংকলকটি চিত্রের বাইরে। রান সময় দ্বারা, ফাংশনটি একত্রিত হয়ে লিঙ্ক করা হবে, এর ঠিকানাটি সমাধান হয়ে যাবে এবং কলটির ঠিকানা স্থানধারীর মধ্যে আটকে থাকবে। (এটি এর চেয়ে কিছুটা জটিল, তবে এটি প্রাথমিক ধারণা)) প্রসেসরটি সামনে বা পিছনে শাখা করতে পারে।
blrfl

20
@ ব্যবহারকারী31782: সংকলক মানটি মুদ্রণ করে না । আপনার সংকলক প্রোগ্রামটি চালায় না !!
মনিকার সাথে লাইটনেস রেস

1
পছন্দ করুন উপরের মন্তব্যে আমি ভুল করে সংকলক লিখেছিলাম কারণ আমি নাম প্রসেসরটি ভুলে গিয়েছি ।
ব্যবহারকারী 106313

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

2
@ ব্যবহারকারী31782: প্রসেসরের সঠিক সমাবেশ তৈরি করতে সংকলকটিকে অবশ্যই ভেরিয়েবলের প্রকারটি জানতে হবে। সংকলকটি অন্তর্ভুক্তটিকে সন্ধান করলে Func_i()এটি তাত্ক্ষণিকভাবে প্রসেসরের অন্য কোনও জায়গায় যাওয়ার জন্য কোডটি তৈরি করে এবং সংরক্ষণ করে, তারপরে কিছু পূর্ণসংখ্যা গ্রহণ করে এবং তারপরে চালিয়ে যায়। সংকলক পরে Func_iসংজ্ঞাটি সন্ধান করলে এটি স্বাক্ষরগুলির সাথে মেলে কিনা তা নিশ্চিত করে এবং যদি তারা তা করে তবে এটি Func_i()ঠিকানায় এই সমাবেশটি স্থাপন করে এবং কিছু পূর্ণসংখ্যা ফেরত দিতে বলে। আপনি যখন প্রোগ্রামটি চালান, প্রসেসর তারপরে মান সহ সেই নির্দেশাবলী অনুসরণ করে 3
23'14 এ 17-22 এ হাঁসকে মৃগিং করা হচ্ছে

10

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

দ্বিতীয়ত, এটি আপনার ভাবা হিসাবে কাজ করে না।

  1. ফলাফলের ধরণটি সি 90-এ alচ্ছিক, কোনওটি intফলাফল দেয় না। এটি ভেরিয়েবল ঘোষণার ক্ষেত্রেও সত্য (তবে আপনাকে স্টোরেজ ক্লাস দিতে হবে, staticবা extern)।

  2. Func_iপূর্ববর্তী ঘোষণা ছাড়াই ডাকাডাক্টিকটি দেখে সংকলক যা করেন তা ধরে নিচ্ছে যে কোনও ঘোষণা আছে

    extern int Func_i();

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

    নোট করুন যে ঘোষণায় খালি প্যারামিটার তালিকার অর্থ এই নয় যে ফাংশনটি প্যারামিটার নেয় না (এর (void)জন্য আপনাকে নির্দিষ্ট করতে হবে ), এর অর্থ এই নয় যে সংকলকটি পরামিতিগুলির প্রকারগুলি পরীক্ষা করতে হবে না এবং একই হবে অন্তর্নিহিত রূপান্তরগুলি যা আর্গুমেন্টগুলিতে প্রয়োগ করা হয় ভেরিয়াদিক কার্যগুলিতে প্রেরণ করা হয়।


সংকলক যদি মূল () থেকে বেরিয়ে এবং উত্স কোডটি সন্ধান করে ফানক_আই () দ্বারা প্রত্যাবর্তিত মানটি খুঁজে পেতে পারে তবে কেন এটি স্পষ্টভাবে উল্লিখিত ফানক_আই () এর ধরণটি খুঁজে পাবে না।
ব্যবহারকারী 106313

1
@ ব্যবহারকারীর ১ Fun82৮২ যদি ফানক_আই এর পূর্বের কোনও ঘোষণা না থাকে, যখন দেখছিলেন যে ফানসি_আইটি কোনও কল এক্সপ্রেশনে ব্যবহৃত হয়, এমন আচরণ করে যে সেখানে রয়েছে extern int Func_i()। এটি কোথাও দেখায় না।
এপ্রোগ্রামার

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

10
@ ইউজার 31782, প্রোগ্রামগুলি কীভাবে কাজ করে সে সম্পর্কে আপনার গুরুতর ভুল ধারণা রয়েছে। এত গুরুতর যে আমি মনে করি না যে p. সেগুলি সংশোধন করার জন্য ভাল জায়গা (সম্ভবত চ্যাট, তবে আমি এটি করার চেষ্টা করব না)।
এপ্রোগ্রামার

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

7

আপনি একটি মন্তব্যে লিখেছেন:

এক্সিকিউশন লাইন বাই লাইন করা হয়। Func_i () দ্বারা প্রত্যাবর্তিত মানটি খুঁজে পাওয়ার একমাত্র উপায় হ'ল মূল থেকে লাফানো

এটি একটি ভ্রান্ত ধারণা: ফাঁসির কাজটি লাইন বাই লাইন নয়। সংকলন লাইন দ্বারা লাইন করা হয়, এবং সংকলনের সময় নামের সমাধান করা হয় এবং এটি কেবল নামগুলি সমাধান করে, মানগুলি ফেরত দেয় না return

একটি সহায়ক ধারণামূলক মডেলটি হ'ল: সংকলক যখন লাইনটি পড়ে:

  printf("func:%d",Func_i());

এটি এর সমান কোড নির্গত করে:

  1. call "function #2" and put the return value on the stack
  2. put the constant string "func:%d" on the stack
  3. call "function #1"

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

পরে, যখন এটি পার্স করে:

 int Func_i() { ...

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

সুতরাং, Func_iসংকলকটি যখন প্রথম রেফারেন্সটিকে পার্স করেছিল তখন "চেহারা" দেয় নি । এটি কেবল কিছু টেবিলে একটি নোট তৈরি করেছিল, পরের লাইনে পার্স করার চেষ্টা করেছে। এবং ফাইলের শেষে, এটিতে একটি অবজেক্ট ফাইল এবং জাম্পের ঠিকানাগুলির একটি তালিকা রয়েছে।

পরে, লিঙ্কারটি এই সমস্ত গ্রহণ করে এবং সমস্ত পয়েন্টারগুলিকে "ফাংশন # 2" এ প্রকৃত জাম্পের ঠিকানা দিয়ে প্রতিস্থাপন করে, তাই এটি এরকম কিছু নির্গত করে:

  call 0x0001215 and put the result on the stack
  put constant ... on the stack
  call ...
...
[at offset 0x0001215 in the file, compiled result of Func_i]:
  put 3 on the stack
  return top of the stack

অনেক পরে, যখন এক্সিকিউটেবল ফাইলটি চালানো হয়, তখনই জাম্পের ঠিকানাটি সমাধান হয়ে যায় এবং কম্পিউটার 0x1215 ঠিকানার জন্য ঝাঁপিয়ে পড়ে। কোন নাম অনুসন্ধান প্রয়োজন।

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


আপনার উত্তর করার জন্য আপনাকে ধন্যবাদ। সংকলক কোডটি নির্গত করতে পারে না:1. call "function #2", put the return-type onto the stack and put the return value on the stack?
ব্যবহারকারী 106313

1
(। চলছে) এছাড়াও: কি যদি আপনি লিখেছেন printf(..., Func_i()+1);- কম্পাইলার আছে ধরণ জানতে Func_i, তাই এটি সিদ্ধান্ত নিতে পারেন যদি এটি একটি নির্গত উচিত add integerঅথবা একটি add floatনির্দেশ। আপনি কিছু বিশেষ কেস পেতে পারেন যেখানে সংকলক প্রকারের তথ্য ছাড়াই চলতে পারে তবে সংকলকটি সমস্ত ক্ষেত্রে কাজ করতে পারে।
নিকি

4
@ ব্যবহারকারী31782: মেশিনের নির্দেশাবলী, একটি নিয়ম হিসাবে খুব সহজ: দুটি 32-বিট পূর্ণসংখ্যার নিবন্ধ যুক্ত করুন। একটি 16-বিট পূর্ণসংখ্যার নিবন্ধটিতে একটি মেমরি ঠিকানা লোড করুন। একটি ঠিকানায় যান এছাড়াও, কোনও প্রকার নেই : আপনি আনন্দের সাথে একটি মেমরির অবস্থান লোড করতে পারেন যা একটি 32 বিট ফ্ল্যাট নম্বরকে 32 বিট পূর্ণসংখ্যার নিবন্ধে উপস্থাপন করতে পারে এবং এটির সাথে কিছু গাণিতিক করতে পারে। (এটি খুব কমই বোঝা যায়)) সুতরাং না, আপনি সরাসরি মেশিন কোডটি নির্গত করতে পারবেন না। আপনি একটি সংকলক লিখতে পারেন যা রানটাইম চেক এবং স্ট্যাকের অতিরিক্ত টাইপের ডেটা সহ সমস্ত কাজ করে। তবে এটি কোনও সি সংকলক হবে না।
নিকি

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

1
সমস্ত আলোচনা থেকে আমি এই বোঝাপড়ায় পৌঁছে গেছি: সংকলক Func_i()বা / এবং সহ যে কোনও বিবৃতি দেওয়ার জন্য একটি প্রশংসনীয় সমাবেশ কোড তৈরি করতে Data_i, এটি তাদের ধরণগুলি নির্ধারণ করতে হবে; ডেটা টাইপের ক্ষেত্রে কল করা সমাবেশের ভাষায় সম্ভব নয়। আশ্বাস পাওয়ার জন্য আমার নিজের কাছে বিশদগুলি অধ্যয়ন করা দরকার।
ব্যবহারকারী 106313

5

প্রসেসরের সময় এবং স্মৃতিশক্তি ব্যয়বহুল হয়ে ওঠে এমন এক যুগে সি এবং অন্যান্য কয়েকটি ভাষার জন্য যেগুলি ঘোষণাপত্রের দরকার হয় সেগুলি ডিজাইন করা হয়েছিল। সি এবং ইউনিক্স উন্নয়নে বেশ কিছু সময়ের জন্য হাতে হাত গিয়েছিলাম, এবং পরেরটির ভার্চুয়াল মেমরি ছিল না যতক্ষণ না 3BSD 1979 সালে হাজির কাজ অতিরিক্ত রুম ছাড়া, কম্পাইলার হতে সেদিকেই ঝুঁকেছে একক পাস কারণ তারা না বিষয়াবলি পুরো ফাইলটির কিছু উপস্থাপনা একবারে স্মৃতিতে রাখার সক্ষমতা প্রয়োজন।

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

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

তাহলে রিটার্ন টাইপের কী হবে?

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

প্রারম্ভিক সি এর আর একটি বৈশিষ্ট্য হ'ল প্রোটোটাইপগুলি হিসাবে আমরা জানি যে সেগুলি এখন নেই। আপনি কোনও ফাংশনের রিটার্নের ধরণ (যেমন, int foo()) ঘোষণা করতে পারেন , তবে এর আর্গুমেন্টগুলি নয় (অর্থাত্ int foo(int bar)কোনও বিকল্প ছিল না)। এটি বিদ্যমান ছিল কারণ উপরে বর্ণিত হিসাবে প্রোগ্রামটি সর্বদা একটি কলিং কনভেনশনে আটকে থাকে যা যুক্তি দ্বারা নির্ধারিত হতে পারে। যদি আপনি কোনও ফাংশনকে ভুল ধরণের যুক্তি দিয়ে ডাকেন তবে এটি ছিল আবর্জনা, আবর্জনা পরিস্থিতি situation

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

কোডের এই বিটগুলি বিবেচনা করুন:

double foo();         double foo();
double x;             int x;
x = foo();            x = foo();

বাম দিকের কোডটি একটি কলটিতে সংকলিত হয় foo()তারপরে কল / রিটার্ন কনভেনশনের মাধ্যমে সরবরাহিত ফলাফলটি যেখানেই xসঞ্চিত থাকে সেখানে অনুলিপি করে । এটা সহজ কেস।

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

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


আপনার উত্তর (+1) জন্য আপনাকে ধন্যবাদ। সংকলক, এসেম্বলার, প্রসেসর ইত্যাদির অভ্যন্তরীণ কাজের বিবরণ আমার জানা নেই আমার প্রশ্নের প্রাথমিক ধারণাটি হ'ল আমি যদি ব্যবহারের পরে সোর্স কোডটিতে ফাংশনের রিটার্ন-মানটি লিখি (লিখি) তবে সেই ফাংশনটির পরে ভাষাটি কোনও ত্রুটি না দিয়ে কম্পিউটারকে সেই মানটি খুঁজে পেতে দেয় । এখন কেন কম্পিউটার একই ধরণের সন্ধান করতে পারে না । করতে পারবেন না কেন টাইপ Data_i হিসাবে পাওয়া যাবে Func_i()'র ফেরত মান পাওয়া যায়নি।
ব্যবহারকারী 106313

আমি এখনও সন্তুষ্ট না। double foo(); int x; x = foo();কেবল ত্রুটি দেয়। আমি জানি যে আমরা এটি করতে পারি না। আমার প্রশ্নটি হ'ল ফাংশনটিতে প্রসেসর কেবল রিটার্ন-মান খুঁজে পায়; কেন এটি রিটার্ন-টাইপও খুব খুঁজে পাচ্ছে না?
ব্যবহারকারী 106313

1
@ ইউজার 31782: এটি করা উচিত নয়। এর জন্য একটি প্রোটোটাইপ রয়েছে foo(), তাই সংকলকটি কী করতে পারে তা জানে।
blrfl

2
@ user31782: প্রসেসরের রিটার্ন টাইপের কোনও ধারণা নেই।
blrfl

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