লুপের শেষ রানটি কী হওয়া উচিত, আপনি লিখেছেন array[10]
, তবে অ্যারেতে 10 টি উপাদান রয়েছে, 0 থেকে 9 পর্যন্ত সংখ্যাযুক্ত। সি ভাষার স্পেসিফিকেশন বলে যে এটি "অপরিজ্ঞাত আচরণ"। বাস্তবে এর অর্থ হ'ল আপনার প্রোগ্রামটি int
মেমরির সাথে সাথে থাকা মেমরির আকারের টুকরোটিতে লেখার চেষ্টা করবে array
। তারপরে যা ঘটে তা নির্ভর করে যা সেখানে রয়েছে, তার উপর নির্ভর করে এবং এটি কেবল অপারেটিং সিস্টেমের উপরই নির্ভর করে না তবে সংযোজক বিকল্পগুলির (যেমন অপ্টিমাইজেশন সেটিংসের মতো) প্রসেসরের আর্কিটেকচারের উপরের পার্শ্ববর্তী কোডের উপর নির্ভর করে ইত্যাদি। এটি মৃত্যুদন্ড কার্যকর করা থেকে মৃত্যুদন্ডের ক্ষেত্রেও পৃথক হতে পারে, যেমন অ্যাড্রেস স্পেস র্যান্ডমাইজেশনের কারণে (সম্ভবত এই খেলনাটির উদাহরণে নয়, তবে এটি বাস্তব জীবনে ঘটে)। কিছু সম্ভাবনার মধ্যে রয়েছে:
- অবস্থানটি ব্যবহার করা হয়নি। লুপটি সাধারণত শেষ হয়।
- অবস্থানটি এমন কোনও কিছুর জন্য ব্যবহৃত হয়েছিল যার মান 0 হয় The লুপটি সাধারণত শেষ হয়।
- অবস্থানটিতে ফাংশনের ফেরতের ঠিকানা রয়েছে। লুপটি সাধারণত শেষ হয় তবে প্রোগ্রামটি ক্র্যাশ হয়ে যায় কারণ এটি ঠিকানায় 0 এ যাওয়ার চেষ্টা করে।
- অবস্থানটিতে ভেরিয়েবল রয়েছে
i
। লুপটি কখনই শেষ হয় না কারণ i
0 এ পুনরায় চালু হয়।
- অবস্থানটিতে আরও কিছু ভেরিয়েবল রয়েছে। লুপটি সাধারণত সমাপ্ত হয় তবে তারপরে "আকর্ষণীয়" জিনিসগুলি ঘটে।
- অবস্থানটি একটি অবৈধ মেমরি ঠিকানা, উদাহরণস্বরূপ কারণ
array
একটি ভার্চুয়াল মেমরি পৃষ্ঠার শেষে এবং পরবর্তী পৃষ্ঠাটি ম্যাপ করা হয়নি।
- ভূত আপনার নাক থেকে উড়ে । ভাগ্যক্রমে বেশিরভাগ কম্পিউটারে প্রয়োজনীয় হার্ডওয়্যারটির অভাব রয়েছে।
আপনি উইন্ডোতে যা পর্যবেক্ষণ করেছিলেন তা হ'ল সংকলকটি i
মেমরিতে অ্যারের পরে অবিলম্বে পরিবর্তনশীল রাখার সিদ্ধান্ত নিয়েছে , সুতরাং নির্ধারিতটি array[10] = 0
শেষ হয়েছিল i
। উবুন্টু এবং সেন্টস-এ, সংকলকটি i
সেখানে রাখেনি । প্রায় সমস্ত সি প্রয়োগকারী একটি বড় ব্যতিক্রম সহ একটি মেমরি স্ট্যাকের উপর মেমরিতে গ্রুপ স্থানীয় ভেরিয়েবলগুলি করে : কিছু স্থানীয় ভেরিয়েবল সম্পূর্ণভাবে রেজিস্টারগুলিতে স্থাপন করা যেতে পারে । ভেরিয়েবলটি স্ট্যাকের মধ্যে থাকলেও, ভেরিয়েবলের ক্রম সংকলক দ্বারা নির্ধারিত হয় এবং এটি কেবল উত্স ফাইলে অর্ডের উপর নির্ভর করে না তবে তাদের ধরণের উপরও নির্ভর করে (গর্তগুলি ছেড়ে দেবে এমন প্রান্তিককরণের সীমাবদ্ধতায় স্মৃতিশক্তি নষ্ট করা এড়াতে) , তাদের নামগুলিতে, সংকলকের অভ্যন্তরীণ ডেটা কাঠামোতে ব্যবহৃত কিছু হ্যাশ মান ইত্যাদিতে
আপনি যদি জানতে চান যে আপনার সংকলকটি কী সিদ্ধান্ত নিয়েছে, আপনি এটি সংহিত কোডটি দেখানোর জন্য বলতে পারেন। ওহ, এবং ডিসেম্বারার ডিসিফার শিখুন (এটি লেখার চেয়ে সহজ)। জিসিসি (এবং আরও কিছু সংকলক, বিশেষত ইউনিক্স ওয়ার্ল্ডে) এর সাথে -S
বাইনারি পরিবর্তে এসেম্বলার কোড তৈরির বিকল্পটি পাস করুন । উদাহরণস্বরূপ, -O0
স্বনির্ধারিত অপশনটি (কোনও অপ্টিমাইজেশন নয়) সহ amd64 এ জিসিসির সাথে সংকলন থেকে লুপের জন্য এসেম্বলারের স্নিপেট এখানে মন্তব্য সহ ম্যানুয়ালি যুক্ত হয়েছে:
.L3:
movl -52(%rbp), %eax ; load i to register eax
cltq
movl $0, -48(%rbp,%rax,4) ; set array[i] to 0
movl $.LC0, %edi
call puts ; printf of a constant string was optimized to puts
addl $1, -52(%rbp) ; add 1 to i
.L2:
cmpl $10, -52(%rbp) ; compare i to 10
jle .L3
এখানে ভ্যারিয়েবলটি i
স্ট্যাকের শীর্ষের নীচে 52 বাইট, আর অ্যারে স্ট্যাকের শীর্ষের নীচে 48 বাইট শুরু হয়। সুতরাং এই সংকলক i
অ্যারের ঠিক আগে স্থাপন করা হয়েছে ; আপনি i
লিখিত হয়ে যদি আপনি ওভাররাইট চাই array[-1]
। আপনি যদি পরিবর্তিত array[i]=0
হন array[9-i]=0
, আপনি এই নির্দিষ্ট প্ল্যাটফর্মটিতে এই বিশেষ সংকলক বিকল্পগুলির সাথে একটি অসীম লুপ পাবেন।
এখন আসুন আপনার প্রোগ্রামটি সংকলন করুন gcc -O1
।
movl $11, %ebx
.L3:
movl $.LC0, %edi
call puts
subl $1, %ebx
jne .L3
তা খাটো! সংকলক কেবল স্ট্যাকের জন্য বরাদ্দ দিতে অস্বীকার করেনি i
- এটি কেবল কখনও নিবন্ধেই সঞ্চিত থাকে ebx
- তবে এটির জন্য কোনও মেমরি বরাদ্দ করতে array
, বা এর উপাদানগুলি সেট করার জন্য কোড উত্পন্ন করতে বিরত করেননি , কারণ এটি লক্ষ্য করেছে যে উপাদানগুলির মধ্যে কোনওটিই নয় কখনও ব্যবহৃত হয়।
এই উদাহরণটি আরও বলার জন্য, আসুন নিশ্চিত করুন যে অ্যারে অ্যাসাইনমেন্টগুলি এমন কোনও সংস্থার সরবরাহ করে যাতে এটি অপ্টিমাইজ করতে সক্ষম হয় না তা সম্পাদিত হয়। কারণ পৃথক সংকলন এর কম্পাইলার জানে না কী অন্য ফাইল ঘটে (যদি না তা লিংক সময়ে সেরা অনুকূল রূপ দেয়, যা - একটি সহজ উপায় যে কাজ করতে অন্য ফাইল থেকে অ্যারের ব্যবহার করা gcc -O0
বা gcc -O1
না)। একটি উৎস ফাইল তৈরি করুন use_array.c
ধারণকারী
void use_array(int *array) {}
এবং আপনার উত্স কোডটি এতে পরিবর্তন করুন
#include <stdio.h>
void use_array(int *array);
int main()
{
int array[10],i;
for (i = 0; i <=10 ; i++)
{
array[i]=0; /*code should never terminate*/
printf("test \n");
}
printf("%zd \n", sizeof(array)/sizeof(int));
use_array(array);
return 0;
}
সংকলন
gcc -c use_array.c
gcc -O1 -S -o with_use_array1.c with_use_array.c use_array.o
এবার এসেম্বলারের কোডটি দেখতে এমন দেখাচ্ছে:
movq %rsp, %rbx
leaq 44(%rsp), %rbp
.L3:
movl $0, (%rbx)
movl $.LC0, %edi
call puts
addq $4, %rbx
cmpq %rbp, %rbx
jne .L3
এখন অ্যারে স্ট্যাকের উপরে রয়েছে, উপরে থেকে 44 বাইট। কি হবে i
? এটি কোথাও প্রদর্শিত হয় না! তবে লুপের কাউন্টারটি রেজিস্টারে রাখা হয় rbx
। ঠিক নয় i
, কিন্তু এর ঠিকানা array[i]
। সংকলকটি সিদ্ধান্ত নিয়েছে যেহেতু মান i
কখনই সরাসরি ব্যবহার করা হয় নি, লুপের প্রতিটি রান চলাকালীন কোথায় 0 সংরক্ষণ করতে হবে তা গণনা করার জন্য পাটিগণিত সম্পাদন করার কোনও অর্থ নেই। পরিবর্তে সেই ঠিকানাটি লুপের পরিবর্তনশীল এবং গণ্ডি নির্ধারণের জন্য গাণিতিকটি সংকলনের সময় আংশিকভাবে সম্পাদিত হয়েছিল (44 টি পেতে অ্যারে উপাদান অনুসারে 4 বাইট দ্বারা 11 পুনরাবৃত্তিগুলি গুণ করুন) এবং আংশিকভাবে রান সময় হলেও একবার এবং সর্বদা লুপটি শুরু হওয়ার আগেই ( প্রাথমিক মান পেতে একটি বিয়োগফল সম্পাদন করুন)।
এমনকি এই খুব সহজ উদাহরণ, আমরা দেখা করেছি কিভাবে কম্পাইলার বিকল্প পরিবর্তন (অপ্টিমাইজেশান করুন) অথবা (কিছু ছোটখাট পরিবর্তন array[i]
করতে array[9-i]
) অথবা এমনকি কিছু দৃশ্যত সম্পর্কহীন (কলে যোগ পরিবর্তন use_array
) কি এক্সিকিউটেবল প্রোগ্রাম উত্পন্ন একটি উল্লেখযোগ্য পার্থক্য করতে পারেন সংকলক দ্বারা। সংকলক অপ্টিমাইজেশানগুলি এমন অনেক কিছু করতে পারে যা অপরিবর্তিত আচরণের জন্য প্রার্থনা করে এমন প্রোগ্রামগুলিতে অপ্রয়োজনীয় প্রদর্শিত হতে পারে । এজন্য অপরিবর্তিত আচরণ পুরোপুরি অপরিবর্তিত রয়েছে। আপনি যখন সত্যিকারের ওয়ার্ল্ড প্রোগ্রামগুলিতে ট্র্যাকগুলি থেকে এতটা সামান্য বিচ্যুত হন, কোডটি কী করে এবং এটি করা উচিত ছিল এমনকী, এমনকি অভিজ্ঞ প্রোগ্রামারদের ক্ষেত্রে সম্পর্কটি বোঝা খুব কঠিন হতে পারে।