दिलचस्प पोस्ट
एक चिड़ियाघर से वर्ष और वर्ष निकालें :: वर्ष वस्तु Mongodb एकत्रीकरण फ्रेमवर्क | एकाधिक मूल्यों पर समूह? फ़ायरफ़ॉक्स में सामग्री यूआरएल काम नहीं कर रहा है SQLParameter एसक्यूएल इंजेक्शन कैसे रोकता है? वसंत एओपी विधि विधि के लिए किसी अन्य विधि के भीतर काम नहीं कर रहा है 1 डी सरणी से कुशल नीलम 2 डी सरणी निर्माण पीपीएपी अभिभावक वर्ग चर का प्रयोग मुझे एक टंकिनर आवेदन में मुख्य लूप को कैसे कॉल करना पड़ता है? जावास्क्रिप्ट फंक्शन ऑब्जेक्ट्स के गुण विंडोज फॉर्म प्रोग्रेसबार: मार्की के शुरू / बंद करने का सबसे आसान तरीका है? क्या मुझे एकल बृहदान्त्र (:) या दोहरे बृहदान्त्र का उपयोग करना चाहिए (:): पहले, बाद में, प्रथम-अक्षर और प्रथम-पंक्ति छद्म-तत्वों के लिए? इनपुट में कुंजी दर्ज करने पर एकल इनपुट फ़ील्ड के साथ फॉर्म क्यों जमा होते हैं? मैं अपने SQL Server एजेंट कार्य में एक कदम कैसे बना सकता हूं जो कि मेरे एसएसआईएस पैकेज को चलाएगा? PHP + MySQL लेनदेन उदाहरण ट्विटर छवि एन्कोडिंग चुनौती

सी और सी ++ कंपाइलर्स फ़ंक्शन के हस्ताक्षर में ऐरे लम्बाई क्यों करते हैं, जब वे कभी लागू नहीं करते हैं?

मेरी सीख अवधि के दौरान मुझे यही मिला:

#include<iostream> using namespace std; int dis(char a[1]) { int length = strlen(a); char c = a[2]; return length; } int main() { char b[4] = "abc"; int c = dis(b); cout << c; return 0; } 

तो चर int dis(char a[1]) , [1] कुछ भी नहीं कर रहा है और इसमें काम नहीं करता है
सब, क्योंकि मैं a[2] उपयोग कर सकता हूँ जैसे int a[] या char *a । मुझे पता है कि सरणी नाम एक सूचक है और एक सरणी कैसे पहुंचाता है, इसलिए मेरी पहेली इस हिस्से के बारे में नहीं है।

मैं क्या जानना चाहता हूं, इसलिए कंपाइलर्स इस व्यवहार को ( int a[1] ) की अनुमति देते हैं। या इसके अन्य अर्थ हैं जिनके बारे में मुझे पता नहीं है?

वेब के समाधान से एकत्रित समाधान "सी और सी ++ कंपाइलर्स फ़ंक्शन के हस्ताक्षर में ऐरे लम्बाई क्यों करते हैं, जब वे कभी लागू नहीं करते हैं?"

कार्य करने के लिए एरेज़ गुजरने के लिए यह वाक्यविन्यास का एक उद्धरण है

दरअसल, सी में एक सरणी गुजरना संभव नहीं है। यदि आप सिंटैक्स लिखते हैं, तो लगता है कि सरणी को पास करना चाहिए, वास्तव में क्या होता है कि सरणी के पहले तत्व के लिए एक संकेतक इसके बजाय पारित किया जाता है।

चूंकि सूचक में कोई भी लम्बाई की जानकारी शामिल नहीं है, फ़ंक्शन औपचारिक पैरामीटर सूची में आपके [] की सामग्री को वास्तव में अनदेखा कर दिया गया है।

इस सिंटैक्स की अनुमति देने का फैसला 1 9 70 के दशक में किया गया था और इसके बाद से बहुत भ्रम हो गया है …

पहले आयाम की लंबाई को अनदेखा कर दिया जाता है, लेकिन कंपाइलर ऑफसेट को सही तरीके से गणना करने की अनुमति देने के लिए अतिरिक्त आयाम की लंबाई आवश्यक है। निम्न उदाहरण में, foo फ़ंक्शन एक पॉइंटर को दो-आयामी सरणी के पास पारित करता है।

 #include <stdio.h> void foo(int args[10][20]) { printf("%zd\n", sizeof(args[0])); } int main(int argc, char **argv) { int a[2][20]; foo(a); return 0; } 

पहले आयाम [10] का आकार नजरअंदाज कर दिया गया; कंपाइलर आपको अन्तराल को बंद करने से रोक नहीं पाएगा (ध्यान दें कि औपचारिक 10 तत्वों को चाहता है, लेकिन वास्तविक केवल 2 प्रदान करता है)। हालांकि, दूसरे आयाम का आकार [20] प्रत्येक पंक्ति की प्रगति को निर्धारित करने के लिए उपयोग किया जाता है, और यहां, औपचारिक वास्तविक से मेल खाना चाहिए। दोबारा, कंपाइलर आपको दूसरी आयाम के समापन को बंद करने से रोक नहीं सकता है।

सरणी के आधार से एक तत्व args[row][col] लिए ऑफसेट बाइट args[row][col] द्वारा निर्धारित किया जाता है:

 sizeof(int)*(col + 20*row) 

ध्यान दें कि यदि col >= 20

sizeof(args[0]) , मेरे मशीन पर 80 देता है जहां sizeof(int) == 4 हालांकि, अगर मैं sizeof(args) लेने का प्रयास करता हूं, तो मुझे निम्न संकलक चेतावनी मिलती है:

 foo.c:5:27: warning: sizeof on array function parameter will return size of 'int (*)[20]' instead of 'int [10][20]' [-Wsizeof-array-argument] printf("%zd\n", sizeof(args)); ^ foo.c:3:14: note: declared here void foo(int args[10][20]) ^ 1 warning generated. 

यहां, कंपाइलर चेतावनी दे रहा है कि यह केवल सूचक का आकार दे रहा है जिसमें सरणी के आकार के बजाय सरणी क्षय हो जाती है।

समस्या और इसे कैसे सी + + में दूर करने के लिए

इस समस्या को पैट और मैट द्वारा बड़े पैमाने पर समझाया गया है कंपाइलर मूल रूप से सरणी के आकार के पहले आयाम को अनदेखी कर रहा है, जो पारित तर्क के आकार को अनदेखी कर रहा है।

सी ++ में, दूसरी ओर, आप इस सीमा को आसानी से दो तरीकों से पार कर सकते हैं:

  • संदर्भों का उपयोग करते हुए
  • std::array का उपयोग करते हुए (सी ++ 11)

संदर्भ

यदि आपका फ़ंक्शन केवल एक मौजूदा सरणी को पढ़ने या संशोधित करने का प्रयास कर रहा है (इसे कॉपी नहीं किया गया है) तो आप आसानी से संदर्भों का उपयोग कर सकते हैं

उदाहरण के लिए, मान लीजिए आप एक ऐसा फ़ंक्शन चाहते हैं, जो कि दस तत्वों की सरणी को प्रत्येक तत्व को 0 सेट करता है। आप निम्न फ़ंक्शन के हस्ताक्षर का उपयोग करके आसानी से ऐसा कर सकते हैं:

 void reset(int (&array)[10]) { ... } 

न केवल यह ठीक काम करेगा, लेकिन यह सरणी के आयाम को भी लागू करेगा।

उपरोक्त कोड जेनेरिक बनाने के लिए आप टेम्प्लेट का उपयोग भी कर सकते हैं:

 template<class Type, std::size_t N> void reset(Type (&array)[N]) { ... } 

और अंत में आप const सही का लाभ ले सकते हैं। चलो एक फ़ंक्शन पर विचार करें जो कि 10 तत्वों की एक सरणी मुद्रित करता है:

 void show(const int (&array)[10]) { ... } 

const क्वालीफायर को लागू करने से हम संभव संशोधनों को रोक रहे हैं।


सरणियों के लिए मानक पुस्तकालय वर्ग

यदि आप उपरोक्त सिंटैक्स को बदसूरत और अनावश्यक दोनों मानते हैं, जैसे मैं करता हूं, तो हम उसे इसे में फेंक सकते हैं और std::array बजाय (सी ++ 11) का उपयोग कर सकते हैं।

यहां रिफैक्टर कोड है:

 void reset(std::array<int, 10>& array) { ... } void show(std::array<int, 10> const& array) { ... } 

क्या ये अद्भुत नहीं है? यह नहीं बताया गया है कि जेनेरिक कोड चाल मैंने आपको पहले सिखाया है, फिर भी काम करता है:

 template<class Type, std::size_t N> void reset(std::array<Type, N>& array) { ... } template<class Type, std::size_t N> void show(const std::array<Type, N>& array) { ... } 

इतना ही नहीं, लेकिन आप प्रतिलिपि प्राप्त करते हैं और सिमेंटिक को मुफ्त में ले जाते हैं। 🙂

 void copy(std::array<Type, N> array) { // a copy of the original passed array // is made and can be dealt with indipendently // from the original } 

तो आप किसका इंतज़ार कर रहे हैं? Go std::array उपयोग करें

यह सी की एक मजेदार विशेषता है , जिससे आप अपने आप को प्रभावी ढंग से शूट कर सकते हैं यदि आप ऐसा चाहते हैं

मुझे लगता है कि इसका कारण यह है कि सी सिर्फ असेंबली भाषा के ऊपर एक कदम है। पीक प्रदर्शन और समान सुरक्षा सुविधाओं को चोटी के प्रदर्शन की अनुमति देने के लिए निकाल दिया गया है, जो प्रोग्रामर बहुत मेहनती है अगर वह बुरी बात नहीं है।

साथ ही, फंक्शन तर्क के लिए एक आकार निर्दिष्ट करना लाभ होता है जब फ़ंक्शन दूसरे प्रोग्रामर द्वारा उपयोग किया जाता है, तो एक मौका है कि वे एक आकार प्रतिबंध नोटिस करेंगे। सिर्फ एक संकेतक का इस्तेमाल करते हुए यह नहीं बताता कि अगली प्रोग्रामर को जानकारी

सबसे पहले, सी सरणी सीमा को कभी नहीं जांचता है अगर वे स्थानीय, वैश्विक, स्थिर, पैरामीटर, जो भी हो, कोई फर्क नहीं पड़ता सरणी की सीमाओं की जांच करने से अधिक प्रसंस्करण होता है, और सी को बहुत ही कुशल माना जाता है, इसलिए ज़रूरत पड़ने पर प्रोग्रामर द्वारा सरणी सीमा जांच की जाती है।

दूसरा, एक ऐसी चाल है जो किसी फ़ंक्शन में पास-बाय-वैल्यू को एक सरणी बनाता है। फंक्शन से एक सरणी वापस-दर-मूल्य भी संभव है आपको संरचना के द्वारा बस एक नया डेटा प्रकार बनाने की जरूरत है उदाहरण के लिए:

 typedef struct { int a[10]; } myarray_t; myarray_t my_function(myarray_t foo) { myarray_t bar; ... return bar; } 

आपको इस तरह के तत्वों का उपयोग करना होगा: foo.a [1] अतिरिक्त ".a" अजीब लग सकता है, लेकिन यह चाल सी भाषा को बड़ी कार्यक्षमता जोड़ती है

कंपाइलर को बताने के लिए कि myArray कम से कम 10 ints की एक सरणी बताता है:

 void bar(int myArray[static 10]) 

यदि आप myArray [10] का उपयोग करते हैं, तो एक अच्छा संकलक आपको एक चेतावनी देना चाहिए "स्थैतिक" कीवर्ड के बिना, 10 का मतलब बिल्कुल कुछ नहीं होगा।

यह C का एक प्रसिद्ध "फीचर" है, जो सी ++ पर पारित हो गया है क्योंकि सी ++ को सी कोड को सही ढंग से संकलित करना है।

कई पहलुओं से समस्या उत्पन्न होती है:

  1. एक सरणी नाम को संकेतक के बराबर होना चाहिए।
  2. सी को तेजी से माना जाता है, मूल रूप से डेवलपर को "उच्च स्तरीय असेंबलर" (विशेषकर पहले "पोर्टेबल ऑपरेटिंग सिस्टम": यूनिक्स) लिखने के लिए डिज़ाइन किया गया है, इसलिए इसे "छुपा" कोड सम्मिलित नहीं करना है; इस प्रकार रनटाइम रेंज जांच "निषिद्ध" है
  3. एक स्थिर सरणी या गतिशील एक (या तो स्टैक या आवंटित) तक पहुंचने के लिए तैयार मशीन कोड वास्तव में अलग है
  4. चूंकि फ़ंक्शन बुलाया जा सकता है कि तर्क के रूप में पारित सारणी के "प्रकार" को नहीं पता जा सकता है सब कुछ एक संकेतक माना जाता है और इस तरह के रूप में माना जाता है।

आप कह सकते हैं कि सी वास्तव में सी में समर्थित नहीं हैं (यह वास्तव में सच नहीं है, जैसा कि मैंने पहले कहा था, लेकिन यह एक अच्छा सन्निकटन है); एक सरणी को वास्तव में डेटा के ब्लॉक में एक संकेतक के रूप में माना जाता है और पॉइंटर अंकगणितीय का उपयोग करके उपयोग किया जाता है। चूंकि सी में आरटीटीआई का कोई रूप नहीं है, इसलिए आपको फ़ंक्शन प्रोटोटाइप में सरणी तत्व के आकार को घोषित करना होगा (सूचक अंकगणितीय को समर्थन देने के लिए)। यह बहुआयामी arrays के लिए "अधिक सच" भी है

वैसे भी इसके बाद के संस्करण सभी वास्तव में अब सच नहीं है: पी

अधिकांश आधुनिक सी / सी + + कंपाइलर्स समर्थन की सीमा जांच करते हैं, लेकिन मानकों को इसे डिफ़ॉल्ट रूप से बंद करना पड़ता है (पिछड़े संगतता के लिए) उदाहरण के लिए, जीसीसी के प्रचलित संस्करणों में, "-O3 -Wall -Wextra" के साथ संकलन-समय सीमा की जांच करना और "-fbounds-checking" के साथ पूर्ण रन-टाइम सीमा जाँच करना है

सी केवल एक प्रकार का पैरामीटर नहीं बदल जाएगा int[5] *int ; घोषित किया गया typedef int intArray5[5]; , यह एक पैरामीटर प्रकार intArray5 को *int रूप में भी बदल देगा। कुछ ऐसी परिस्थितियां हैं जहां यह व्यवहार, हालांकि अजीब, उपयोगी है (विशेषकर va_list परिभाषित va_list जैसी चीजों के साथ, जो कुछ कार्यान्वयन एक सरणी के रूप में परिभाषित करते हैं)। यह एक पैरामीटर के रूप में एक प्रकार की परिभाषा के रूप में परिभाषित करने की अनुमति नहीं होगी int[5] (आयाम की अनदेखी), लेकिन int[5] को सीधे निर्दिष्ट करने की अनुमति नहीं देता

मैं सी के सरणी प्रकार के मापदंडों को बेतुका होने का पता लगाता हूं, लेकिन यह एक विज्ञापन-हॉक भाषा लेने के प्रयासों का एक परिणाम है, जिनमें से बड़े हिस्से विशेष रूप से अच्छी तरह से परिभाषित नहीं होते थे या व्यवहार के साथ आने की कोशिश करते थे विनिर्देशों जो मौजूदा कार्यक्रमों के लिए मौजूदा कार्यान्वयन के अनुरूप हैं सी के कई quirks समझ में है कि प्रकाश में जब देखा, विशेष रूप से अगर एक समझता है कि जब उनमें से कई का आविष्कार किया गया था, भाषा के बड़े हिस्से आज हम जानते हैं कि अभी तक अस्तित्व में नहीं था मैं जो समझता हूं, पूर्ववर्ती में सी को बीसीपीएल कहा जाता है, कम्पाइलर वास्तव में चर प्रकारों का ट्रैक बहुत अच्छी तरह से नहीं रखते थे। एक घोषणापत्र int arr[5]; int anonymousAllocation[5],*arr = anonymousAllocation; बराबर था int anonymousAllocation[5],*arr = anonymousAllocation; ; एक बार आवंटन को अलग रखा गया था। कंपाइलर न तो जानता था और न ही देखभाल करता है कि क्या arr एक सूचक या एक सरणी थी। जब कोई arr[x] या *arr रूप में पहुंचा जाता है, तो इसे कैसे घोषित किया गया था, इसके बावजूद इसे एक सूचक के रूप में माना जाएगा।

एक बात जो अभी तक उत्तरित नहीं हुई है वह वास्तविक प्रश्न है

पहले से ही दिए गए उत्तरों बताते हैं कि सरणियों को किसी भी सी या सी ++ में फ़ंक्शन के लिए मूल्य से पारित नहीं किया जा सकता है। वे यह भी समझाते हैं कि int[] रूप में घोषित पैरामीटर को माना जाता है जैसे कि इसमें टाइप int * , और यह कि एक प्रकार का चर int[] ऐसे फ़ंक्शन को पारित किया जा सकता है

लेकिन वे यह नहीं समझाते हैं कि सरणी लंबाई निर्दिष्ट करने के लिए स्पष्ट त्रुटि क्यों नहीं हुई है।

 void f(int *); // makes perfect sense void f(int []); // sort of makes sense void f(int [10]); // makes no sense 

इनमें से एक आखिरी त्रुटि क्यों नहीं है?

इसके लिए एक कारण यह है कि यह टाइपिंग के साथ समस्याओं का कारण बनता है।

 typedef int myarray[10]; void f(myarray array); 

यदि फ़ंक्शन पैरामीटर में सरणी लंबाई को निर्दिष्ट करने के लिए कोई त्रुटि थी, तो आप फ़ंक्शन पैरामीटर में myarray नाम का उपयोग करने में सक्षम नहीं होंगे। और चूंकि कुछ कार्यान्वयन मानक पुस्तकालय प्रकार जैसे va_list लिए सरणी प्रकारों का उपयोग करते हैं, और jmp_buf एक सरणी प्रकार बनाने के लिए सभी कार्यान्वयन आवश्यक हैं, यह बहुत समस्याग्रस्त होगा यदि उन नामों का उपयोग करके फ़ंक्शन पैरामीटर घोषित करने का कोई मानक तरीका नहीं है: बिना उस क्षमता के, ऐसे कार्यों के पोर्टेबल कार्यान्वयन नहीं हो सकते जैसे vprintf