मैं अपने आवेदन में निम्नलिखित कोड का उपयोग कर रहा हूं, और यह ठीक काम कर रहा है। लेकिन मैं सोच रहा हूं कि क्या इसे मॉलॉक के साथ बनाना बेहतर है या इसे छोड़ देना है?
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()
पता स्थान का एक हिस्सा आवंटित कर सकते हैं , भले ही कोई समान भंडारण उपलब्ध न हो; बाद में उस स्थान का उपयोग करने का प्रयास ओओएम किलर को आमंत्रित कर सकता है । लेकिन malloc()
विफलता के लिए जाँच अभी भी अच्छा अभ्यास है।)
कई प्रणालियों पर एक और मुद्दा यह है कि वीएलएएस जैसी स्वचालित वस्तुओं की तुलना में अधिक स्थान (संभवतः बहुत अधिक स्थान) उपलब्ध है malloc()
।
और जैसा कि फिलिप के जवाब में पहले ही उल्लेख किया गया था, वीएलए को सी 99 में जोड़ा गया था (विशेष रूप से माइक्रोसॉफ्ट उनका समर्थन नहीं करता है)।
और VL11 को C11 में वैकल्पिक बनाया गया। संभवतः अधिकांश C11 संकलक उनका समर्थन करेंगे, लेकिन आप इस पर भरोसा नहीं कर सकते।
C99 में वैरिएबल-लेंथ ऑटोमैटिक एरेज़ को C में पेश किया गया था।
जब तक आपको पुराने मानकों की तुलना करने के बारे में चिंता नहीं है, यह ठीक है।
सामान्य तौर पर, अगर यह काम करता है, तो इसे न छुएं। समय से पहले अनुकूलन न करें। विशेष सुविधाओं या चीजों को करने के चतुर तरीकों को जोड़ने के बारे में चिंता न करें, क्योंकि आप अक्सर इसका उपयोग नहीं करने जा रहे हैं। इसे सरल रखें।
यदि आपका कंपाइलर वैरिएबल-लेंथ एरे का समर्थन करता है, तो एकमात्र खतरा कुछ सिस्टम पर स्टैक को ओवरफ्लो कर रहा है, जब len
यह हास्यास्पद रूप से बड़ा है। यदि आप यह सुनिश्चित करते हैं कि len
एक निश्चित संख्या से बड़ा नहीं होने वाला है, और आप जानते हैं कि आपका स्टैक अधिकतम लंबाई पर भी ओवरफ्लो नहीं होने वाला है, तो कोड को इस प्रकार छोड़ दें; अन्यथा, के साथ इसे फिर से लिखना malloc
और free
।
char result [sizeof(char)]
आकार की एक सरणी है 1
(क्योंकि sizeof(char)
एक के बराबर है), इसलिए असाइनमेंट कम होने वाला है some chars
।
str
एक पॉइंटरsizeof
का फैसला करता है , इसलिए यह आपके सिस्टम पर पॉइंटर आकार के आधार पर चार या आठ होने जा रहा है।
char* result = alloca(len);
, जो स्टैक पर आवंटित करता है। इसका एक ही मूल प्रभाव है (और एक ही मूल समस्या)
मुझे यह विचार पसंद है कि आपके पास स्मृति विखंडन, झूलने वाले बिंदुओं आदि के बिना रन-टाइम आवंटित सरणी हो सकती है, हालांकि, अन्य लोगों ने बताया है कि यह रन-टाइम आवंटन चुपचाप विफल हो सकता है। इसलिए मैंने इसे Cygwin bash वातावरण में gcc 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'
दूसरी कॉल में ओवरली बड़ी लंबाई स्पष्ट रूप से विफलता (मार्कर में अतिप्रवाह) का कारण बनी। इसका मतलब यह नहीं है कि इस तरह की जाँच मूर्खतापूर्ण है (मूर्ख चालाक हो सकते हैं!) या कि यह C99 के मानकों को पूरा करता है, लेकिन अगर आपको यह चिंता है तो इससे मदद मिल सकती है।
हमेशा की तरह, YMMV।
आम तौर पर स्टैक बोलना आपके डेटा को डालने का सबसे आसान और सबसे अच्छा स्थान है।
मैं वीएलए की समस्याओं को केवल आपके द्वारा अपेक्षित सबसे बड़ी सरणी को आवंटित करने से बचूंगा।
हालांकि ऐसे भी मामले हैं जब ढेर सबसे अच्छा है और मॉलॉक के साथ खिलवाड़ करना प्रयास के लायक है।
एम्बेडेड प्रोग्रामिंग में, हम हमेशा मॉलोक के बजाय स्थिर सरणी का उपयोग करते हैं जब मॉलोक और मुफ्त संचालन अक्सर होते हैं। एम्बेडेड सिस्टम में मेमोरी प्रबंधन की कमी के कारण, बार-बार आवंटन और मुफ्त संचालन मेमोरी के टुकड़े का कारण होगा। लेकिन हमें कुछ मुश्किल तरीकों का उपयोग करना चाहिए जैसे कि सरणी के अधिकतम आकार को परिभाषित करना और स्थिर स्थानीय सरणी का उपयोग करना।
यदि आपका एप्लिकेशन लिनक्स या विंडोज में चल रहा है, तो ऐरे या मॉलॉक का उपयोग करना कोई बात नहीं है। मुख्य बिंदु यह है कि आप अपनी तिथि संरचना और अपने कोड तर्क का उपयोग कहां करते हैं।
ऐसा कुछ जिसका किसी ने अभी तक उल्लेख नहीं किया है, वह यह है कि चर लंबाई सरणी विकल्प संभवतः मॉलोक / मुफ्त की तुलना में बहुत अधिक तेज़ होने वाला है क्योंकि वीएलए को आवंटित करना स्टैक पॉइंटर को समायोजित करने का एक मामला है (जीसीसी में कम से कम)।
इसलिए, यदि यह फ़ंक्शन एक है जिसे अक्सर कहा जाता है (जिसे आप निश्चित रूप से, प्रोफाइलिंग द्वारा निर्धारित करेंगे), वीएलए एक अच्छा अनुकूलन विकल्प है।
यह एक बहुत ही सामान्य सी समाधान है जो इस समस्या के लिए उपयोग करता है जो मदद का हो सकता है। वीएलएएस के विपरीत, यह रोग संबंधी मामलों में स्टैक ओवरफ्लो के किसी भी व्यावहारिक जोखिम का सामना नहीं करता है।
/// 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 का उपयोग करते हुए एक विरासत कोडबेस में एक स्थिति के जवाब में बनाया था कि एक पूर्व टीम ने सोचा था कि ऐसा कभी नहीं होगा जहां एक उपयोगकर्ता नाम के साथ एक आइटम का प्रबंधन करने में कामयाब रहा जो 2047 से अधिक वर्णों से लंबा था (शायद वह अपने कीबोर्ड पर सो गया था )। मेरे सहयोगियों ने वास्तव में विभिन्न स्थानों में आवंटित सरणियों के आकार को बढ़ाकर 16,384 करने की कोशिश की, जिसके जवाब में मुझे लगा कि यह हास्यास्पद हो रहा है और सिर्फ बफर ओवरफ्लो के कम जोखिम के बदले स्टैक ओवरफ्लो के अधिक जोखिम का आदान-प्रदान हो रहा है। इसने एक समाधान प्रदान किया जो कोड की एक-दो पंक्तियों को जोड़कर उन मामलों को ठीक करने के लिए प्लग करना बहुत आसान था। इसने सामान्य मामले को बहुत कुशलता से संभाला और अभी भी उन पागल दुर्लभ मामलों के बिना स्टैक का उपयोग किया है जिन्होंने सॉफ्टवेयर को ढेर करने की मांग की। हालांकि, मैं' क्योंकि यह तब भी उपयोगी है C99 के बाद से VLAs अभी भी हमें ढेर overflows के खिलाफ की रक्षा नहीं कर सकता है। यह एक लेकिन अभी भी छोटे आवंटन अनुरोधों के लिए स्टैक से पूल कर सकता है।
कॉल स्टैक हमेशा सीमित है। लिनक्स या विंडोज जैसे मुख्यधारा के ओएस पर सीमा एक या कुछ मेगाबाइट है (और आप इसे बदलने के तरीके पा सकते हैं)। कुछ बहु-थ्रेडेड अनुप्रयोगों के साथ, यह कम हो सकता है (क्योंकि थ्रेड्स को एक छोटे स्टैक के साथ बनाया जा सकता है)। एम्बेडेड सिस्टम पर, यह कुछ किलोबाइट जितना छोटा हो सकता है। अंगूठे का एक अच्छा नियम कुछ किलोबाइट से बड़े कॉल फ़्रेम से बचने के लिए है।
इसलिए वीएलए का उपयोग करना केवल तभी समझ में आता है जब आप सुनिश्चित हों कि आपका len
काफी छोटा है (कम से कम दर्जनों की संख्या में दर्जनों)। अन्यथा आपके पास एक स्टैक अतिप्रवाह है और यह अपरिभाषित व्यवहार का मामला है , बहुत ही डरावनी स्थिति है।
हालाँकि, मैन्युअल C डायनामिक मेमोरी एलोकेशन (जैसे calloc
या malloc
&free
) का उपयोग करने पर भी इसकी कमियां हैं:
यह विफल हो सकता है और आपको हमेशा विफलता के लिए परीक्षण करना चाहिए (उदाहरण के लिए calloc
या malloc
वापस NULL
)।
यह धीमा है: एक सफल वीएलए आवंटन कुछ नैनोसेकंड लेता है, एक सफल malloc
को कई माइक्रोसेकंड (अच्छे मामलों में, केवल एक माइक्रोसेकंड का एक अंश) या उससे भी अधिक (पैथोलॉजिकल मामलों में थ्रेशिंग , बहुत अधिक) की आवश्यकता हो सकती है।
यह कोड के लिए बहुत कठिन है: आप free
केवल तभी सुनिश्चित कर सकते हैं कि इंगित क्षेत्र का अधिक उपयोग नहीं किया जाता है। आपके मामले में आप दोनों कॉल कर सकते हैं calloc
और free
एक ही दिनचर्या में।
यदि आप जानते हैं कि आपका अधिकांश समय result
(बहुत खराब नाम है, तो आपको कभी भी स्वचालित चर VLA का पता नहीं लौटाना चाहिए ; इसलिए मैं नीचे के buf
बजाय इसका उपयोग कर रहा हूं result
) छोटा है आप इसे विशेष मामला कर सकते हैं, जैसे।
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
कि पर्याप्त मेमोरी नहीं होने पर भी कुछ पॉइंटर देता है)। यह एक ऐसी सुविधा है जिसे मैं नापसंद करता हूं और आमतौर पर मेरी लिनक्स मशीनों पर अक्षम होता है।