আমি আমার অ্যাপ্লিকেশনটিতে নিম্নলিখিত কোডটি ব্যবহার করছি এবং এটি ঠিকঠাক কাজ করছে। তবে আমি ভাবছি যে এটি মলোক দিয়ে তৈরি করা বা এটি যেমন রেখে দেওয়া ভাল তবে?
function (int len)
{
char result [len] = some chars;
send result over network
}
আমি আমার অ্যাপ্লিকেশনটিতে নিম্নলিখিত কোডটি ব্যবহার করছি এবং এটি ঠিকঠাক কাজ করছে। তবে আমি ভাবছি যে এটি মলোক দিয়ে তৈরি করা বা এটি যেমন রেখে দেওয়া ভাল তবে?
function (int len)
{
char result [len] = some chars;
send result over network
}
উত্তর:
মূল পার্থক্য হ'ল ভিএলএস (ভেরিয়েবল দৈর্ঘ্যের অ্যারে) বরাদ্দ ব্যর্থতা সনাক্ত করার জন্য কোনও ব্যবস্থা সরবরাহ করে না।
আপনি যদি ঘোষণা
char result[len];
এবং len
উপলব্ধ স্ট্যাকের জায়গার পরিমাণ ছাড়িয়ে গেছে, আপনার প্রোগ্রামের আচরণটি নির্ধারিত। বরাদ্দটি সফল হবে কিনা তা আগাম নির্ধারণের জন্য, বা এটি সফল হয়েছে কিনা তা নির্ধারণের জন্য কোনও ভাষা প্রক্রিয়া নেই।
অন্যদিকে, আপনি যদি লিখেন:
char *result = malloc(len);
if (result == NULL) {
/* allocation failed, abort or take corrective action */
}
তারপরে আপনি ব্যর্থতাগুলি কৌতূহলীভাবে পরিচালনা করতে পারেন বা কমপক্ষে গ্যারান্টি দিতে পারেন যে আপনার প্রোগ্রাম ব্যর্থতার পরে চালিয়ে যাওয়ার চেষ্টা করবে না।
(বেশ, বেশিরভাগ ক্ষেত্রে। লিনাক্স সিস্টেমগুলিতে, malloc()
কোনও উপযুক্ত সঞ্চয়স্থান না থাকলেও ঠিকানার জায়গার একটি অংশ বরাদ্দ করতে পারে; পরে সেই স্থানটি ব্যবহারের চেষ্টা ওওম কিলারকে ডেকে আনতে পারে But তবে malloc()
ব্যর্থতার জন্য অনুসন্ধান করা এখনও ভাল অনুশীলন))
অনেক সিস্টেমে আরেকটি সমস্যা হ'ল ভিএলএস-এর মতো স্বয়ংক্রিয় বস্তুর চেয়ে আরও বেশি জায়গা (সম্ভবত আরও অনেক বেশি জায়গা) উপলব্ধ malloc()
।
এবং ফিলিপের উত্তর ইতিমধ্যে উল্লিখিত হিসাবে, ভিএলএগুলি সি 99 এ যুক্ত হয়েছিল (বিশেষত মাইক্রোসফ্ট তাদের সমর্থন করে না)।
এবং সি 11 এ ভিএলএগুলি .চ্ছিক করা হয়েছিল। সম্ভবত বেশিরভাগ সি 11 সংকলকগুলি তাদের সমর্থন করবে তবে আপনি এটির উপর নির্ভর করতে পারবেন না।
পরিবর্তনশীল দৈর্ঘ্যের স্বয়ংক্রিয় অ্যারেগুলিকে সি 99 এ চালু করা হয়েছিল।
পুরানো মানগুলির তুলনায় পিছনের দিকের তুলনা সম্পর্কে আপনার উদ্বেগ না থাকলে এটি ঠিক আছে।
সাধারণভাবে, যদি এটি কাজ করে তবে এটিকে স্পর্শ করবেন না। সময়ের আগে অপ্টিমাইজ করবেন না। বিশেষ বৈশিষ্ট্য যুক্ত করার বা জিনিস করার চতুর উপায়গুলি সম্পর্কে চিন্তা করবেন না কারণ আপনি প্রায়শই এটি ব্যবহার করবেন না। সহজবোধ্য রাখো.
যদি আপনার সংকলকটি পরিবর্তনশীল-দৈর্ঘ্যের অ্যারেগুলিকে সমর্থন করে len
তবে হাস্যকরভাবে বড় হলে একমাত্র বিপদটি কয়েকটি সিস্টেমে স্ট্যাকটি উপচে পড়ছে। আপনি যদি নিশ্চিতভাবে জানেন যে len
এটি একটি নির্দিষ্ট সংখ্যার চেয়ে বড় হবে না এবং আপনি জানেন যে আপনার স্ট্যাক সর্বোচ্চ দৈর্ঘ্যেও উপচে পড়ছে না, কোডটি যেমন রেখে দিন; অন্যথায়, সঙ্গে এটি পুনর্লিখন malloc
এবং free
।
char result [sizeof(char)]
একটি আকারের অ্যারে 1
(কারণ এটি একটি sizeof(char)
সমান), সুতরাং অ্যাসাইনমেন্টটি কেটে যাবে some chars
।
str
পয়েন্টারের সিদ্ধান্ত নেয় , তাই sizeof
এটি আপনার সিস্টেমে পয়েন্টারের আকারের উপর নির্ভর করে চার বা আট হতে চলেছে।
char* result = alloca(len);
যা স্ট্যাকের জন্য বরাদ্দ। এটি একই বুনিয়াদি প্রভাব (এবং একই বেসিক সমস্যা)
আমি এই ধারণাটি পছন্দ করি যে আপনি মেমরি বিভাজন, ঝুঁকিপূর্ণ পয়েন্টার ইত্যাদি বাদ দিয়ে রান-টাইম বরাদ্দকৃত অ্যারে রাখতে পারেন তবে, অন্যরা উল্লেখ করেছেন যে এই রান-টাইম বরাদ্দটি নিঃশব্দে ব্যর্থ হতে পারে। সুতরাং আমি সিসউইন বাশ পরিবেশে জিসিসি 4.5.3 ব্যবহার করে এটি চেষ্টা করেছি:
#include <stdio.h>
#include <string.h>
void testit (unsigned long len)
{
char result [len*2];
char marker[100];
memset(marker, 0, sizeof(marker));
printf("result's size: %lu\n", sizeof(result));
strcpy(result, "this is a test that should overflow if no allocation");
printf("marker's contents: '%s'\n", marker);
}
int main(int argc, char *argv[])
{
testit(100);
testit((unsigned long)-1); // probably too big
}
আউটপুটটি ছিল:
$ ./a.exe
result's size: 200
marker's contents: ''
result's size: 4294967294
marker's contents: 'should overflow if no allocation'
দ্বিতীয় কলটিতে অতি মাত্রায় অতিবাহিত হওয়া ব্যর্থতার কারণ হিসাবে স্পষ্ট হয়েছে (মার্কারে প্রবাহিত []]। এর অর্থ এই নয় যে এই ধরণের চেকটি বোকা-প্রমাণ (বোকা চতুর হতে পারে!) বা এটি সি 99 এর মানগুলি পূরণ করে, তবে আপনার যদি এই উদ্বেগ থাকে তবে এটি সহায়তা করতে পারে।
যথারীতি ওয়াইএমএমভি।
সাধারণত স্ট্যাকের কথা বলা আপনার ডেটা রাখার সবচেয়ে সহজ এবং সর্বোত্তম জায়গা।
আপনার প্রত্যাশা করা সবচেয়ে বড় অ্যারেটি কেবল বরাদ্দ করে আমি ভিএলএগুলির সমস্যাগুলি এড়াব।
তবে কিছু ক্ষেত্রে আছে যখন গাদাটি সবচেয়ে ভাল হয় এবং ম্যালোকের সাথে জগাখিচুড়ি করা প্রচেষ্টাটির পক্ষে মূল্যবান।
এম্বেড থাকা প্রোগ্রামিংগুলিতে, যখন ম্যালোক এবং ফ্রি অপারেশন ঘন ঘন থাকে তখন আমরা সর্বদা ম্যালোকের পরিবর্তে স্ট্যাটিক অ্যারে ব্যবহার করি। এম্বেড থাকা সিস্টেমে মেমরি পরিচালনার অভাবে, ঘন ঘন বরাদ্দ এবং নিখরচায় ক্রিয়াকলাপ মেমরির খণ্ডন ঘটায়। তবে আমাদের কিছু জটিল পদ্ধতি ব্যবহার করা উচিত যেমন অ্যারের সর্বাধিক আকার নির্ধারণ এবং স্থিতিশীল স্থানীয় অ্যারে ব্যবহার করা।
যদি আপনার অ্যাপ্লিকেশনটি লিনাক্স বা উইন্ডোতে চলমান থাকে তবে অ্যারে বা ম্যালোক ব্যবহার করা কোনও বিষয় নয়। মূল বিষয়টি যেখানে আপনি নিজের তারিখের কাঠামো এবং আপনার কোড যুক্তি ব্যবহার করেন সেখানে থাকে।
এমন কিছু যা এখনও কেউ উল্লেখ করেনি তা হ'ল ভেরিয়েবল দৈর্ঘ্যের অ্যারে বিকল্পটি সম্ভবত ম্যালোক / ফ্রি থেকে দ্রুততর হতে চলেছে কারণ একটি ভিএলএ বরাদ্দ করা কেবল স্ট্যাক পয়েন্টারটি সামঞ্জস্য করার ক্ষেত্রে (কমপক্ষে জিসিসিতে)।
সুতরাং, যদি এই ফাংশনটি এমন হয় যা ঘন ঘন বলা হয় (যা আপনি অবশ্যই প্রোফাইলের মাধ্যমে নির্ধারণ করবেন), ভিএলএ একটি ভাল অপ্টিমাইজেশন বিকল্প।
এটি সমস্যার জন্য আমি ব্যবহার করি এটি সাধারণ সমাধান which ভিএলএএস থেকে পৃথক, এটি প্যাথলজিকাল ক্ষেত্রে স্ট্যাক ওভারফ্লো হওয়ার কোনও ব্যবহারিক ঝুঁকির মুখোমুখি নয়।
/// Used for frequent allocations where the common case generally allocates
/// a small amount of memory, at which point a heap allocation can be
/// avoided, but rare cases also need to be handled which may allocate a
/// substantial amount. Note that this structure is not safe to copy as
/// it could potentially invalidate the 'data' pointer. Its primary use
/// is just to allow the stack to be used in common cases.
struct FastMem
{
/// Stores raw bytes for fast access.
char fast_mem[512];
/// Points to 'fast_mem' if the data fits. Otherwise, it will point to a
/// dynamically allocated memory address.
void* data;
};
/// @return A pointer to a newly allocated memory block of the specified size.
/// If the memory fits in the specified fast memory structure, it will use that
/// instead of the heap.
void* fm_malloc(struct FastMem* mem, int size)
{
// Utilize the stack if the memory fits, otherwise malloc.
mem->data = (size < sizeof mem->fast_mem) ? mem->fast_mem: malloc(size);
return mem->data;
}
/// Frees the specified memory block if it has been allocated on the heap.
void fm_free(struct FastMem* mem)
{
// Free the memory if it was allocated dynamically with 'malloc'.
if (mem->data != mem->fast_mem)
free(mem->data);
mem->data = 0;
}
আপনার ক্ষেত্রে এটি ব্যবহার করতে:
struct FastMem fm;
// `result` will be allocated on the stack if 'len <= 512'.
char* result = fm_malloc(&fm, len);
// send result over network.
...
// this function will only do a heap deallocation if 'len > 512'.
fm_free(&fm, result);
উপরের ক্ষেত্রে এটি যা করে তা হ'ল স্ট্যাকটি ব্যবহার করা যদি স্ট্রিংটি 512 বাইট বা তার চেয়ে কম হয়। অন্যথায় এটি একটি গাদা বরাদ্দ ব্যবহার করে। যদি বলা যায়, 99% সময়, স্ট্রিংটি 512 বাইট বা তার চেয়ে কম হয়ে যায় তবে এটি কার্যকর হতে পারে। যাইহোক, আসুন আমরা এমন কিছু ক্রেজি এক্সটিক কেস দেখতে পাই যা আপনার মাঝে মাঝে হ্যান্ডেল করার প্রয়োজন হতে পারে যেখানে স্ট্রিংটি 32 কিলোবাইট যেখানে ব্যবহারকারী তার কীবোর্ডে ঘুমিয়ে পড়েছিলেন বা এরকম কিছু। এটি উভয় পরিস্থিতিই সমস্যা ছাড়াই পরিচালনা করতে দেয়।
আমি উত্পাদনে ব্যবহার করি আসল সংস্করণটির নিজস্ব সংস্করণও রয়েছে realloc
এবং calloc
একই সাথে একই ধারণার উপর নির্মিত স্ট্যান্ডার্ড-কনফর্মিং সি ++ ডেটা স্ট্রাকচার রয়েছে তবে আমি ধারণাটি চিত্রিত করার জন্য প্রয়োজনীয় ন্যূনতমটি বের করেছি।
এটিতে সতর্কতা রয়েছে যে এটি চারপাশে অনুলিপি করা বিপজ্জনক এবং আপনার মাধ্যমে বরাদ্দকৃত পয়েন্টারগুলি ফিরিয়ে দেওয়া উচিত নয় ( FastMem
উদাহরণটি ধ্বংস হওয়ার সাথে সাথে তারা অবৈধ হয়ে যেতে পারে )। এটি কোনও স্থানীয় ফাংশনের ক্ষেত্রের মধ্যে সাধারণ ক্ষেত্রে ব্যবহার করার জন্য বোঝানো হয়েছিল যেখানে আপনি সর্বদা স্ট্যাক / ভিএলএস ব্যবহার করতে প্ররোচিত হন অন্যথায় যেখানে বিরল কিছু ক্ষেত্রে বাফার / স্ট্যাকের উপচে পড়তে পারে। এটি সাধারণ উদ্দেশ্যে বরাদ্দকারী নয় এবং এটি ব্যবহার করা উচিত নয়।
আমি আসলে বহু বছর আগে C89 ব্যবহার করে লিগ্যাসি কোডবাসে অবস্থার প্রতিক্রিয়ায় এটি তৈরি করেছি যে কোনও প্রাক্তন দল ভেবেছিল কখনই ঘটবে না যেখানে কোনও ব্যবহারকারী কোনও আইটেমের নাম রাখেন যেটি এমন একটি আইটেমের নাম রেখেছিল যেটি ২০৪৪ অক্ষরের বেশি দীর্ঘ (সম্ভবত সে তার কীবোর্ডে ঘুমিয়ে পড়েছিল) )। আমার সহকর্মীরা প্রকৃতপক্ষে বিভিন্ন স্থানে বরাদ্দকৃত অ্যারের আকার বাড়িয়ে 16,384 করার চেষ্টা করেছিল যার প্রতিক্রিয়ায় আমি ভেবেছিলাম এটি হাস্যকর হয়ে উঠছে এবং কেবল বাফার ওভারফ্লোয়ের কম ঝুঁকির বিনিময়ে স্ট্যাক ওভারফ্লোয়ের একটি বৃহত ঝুঁকির আদান প্রদান করছে। এটি এমন একটি সমাধান সরবরাহ করেছে যা কেবলমাত্র কয়েকটি লাইন কোড যুক্ত করে এই কেসগুলি ঠিক করতে প্লাগ ইন করা খুব সহজ ছিল। এটি সাধারণ কেসটিকে খুব দক্ষতার সাথে পরিচালনা করার অনুমতি দেয় এবং এখনও সফটওয়্যার ক্র্যাশ করার জন্য যে পাগলগুলির দাগ দাবি করে crazy তবে, আমি ' সিএল-এর পরেও ভিএলএগুলি এখনও স্ট্যাকের ওভারফ্লো থেকে রক্ষা করতে পারে না তার পরে এটি কার্যকর খুঁজে পেয়েছে। এটি একটি ছোট বরাদ্দের অনুরোধের জন্য স্ট্যাক থেকে কিন্তু এখনও পুলগুলি করতে পারে।
কল স্ট্যাক সবসময় সীমাবদ্ধ। লিনাক্স বা উইন্ডোজের মতো মূলধারার ওএসগুলিতে সীমাটি এক বা কয়েকটি মেগাবাইট (এবং আপনি এটি পরিবর্তন করার উপায়গুলি খুঁজে পেতে পারেন)। কিছু মাল্টি-থ্রেডেড অ্যাপ্লিকেশন সহ, এটি কম হতে পারে (কারণ থ্রেডগুলি একটি ছোট স্ট্যাক দিয়ে তৈরি করা যেতে পারে)। এম্বেড থাকা সিস্টেমে এটি কয়েক কিলোবাইটের মতো ছোট হতে পারে। থাম্বের একটি ভাল নিয়ম হল কয়েক কিলোবাইটের চেয়ে বড় কল ফ্রেমগুলি এড়ানো avoid
সুতরাং কোনও ভিএলএ ব্যবহার করা কেবল তখনই বোধগম্য হয় যদি আপনি নিশ্চিত হন যে আপনার len
যথেষ্ট পরিমাণে ছোট (বেশিরভাগ কয়েক হাজার কয়েক হাজার)। অন্যথায় আপনার স্ট্যাকের ওভারফ্লো হয়েছে এবং এটি অপরিজ্ঞাত আচরণের একটি ঘটনা , খুব ভয়ঙ্কর পরিস্থিতি।
তবে ম্যানুয়াল সি ডায়নামিক মেমোরি বরাদ্দ (উদাহরণস্বরূপ calloc
বা malloc
&free
) ব্যবহারেরও এর ত্রুটি রয়েছে:
এটি ব্যর্থ হতে পারে এবং আপনার সর্বদা ব্যর্থতার জন্য পরীক্ষা করা উচিত (যেমন calloc
বা malloc
প্রত্যাবর্তন NULL
)।
এটি ধীরে ধীরে: একটি সফল ভিএলএ বরাদ্দ কয়েকটি ন্যানোসেকেন্ড নেয়, সফলকে malloc
বেশ কয়েকটি মাইক্রোসেকেন্ডের প্রয়োজন হতে পারে (ভাল ক্ষেত্রে, কেবল একটি মাইক্রোসেকেন্ডের একটি ভগ্নাংশ) বা আরও বেশি ( থ্র্যাশিংয়ের সাথে জড়িত প্যাথলজিকাল ক্ষেত্রে আরও অনেক বেশি)।
এটি কোড করা বেশ কঠিন: আপনি free
কেবল তখনই পারেন যখন আপনি নিশ্চিত হন যে পয়েন্টযুক্ত অঞ্চলটি আর ব্যবহার করা হবে না। আপনার ক্ষেত্রে আপনি উভয় calloc
এবং free
একই রুটিন কল করতে পারেন ।
যদি আপনি জানেন যে বেশিরভাগ সময় আপনার result
(খুব খারাপ নাম, আপনার কখনই কোনও স্বয়ংক্রিয় ভেরিয়েবল ভিএলএর ঠিকানা ফেরত দেওয়া উচিত নয় ; তাই আমি নীচের buf
পরিবর্তে ব্যবহার করছি result
) আপনি যদি খুব ছোট হন তবে এটি বিশেষ ক্ষেত্রে ছোট হতে পারে eg
char tinybuf[256];
char *buf = (len<sizeof(tinybuf))?tinybuf:malloc(len);
if (!buf) { perror("malloc"); exit(EXIT_FAILURE); };
fill_buffer(buf, len);
send_buffer_on_network(buf, len);
if (buf != tinybuf)
free(buf);
তবে উপরের কোডটি কম পঠনযোগ্য এবং সম্ভবত অকাল অপটিমাইজেশন। এটি খাঁটি ভিএলএ সমাধানের চেয়ে আরও শক্তিশালী।
পুনশ্চ. কিছু সিস্টেমে (যেমন কিছু লিনাক্স ডিস্ট্রিবিউশন ডিফল্টরূপে সক্ষম হয়) মেমোরি ওভারকমিটমেন্ট থাকে (যা malloc
পর্যাপ্ত মেমরি না থাকলেও কিছু পয়েন্টার দেয়)। এটি এমন একটি বৈশিষ্ট্য যা আমি অপছন্দ করি এবং সাধারণত আমার লিনাক্স মেশিনে অক্ষম করি।