एक स्ट्रिंग शाब्दिक बुरे अभ्यास के साथ एक चार [] को शुरू करना है?


44

मैं कोडगुरू पर "स्ट्रलेन बनाम साइज़ोफ़" शीर्षक वाला एक धागा पढ़ रहा था , और एक उत्तर में कहा गया है कि "इनिशिएल [एसआईसी] बैड प्रैक्टिस charविद अ स्ट्रि लिटरल एक सरणी है।"

क्या यह सच है, या यह कि सिर्फ उनकी ("कुलीन सदस्य") की राय है?


यहाँ मूल प्रश्न है:

#include <stdio.h>
#include<string.h>
main()
{
    char string[] = "october";
    strcpy(string, "september");

    printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string));
    return 0;
}

सही। आकार लंबाई प्लस 1 हां होना चाहिए?

यह आउटपुट है

the size of september is 8 and the length is 9

आकार निश्चित रूप से 10 होना चाहिए। इसके आकार की स्ट्रिंग की गणना करने से पहले इसे स्ट्रॉपी द्वारा बदल दिया जाता है, लेकिन इसके बाद की लंबाई।

क्या मेरे वाक्यविन्यास में कुछ गड़बड़ है या क्या है?


यहाँ उत्तर है :

यह एक स्ट्रिंग शाब्दिक के साथ एक चार सरणी इनिशियलाइज़ करने के लिए वैसे भी खराब अभ्यास है। इसलिए हमेशा निम्न में से एक करें:

const char string1[] = "october";
char string2[20]; strcpy(string2, "september");

पहली पंक्ति पर "कॉन्स्ट" पर ध्यान दें। क्या ऐसा हो सकता है कि लेखक ने c के बजाय c ++ मान लिया? सी ++ में यह "बुरा व्यवहार" है, क्योंकि एक शाब्दिक कास्ट होना चाहिए और किसी भी हाल में सी ++ संकलक एक गैर-कास्ट एरे को एक कॉन्स्टेबल शाब्दिक आवंटित करने के बारे में चेतावनी (या त्रुटि) देगा।
आंद्रे

@ एंड्रे सी ++ स्ट्रिंग एरियल को कांस्टेबल सरणियों के रूप में परिभाषित करता है, क्योंकि यह उनके साथ निपटने का एकमात्र सुरक्षित तरीका है। वह C समस्या नहीं है, इसलिए आपके पास एक सामाजिक नियम है जो सुरक्षित चीज़ को लागू करता है
Caleth

@Caleth। मुझे पता है, मैं यह तर्क देने की कोशिश कर रहा था कि उत्तर के लेखक सी ++ परिप्रेक्ष्य से "बुरा व्यवहार" कर रहे थे।
आंद्रे

@ André यह C ++ में एक बुरा अभ्यास नहीं है, क्योंकि यह एक अभ्यास नहीं है , यह एक सीधे प्रकार की त्रुटि है। यह C में एक प्रकार की त्रुटि होनी चाहिए , लेकिन ऐसा नहीं है, इसलिए आपको एक स्टाइल गाइड नियम बताना होगा जो आपको "यह मना है"
Caleth

जवाबों:


59

यह एक स्ट्रिंग शाब्दिक के साथ एक चार सरणी इनिशियलाइज़ करने के लिए वैसे भी खराब अभ्यास है।

उस टिप्पणी के लेखक कभी भी इसे सही नहीं ठहराते हैं, और मुझे यह कथन अजीब लगता है।

C में (और आपने इसे C के रूप में टैग किया है), यह एक स्ट्रिंग मान के साथ एक सरणी को इनिशियलाइज़ करने का एकमात्र तरीका है char(आरंभीकरण असाइनमेंट से अलग है)। आप लिख भी सकते हैं

char string[] = "october";

या

char string[8] = "october";

या

char string[MAX_MONTH_LENGTH] = "october";

पहले मामले में, सरणी का आकार इनिशलाइज़र के आकार से लिया गया है। स्ट्रिंग साहित्यिकों को charएक समाप्ति 0 बाइट के साथ सरणियों के रूप में संग्रहीत किया जाता है , इसलिए सरणी का आकार 8 ('o', 'c', 't', 'o', 'b', 'e', ​​'r') है। 0)। दूसरे दो मामलों में, ऐरे का आकार घोषणा के भाग के रूप में निर्दिष्ट किया गया है (8 और MAX_MONTH_LENGTH, जो कुछ भी होता है)।

आप जो नहीं कर सकते हैं वह कुछ इस तरह से लिखें

char string[];
string = "october";

या

char string[8];
string = "october";

आदि पहले मामले में, की घोषणा stringहै अधूरा है क्योंकि कोई सरणी आकार निर्दिष्ट किया गया है और वहाँ से आकार लेने की कोई प्रारंभकर्ता है। दोनों मामलों में, =काम नहीं करेगा क्योंकि (क) एक सरणी अभिव्यक्ति जैसे कि stringअसाइनमेंट का लक्ष्य नहीं हो सकता है और बी) =ऑपरेटर किसी भी तरह से एक सरणी की सामग्री को कॉपी करने के लिए परिभाषित नहीं है।

उसी टोकन से, आप लिख नहीं सकते

char string[] = foo;

जहां fooका एक और सरणी है char। आरंभीकरण का यह रूप केवल स्ट्रिंग शाब्दिक के साथ काम करेगा।

संपादित करें

मुझे यह कहने के लिए संशोधन करना चाहिए कि आप ऐरे-स्टाइल इनिशियलाइज़र के साथ एक स्ट्रिंग को पकड़ने के लिए ऐरे को भी इनिशियलाइज़ कर सकते हैं, जैसे

char string[] = {'o', 'c', 't', 'o', 'b', 'e', 'r', 0};

या

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII

लेकिन यह आंखों पर स्ट्रिंग शाब्दिक का उपयोग करने के लिए आसान है।

EDIT 2

एक घोषणा के बाहर एक सरणी की सामग्री को असाइन करने के लिए , आपको या तो strcpy/strncpy(0-समाप्त स्ट्रिंग्स के लिए) या memcpy(किसी अन्य प्रकार के सरणी के लिए) का उपयोग करने की आवश्यकता होगी:

if (sizeof string > strlen("october"))
  strcpy(string, "october");

या

strncpy(string, "october", sizeof string); // only copies as many characters as will
                                           // fit in the target buffer; 0 terminator
                                           // may not be copied, but the buffer is
                                           // uselessly completely zeroed if the
                                           // string is shorter!


@KeithThompson: असहमति नहीं, बस इसे पूर्णता की खातिर जोड़ा।
जॉन बोडे

16
कृपया ध्यान दें कि char[8] str = "october";बुरा अभ्यास है। मैं सचमुच चार यकीन है कि यह एक अतिप्रवाह नहीं था बनाने के लिए अपने आप को गिनती करने के लिए था और यह सुधार कार्य प्रगति पर टूट जाता है ... जैसे से वर्तनी की त्रुटि को सही करने seprateके लिए separateकरता है, तो आकार अद्यतन नहीं टूट जाएगा।
djechlin

1
मैं djechlin से सहमत हूं, यह दिए गए कारणों के लिए बुरा व्यवहार है। जॉनबोड का जवाब "बुरे अभ्यास" पहलू (जो प्रश्न का मुख्य हिस्सा है !!) पर कोई टिप्पणी नहीं करता है, यह सिर्फ यह बताता है कि आप सरणी को शुरू करने के लिए क्या कर सकते हैं या नहीं कर सकते हैं।
मस्तोव

माइनर: जैसा कि 'लंबाई "मान दिया से strlen()अशक्त चरित्र शामिल नहीं है, का उपयोग कर MAX_MONTH_LENGTHके लिए आवश्यक अधिकतम आकार धारण करने के लिए char string[]अक्सर लग रहा है । गलत IMO, MAX_MONTH_SIZEबेहतर यहाँ होगा।
chux - को पुनः स्थापित मोनिका

10

एकमात्र समस्या जो मुझे याद आती है वह है स्ट्रिंग शाब्दिक char *:

char var1[] = "september";
var1[0] = 'S'; // Ok - 10 element char array allocated on stack
char const *var2 = "september";
var2[0] = 'S'; // Compile time error - pointer to constant string
char *var3 = "september";
var3[0] = 'S'; // Modifying some memory - which may result in modifying... something or crash

उदाहरण के लिए इस कार्यक्रम को लें:

#include <stdio.h>

int main() {
  char *var1 = "september";
  char *var2 = "september";
  var1[0] = 'S';
  printf("%s\n", var2);
}

मेरे प्लेटफ़ॉर्म (लिनक्स) पर यह दुर्घटनाग्रस्त हो जाता है क्योंकि यह पृष्ठ को केवल पढ़ने के लिए चिह्नित करने की कोशिश करता है। अन्य प्लेटफार्मों पर यह 'सितंबर' आदि प्रिंट कर सकता है।

यह कहा गया - शाब्दिक द्वारा आरम्भिक आरक्षण आरक्षण की विशिष्ट राशि बनाता है इसलिए यह काम नहीं करेगा:

char buf[] = "May";
strncpy(buf, "September", sizeof(buf)); // Result "Sep"

लेकिन यह होगा

char buf[32] = "May";
strncpy(buf, "September", sizeof(buf));

अंतिम टिप्पणी के रूप में - मैं बिल्कुल भी उपयोग नहीं करूंगा strcpy:

char buf[8];
strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory

हालांकि कुछ संकलक इसे सुरक्षित कॉल में बदल सकते हैं, यह strncpyअधिक सुरक्षित है:

char buf[1024];
strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers.
buf[sizeof(buf) - 1] = '\0';

बफर बफर के लिए अभी भी एक जोखिम है, strncpyक्योंकि यह प्रतिलिपि स्ट्रिंग को समाप्त नहीं करता है जब लंबाई की something_elseतुलना में अधिक है sizeof(buf)। मैं आमतौर पर buf[sizeof(buf)-1] = 0उस से बचाने के लिए अंतिम चार सेट करता हूं , या यदि bufशून्य-प्रारंभिक है, sizeof(buf) - 1तो कॉपी लंबाई के रूप में उपयोग करें ।

का उपयोग करें strlcpyया strcpy_sया यहां तक ​​कि snprintfअगर आप के लिए है।
user253751

फिक्स्ड। दुर्भाग्य से ऐसा करने का कोई आसान पोर्टेबल तरीका नहीं है जब तक कि आपके पास नवीनतम संकलक के साथ काम करने का लक्जरी नहीं है ( strlcpyऔर snprintfएमएसवीसी पर सीधे पहुंच नहीं है, कम से कम आदेश और strcpy_s* निक्स पर नहीं हैं)।
मैकिज पीचोटका

@MaciejPiechotka: ठीक है, धन्यवाद भगवान यूनिक्स ने microsoft- प्रायोजित एनेक्स k को अस्वीकार कर दिया।
डेडुप्लिकेटर

6

एक चीज जो न तो धागा लाती है वह यह है:

char whopping_great[8192] = "foo";

बनाम

char whopping_great[8192];
memcpy(whopping_great, "foo", sizeof("foo"));

पूर्व कुछ ऐसा करेगा:

memcpy(whopping_great, "foo", sizeof("foo"));
memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));

उत्तरार्द्ध केवल ज्ञापन करता है। C मानक इस बात पर जोर देता है कि यदि किसी ऐरे के किसी भाग को आरंभीकृत किया जाता है, तो यह सब है। तो इस मामले में, इसे स्वयं करना बेहतर है। मुझे लगता है कि हो सकता है कि ट्रस में क्या हो रहा था।

पक्का

char whopping_big[8192];
whopping_big[0] = 0;

या तो बेहतर है:

char whopping_big[8192] = {0};

या

char whopping_big[8192] = "";

ps बोनस अंक के लिए, आप कर सकते हैं:

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));

यदि आप सरणी को ओवरफ्लो करने के बारे में शून्य त्रुटि से भाग करने के लिए एक संकलन समय फेंकने के लिए।


5

मुख्य रूप से क्योंकि आपके पास char[]एक चर / निर्माण का आकार नहीं होगा जिसे आप प्रोग्राम के भीतर आसानी से उपयोग कर सकते हैं।

लिंक से कोड नमूना:

 char string[] = "october";
 strcpy(string, "september");

stringस्टैक पर 7 या 8 वर्णों के रूप में आवंटित किया गया है। मुझे याद नहीं आ रहा है कि क्या यह इस तरह से समाप्त हो गया है या नहीं - आपके द्वारा जोड़ा गया धागा जो यह कहा गया है कि यह है।

उस तार पर "सिपाही" की नकल करना एक स्पष्ट स्मृति अति है।

एक अन्य चुनौती यह है कि यदि आप stringकिसी अन्य फ़ंक्शन को पास करते हैं तो अन्य फ़ंक्शन सरणी में लिख सकते हैं। आपको अन्य फ़ंक्शन को यह बताने की आवश्यकता है कि सरणी कितनी लंबी है इसलिए यह ओवररन नहीं बनाता है। आप stringपरिणाम के साथ गुजर सकते हैं , strlen()लेकिन धागा बताते हैं कि यह कैसे stringसमाप्त हो सकता है अगर यह शून्य-समाप्त नहीं है।

आप एक निश्चित आकार के साथ एक स्ट्रिंग आवंटित करने से बेहतर हैं (अधिमानतः एक स्थिरांक के रूप में परिभाषित) और फिर सरणी और निश्चित आकार को दूसरे फ़ंक्शन में पास करें। @ जॉन बोद की टिप्पणी सही है, और इन जोखिमों को कम करने के तरीके हैं। उनका उपयोग करने के लिए उन्हें आपकी ओर से अधिक प्रयास की भी आवश्यकता होती है।

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


sizeof stringआपको बफर का आकार देगा (8 बाइट्स); strlenजब आप स्मृति के बारे में चिंतित हों तो इसके बजाय उस अभिव्यक्ति के परिणाम का उपयोग करें ।
इसी तरह, आप कॉल से पहले एक चेक बना सकते हैं यह देखने के strcpyलिए कि क्या स्रोत स्ट्रिंग के लिए आपका लक्ष्य बफर काफी बड़ा है if (sizeof target > strlen(src)) { strcpy (target, src); }:।
हाँ, यदि आप एक समारोह के लिए सरणी पारित करने के लिए है, तो आप अपनी शारीरिक आकार के साथ-साथ उत्तीर्ण होना होगा: foo (array, sizeof array / sizeof *array);। - जॉन बोडे


2
sizeof stringआपको बफर का आकार देगा (8 बाइट्स); strlenजब आप स्मृति के बारे में चिंतित हों तो इसके बजाय उस अभिव्यक्ति के परिणाम का उपयोग करें । इसी तरह, आप कॉल से पहले एक चेक बना सकते हैं यह देखने के strcpyलिए कि क्या स्रोत स्ट्रिंग के लिए आपका लक्ष्य बफर काफी बड़ा है if (sizeof target > strlen(src)) { strcpy (target, src); }:। हाँ, यदि आप एक समारोह के लिए सरणी पारित करने के लिए है, तो आप अपनी शारीरिक आकार के साथ-साथ उत्तीर्ण होना होगा: foo (array, sizeof array / sizeof *array);
जॉन बोडे

1
@ जॉनबोड - धन्यवाद, और वे अच्छे अंक हैं। मैंने आपकी टिप्पणी को अपने उत्तर में शामिल कर लिया है।

1
अधिक सटीक रूप से, सरणी नाम के अधिकांश संदर्भ, सरणी के पहले तत्व की ओर इशारा करते हुए stringएक अंतर्निहित रूपांतरण में परिणत होते हैं char*। यह सरणी सीमा जानकारी खो देता है। एक फ़ंक्शन कॉल कई संदर्भों में से एक है जिसमें ऐसा होता है। char *ptr = string;दूसरा है। यहां तक ​​कि string[0]इसका एक उदाहरण है; []ऑपरेटर संकेत पर काम करता है, सीधे नहीं सरणियों पर। सुझाव पठन: comp.lang.c के अनुभाग 6 में अक्सर पूछे जाने वाले प्रश्न
कीथ थॉम्पसन

अंत में एक उत्तर जो वास्तव में प्रश्न को संदर्भित करता है!
मस्तोव

2

मुझे लगता है कि "बुरा व्यवहार" विचार इस तथ्य से आता है:

char string[] = "october is a nice month";

स्रोत मशीन कोड से स्टैक के लिए अनुमानित रूप से एक कठिन बनाता है।

यह केवल उस स्ट्रिंग की एक कड़ी को संभालने के लिए अधिक कुशल है। जैसे:

char *string = "october is a nice month";

या सीधे:

strcpy(output, "october is a nice month");

(लेकिन ज्यादातर कोड में यह निश्चित रूप से कोई फर्क नहीं पड़ता)


यदि आप इसे संशोधित करने का प्रयास करते हैं तो क्या यह केवल एक प्रति नहीं होगी? मुझे लगता है कि संकलक इससे ज्यादा स्मार्ट होगा
कोल जॉनसन

1
ऐसे मामलों के बारे में char time_buf[] = "00:00";जहां आप एक बफर को संशोधित करने जा रहे हैं? एक char *स्ट्रिंग शाब्दिक के लिए प्रारंभिक पहले बाइट के पते पर सेट किया गया है, इसलिए इसे संशोधित करने की कोशिश कर रहा है अपरिभाषित व्यवहार में परिणाम क्योंकि स्ट्रिंग शाब्दिक भंडारण की विधि अज्ञात (कार्यान्वयन परिभाषित) है, जबकि एक के बाइट्स को संशोधित करना char[]पूरी तरह से कानूनी है क्योंकि इनिशियलाइज़ेशन बाइट्स को एक स्टिक पर आवंटित राइट स्पेस पर कॉपी करता है। यह कहना कि यह "कम कुशल" या "बुरा अभ्यास" है, बिना किसी की बारीकियों के बारे में विस्तार से बताए बिना char* vs char[]भ्रामक है।
ब्रैडेन बेस्ट

-3

कभी भी वास्तव में लंबे समय तक नहीं होता है, लेकिन आपको इनिशियलाइज़ेशन चार्ट से बचना चाहिए [] स्ट्रिंग के लिए, क्योंकि, "स्ट्रिंग" कांस्ट चर * है, और आप इसे चार * को असाइन कर रहे हैं। इसलिए यदि आप इस चर [] को विधि में बदलते हैं जो डेटा को बदलता है तो आप दिलचस्प व्यवहार कर सकते हैं।

जैसा कि कहा गया है कि मैंने चार * के साथ थोड़ा सा चार [] मिलाया है, यह अच्छा नहीं है क्योंकि वे थोड़ा अलग हैं।

डेटा को चार सरणी में असाइन करने के बारे में कुछ भी गलत नहीं है, लेकिन इस सरणी का उपयोग करने के इरादे से इसे 'स्ट्रिंग' (चार *) के रूप में उपयोग करना है, यह भूलना आसान है कि आपको इस सरणी को संशोधित नहीं करना चाहिए।


3
गलत। आरंभीकरण स्ट्रिंग शाब्दिक की सामग्री को सरणी में कॉपी करता है। सरणी वस्तु constतब तक नहीं है जब तक आप इसे इस तरह परिभाषित नहीं करते हैं। (और सी में स्ट्रिंग शाब्दिक नहीं हैं const, हालांकि एक स्ट्रिंग शाब्दिक को संशोधित करने के किसी भी प्रयास में अपरिभाषित व्यवहार नहीं है।) char *s = "literal";उस तरह का व्यवहार है जिसके बारे में आप बात कर रहे हैं; यह बेहतर रूप में लिखा गया हैconst char *s = "literal";
कीथ थॉम्पसन

वास्तव में मेरी गलती है, मैंने चार [] को चार * के साथ मिलाया। लेकिन मैं सामग्री को सरणी में कॉपी करने के बारे में इतना निश्चित नहीं हूं। एमएस सी संकलक के साथ त्वरित जांच से पता चलता है कि 'चार सी [] = "एसएफडी";) const सेगमेंट में 'string' बनाएंगे और फिर इस एड्रेस को ऐरे वेरिएबल में असाइन करेंगे। यह वास्तव में एक कारण है कि मैंने नॉन कॉस्ट चार सरणी में असाइनमेंट से बचने के बारे में कहा।
Dainius

मुझे संदेह है। इस कार्यक्रम का प्रयास करें और मुझे बताएं कि आपको क्या आउटपुट मिलता है।
कीथ थॉम्पसन

2
"और आम तौर पर" asdf "एक स्थिर है, इसलिए इसे कॉन्स्टेबल घोषित किया जाना चाहिए।" - एक ही तर्क एक constपर कॉल करेगा int n = 42;, क्योंकि 42एक स्थिर है।
कीथ थॉम्पसन

1
इससे कोई फर्क नहीं पड़ता कि आप किस मशीन पर हैं। भाषा मानक गारंटी देता है कि cयह परिवर्तनीय है। यह उतना ही मजबूत है जितना कि एक गारंटी जो इसका 1 + 1मूल्यांकन करता है 2। यदि मैं ऊपर से जुड़ा हुआ कार्यक्रम मुद्रण के अलावा कुछ भी करता है EFGH, तो यह एक गैर-अनुरूपता सी कार्यान्वयन को इंगित करता है।
कीथ थॉम्पसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.