एक समारोह से एक सी स्ट्रिंग लौट रहा है


109

मैं एक फ़ंक्शन से C स्ट्रिंग वापस करने की कोशिश कर रहा हूं, लेकिन यह काम नहीं कर रहा है। यहाँ मेरा कोड है।

char myFunction()
{
    return "My String";
}

में mainइसे इस तरह से बुला रहा हूँ:

int main()
{
  printf("%s", myFunction());
}

मैंने कुछ अन्य तरीकों के लिए भी कोशिश की है myFunction, लेकिन वे काम नहीं कर रहे हैं। उदाहरण के लिए:

char myFunction()
{
  char array[] = "my string";
  return array;
}

नोट: मुझे पॉइंटर्स का उपयोग करने की अनुमति नहीं है!

इस समस्या पर छोटी पृष्ठभूमि:

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

तो जब यह प्रिंट करने जा रहा है, यह इसे इस तरह कर रहा है printf("Month: %s",calculateMonth(month));:। अब समस्या यह है कि calculateMonthफ़ंक्शन से उस स्ट्रिंग को कैसे वापस किया जाए ।


10
दुर्भाग्य से आपको इस मामले में संकेत की आवश्यकता है
निक बेडफोर्ड

1
@ हयातो का मानना ​​है कि हम यहां वयस्क हैं और जानते हैं कि इसे वापस लौटना चाहिए, यह सिर्फ उदाहरण के लिए
लक्स

3
return 0केवल C99 (और C ++) में डिफ़ॉल्ट रूप से निहित है, लेकिन C90 में नहीं।
कूच

1
फिर आप इसे करने में सक्षम नहीं होने जा रहे हैं, गूंगा हैक के बगल में जो वास्तव में वैसे भी सूचक जोड़ तोड़ कर रहा है। एक कारण के लिए संकेत मौजूद हैं ...: |
GMANNICKG

जवाबों:


222

आपका फ़ंक्शन हस्ताक्षर होना चाहिए:

const char * myFunction()
{
    return "My String";
}

पृष्ठभूमि:

यह C & C ++ के लिए बहुत मौलिक है, लेकिन थोड़ी और चर्चा क्रम में होनी चाहिए।

C (& C ++ में उस मामले के लिए), एक स्ट्रिंग बाइट्स की एक सरणी है जिसे एक शून्य बाइट के साथ समाप्त किया जाता है - इसलिए स्ट्रिंग के इस विशेष स्वाद का प्रतिनिधित्व करने के लिए "स्ट्रिंग-शून्य" शब्द का उपयोग किया जाता है। अन्य प्रकार के तार हैं, लेकिन सी (और सी ++) में, यह स्वाद स्वाभाविक रूप से भाषा द्वारा ही समझा जाता है। अन्य भाषाएँ (जावा, पास्कल, आदि) "मेरी स्ट्रिंग" को समझने के लिए विभिन्न तरीकों का उपयोग करती हैं।

यदि आप कभी भी Windows API (जो C ++ में है) का उपयोग करते हैं, तो आपको नियमित रूप से फ़ंक्शन पैरामीटर जैसे: "LPCSTR lpszNot" दिखाई देंगे। 'Sz' भाग 'string-शून्य' की इस धारणा को दर्शाता है: एक शून्य (/ शून्य) टर्मिनेटर के साथ बाइट्स की एक सरणी।

स्पष्टीकरण:

इस 'परिचय' के लिए, मैं 'बाइट्स' और 'अक्षर' शब्द का परस्पर उपयोग करता हूं, क्योंकि इस तरह से सीखना आसान है। विदित हो कि अन्य विधियाँ (विस्तृत-वर्ण और बहु-बाइट वर्ण प्रणालियाँ ( mbcs )) हैं जिनका उपयोग अंतर्राष्ट्रीय वर्णों का सामना करने के लिए किया जाता है। UTF-8 एक mbcs का एक उदाहरण है। इंट्रो के लिए, मैं चुपचाप इस सब को 'स्किप ओवर' करता हूं।

स्मृति:

इसका मतलब है कि "मेरी स्ट्रिंग" जैसी एक स्ट्रिंग वास्तव में 9 + 1 (= 10!) बाइट्स का उपयोग करती है। यह जानना महत्वपूर्ण है कि आप अंत में गतिशील रूप से तार आवंटित करने के लिए चारों ओर कब पहुंचते हैं।

तो, इस 'शून्य को समाप्त' के बिना, आपके पास एक स्ट्रिंग नहीं है। आपके पास वर्णों की एक सरणी है (जिसे बफर भी कहा जाता है) स्मृति में चारों ओर लटका हुआ है।

डेटा की दीर्घायु:

इस तरह से फ़ंक्शन का उपयोग:

const char * myFunction()
{
    return "My String";
}

int main()
{
    const char* szSomeString = myFunction(); // Fraught with problems
    printf("%s", szSomeString);
}

... आम तौर पर आप यादृच्छिक यादृच्छिक अपवाद / खंड दोष और जैसे, विशेष रूप से 'नीचे सड़क' के साथ भूमि जाएगा।

संक्षेप में, हालांकि मेरा जवाब सही है - 10 में से 9 बार आप एक प्रोग्राम के साथ समाप्त हो जाएंगे, अगर आप इसे इस तरह से उपयोग करते हैं, खासकर अगर आपको लगता है कि यह उस तरह से करने के लिए 'अच्छा अभ्यास' है। संक्षेप में: यह आमतौर पर नहीं है।

उदाहरण के लिए, भविष्य में कुछ समय की कल्पना करें, स्ट्रिंग को अब किसी तरह से हेरफेर करने की आवश्यकता है। आम तौर पर, एक कोडर 'आसान रास्ता ले जाएगा' और (इस तरह) कोड लिखने की कोशिश करें:

const char * myFunction(const char* name)
{
    char szBuffer[255];
    snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
    return szBuffer;
}

है यही कारण है, अपने कार्यक्रम क्योंकि संकलक (/ नहीं हो सकता है) द्वारा इस्तेमाल किया स्मृति जारी किया है दुर्घटना होगा szBufferजब तक printf()में main()कहा जाता है। (आपके कंपाइलर को भी आपको ऐसी समस्याओं से पहले ही आगाह कर देना चाहिए।)

स्ट्रिंग्स को वापस करने के दो तरीके हैं जो इतनी आसानी से बारफ नहीं करेंगे।

  1. लौटने वाले बफ़र्स (स्थिर या गतिशील रूप से आवंटित) जो थोड़ी देर के लिए रहते हैं। C ++ std::stringमें डेटा की लंबी उम्र को संभालने के लिए 'हेल्पर क्लासेस' (उदाहरण के लिए ) का उपयोग करें (जिसमें फ़ंक्शन के रिटर्न मान को बदलने की आवश्यकता होती है, या या
  2. फ़ंक्शन के लिए एक बफ़र पास करें जो जानकारी से भर जाता है।

ध्यान दें कि सी में पॉइंटर्स का उपयोग किए बिना स्ट्रिंग्स का उपयोग करना असंभव है जैसा कि मैंने दिखाया है, वे समानार्थक हैं। यहां तक ​​कि टेम्पलेट कक्षाओं के साथ C ++ में, पृष्ठभूमि में हमेशा बफ़र (यानी, पॉइंटर्स) का उपयोग किया जा रहा है।

इसलिए, (अब संशोधित प्रश्न) का बेहतर उत्तर देने के लिए। (निश्चित रूप से 'अन्य उत्तर' प्रदान किए जा सकते हैं।)

सुरक्षित उत्तर:

उदाहरण 1, सांख्यिकीय रूप से आवंटित स्ट्रिंग्स का उपयोग करते हुए:

const char* calculateMonth(int month)
{
    static char* months[] = {"Jan", "Feb", "Mar" .... };
    static char badFood[] = "Unknown";
    if (month<1 || month>12)
        return badFood; // Choose whatever is appropriate for bad input. Crashing is never appropriate however.
    else
        return months[month-1];
}

int main()
{
    printf("%s", calculateMonth(2)); // Prints "Feb"
}

यहां 'स्थिर' क्या करता है (कई प्रोग्रामर इस तरह के 'आवंटन' को पसंद नहीं करते हैं) यह है कि स्ट्रिंग्स को प्रोग्राम के डेटा सेगमेंट में डाल दिया जाता है। यही है, यह स्थायी रूप से आवंटित किया गया है।

यदि आप C ++ से आगे बढ़ते हैं, तो आप समान रणनीतियों का उपयोग करेंगे:

class Foo
{
    char _someData[12];
public:
    const char* someFunction() const
    { // The final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
        return _someData;
    }
}

... लेकिन शायद सहायक वर्गों का उपयोग करना आसान है, जैसे कि std::string, यदि आप अपने स्वयं के उपयोग के लिए कोड लिख रहे हैं (और दूसरों के साथ साझा करने के लिए पुस्तकालय का हिस्सा नहीं है)।

उदाहरण 2, कॉलर-परिभाषित बफ़र्स का उपयोग कर:

यह चारों ओर तार गुजरने का अधिक 'मूर्खतापूर्ण' तरीका है। लौटाया गया डेटा कॉलिंग पार्टी द्वारा हेरफेर के अधीन नहीं है। यही है, उदाहरण 1 को कॉलिंग पार्टी द्वारा आसानी से दुरुपयोग किया जा सकता है और आपको आवेदन दोषों को उजागर कर सकता है। इस तरह, यह अधिक सुरक्षित है (यद्यपि कोड की अधिक पंक्तियों का उपयोग करता है):

void calculateMonth(int month, char* pszMonth, int buffersize)
{
    const char* months[] = {"Jan", "Feb", "Mar" .... }; // Allocated dynamically during the function call. (Can be inefficient with a bad compiler)
    if (!pszMonth || buffersize<1)
        return; // Bad input. Let junk deal with junk data.
    if (month<1 || month>12)
    {
        *pszMonth = '\0'; // Return an 'empty' string
        // OR: strncpy(pszMonth, "Bad Month", buffersize-1);
    }
    else
    {
        strncpy(pszMonth, months[month-1], buffersize-1);
    }
    pszMonth[buffersize-1] = '\0'; // Ensure a valid terminating zero! Many people forget this!
}

int main()
{
    char month[16]; // 16 bytes allocated here on the stack.
    calculateMonth(3, month, sizeof(month));
    printf("%s", month); // Prints "Mar"
}

दूसरी विधि बेहतर होने के कई कारण हैं, खासकर यदि आप दूसरों द्वारा उपयोग की जाने वाली लाइब्रेरी लिख रहे हैं (आपको किसी विशेष आवंटन / अस्वीकृति योजना में लॉक करने की आवश्यकता नहीं है, तो तृतीय पक्ष आपका कोड नहीं तोड़ सकते हैं) और आपको एक विशिष्ट मेमोरी मैनेजमेंट लाइब्रेरी से लिंक करने की आवश्यकता नहीं है), लेकिन सभी कोड की तरह, यह आपके ऊपर है कि आपको क्या पसंद है। उस कारण से, अधिकांश लोग उदाहरण 1 का चयन करते हैं जब तक कि उन्हें इतनी बार जलाया नहीं गया है कि वे इसे इस तरह से लिखने से इनकार करते हैं;)

अस्वीकरण:

मैंने कई साल पहले संन्यास लिया था और मेरा सी अब थोड़ा रस्टी है। यह डेमो कोड सभी को C के साथ ठीक से संकलित करना चाहिए (यह किसी भी C ++ कंपाइलर के लिए ठीक है)।


2
वास्तव में, फ़ंक्शन को char *C में स्ट्रिंग शाब्दिक के रूप में वापस लौटने की आवश्यकता है char[]। हालांकि, उन्हें किसी भी तरह से संशोधित नहीं किया जाना चाहिए, इसलिए रिटर्निंग const char*को प्राथमिकता दी जाती है (देखें Securecoding.cert.org/confluence/x/mwAV )। रिटर्निंग की char *आवश्यकता हो सकती है यदि स्ट्रिंग का उपयोग एक विरासत या बाहरी लाइब्रेरी फ़ंक्शन में किया जाएगा जो (दुर्भाग्य से) एक char*तर्क के रूप में उम्मीद करता है, यहां तक ​​कि कठिन भी इसे केवल से पढ़ा जाएगा। सी ++, दूसरी ओर, स्ट्रिंग const char[]प्रकार के स्ट्रिंग हैं (और, सी ++ 11 के बाद से, आप भी std::stringशाब्दिक हो सकते हैं )।
तमनहन्ते

17
@cmroanirgo मेरा उपसर्ग पाठक को बताता है कि फ़ंक्शन उपयोगकर्ता द्वारा बनाया गया था। मुझे इस तरह के संदर्भ में उपयोग करना पूरी तरह से उचित लगता है।
मात्रा

4
यहाँ के अनुसार: stackoverflow.com/questions/9970295/… , आप स्ट्रिंग शाब्दिक लौटा सकते हैं
giorgim

6
fraught with problems"डेटा की दीर्घायु" अनुभाग में चिह्नित कोड वास्तव में पूरी तरह से वैध है। स्ट्रिंग शाब्दिकों में C / C ++ में स्थिर जीवनकाल होता है। लिंक देखें ऊपर जियोर्गी उल्लेख है।
चेंगिज

1
@cmroanirgo रिटर्निंग स्ट्रिंग शाब्दिक अच्छा अभ्यास है, और अच्छी शैली है। यह "समस्याओं से भरा" नहीं है, और यह 10 में से 9 बार दुर्घटनाग्रस्त नहीं होगा: यह कभी भी दुर्घटनाग्रस्त नहीं होगा। यहां तक ​​कि 80 के दशक के कंपाइलर (कम से कम जिनका मैंने उपयोग किया है) स्ट्रिंग स्ट्रिंग के असीमित जीवनकाल का सही समर्थन करते हैं। नोट: मुझे यकीन नहीं है कि आप जवाब को संपादित करने के बारे में क्या मतलब है: मैं अभी भी देखता हूं कि यह कहता है कि यह क्रैश होने का खतरा है।
cesss

12

AC स्ट्रिंग को वर्णों की एक सरणी के लिए एक संकेतक के रूप में परिभाषित किया गया है।

यदि आपके पास संकेत नहीं हैं, तो परिभाषा के अनुसार आपके पास तार नहीं हो सकते।


आप किसी सरणी में किसी फ़ंक्शन में जा सकते हैं और फिर उस सरणी पर काम कर सकते हैं void foo( char array[], int length):। बेशक, arrayहुड के तहत एक संकेतक है, लेकिन यह एक संकेतक के रूप में "स्पष्ट रूप से" नहीं है, और इसलिए यह किसी को सीखने वाले सरणियों के लिए अधिक सहज हो सकता है, लेकिन जो काफी सीखा संकेत नहीं है
jvriesem

12

इस नए समारोह पर ध्यान दें:

const char* myFunction()
{
    static char array[] = "my string";
    return array;
}

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

एक और विकल्प यह होगा कि आप ढेर में स्ट्रिंग को आवंटित करने के लिए मॉलोक का उपयोग करें , और फिर अपने कोड के सही स्थानों पर मुफ्त। यह कोड रीइंटरेंट और थ्रेडसेफ़ होगा।

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

यह स्मृति आवंटन के बारे में कॉलर को संभालने देने के लिए बहुत अधिक अनुशंसित है। यह नया उदाहरण देखें:

char* myFunction(char* output_str, size_t max_len)
{
   const char *str = "my string";
   size_t l = strlen(str);
   if (l+1 > max_len) {
      return NULL;
   }
   strcpy(str, str, l);
   return input;
}

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


2
यह आमतौर पर चीजों के बारे में जाने का एक बुरा तरीका है। आसपास के कोड द्वारा चार * को हेरफेर करने में सक्षम है। यही है, आप इस तरह की चीजें कर सकते हैं: strcpy (myFunction (), "वास्तव में लंबी स्ट्रिंग"); और आपका कार्यक्रम पहुंच उल्लंघन के कारण दुर्घटनाग्रस्त हो जाएगा।
सेमीरोनिर्गो

"उपयोगकर्ता कि एक" के पास कुछ गायब है ।
पीटर मोर्टेंसन

8

आपकी समस्या फ़ंक्शन के रिटर्न प्रकार के साथ है - यह होना चाहिए:

char *myFunction()

... और फिर आपका मूल सूत्रीकरण काम करेगा।

ध्यान दें कि आपके पास बिंदु के बिना सी स्ट्रिंग्स शामिल नहीं हो सकते हैं , कहीं रेखा के साथ।

इसके अलावा: अपने संकलक चेतावनी को चालू करें। यह आपको एक स्पष्ट डाली के बिना एक char *को बदलने कि रिटर्न लाइन के बारे में चेतावनी देनी चाहिए charथी।


1
मुझे लगता है कि हस्ताक्षर को चार * होना चाहिए क्योंकि स्ट्रिंग एक शाब्दिक है, लेकिन अगर मुझे गलती नहीं है तो संकलक इसे स्वीकार करेगा।
ल्यूक

5

इस प्रश्न के साथ अपने नए जोड़े गए बैकस्टोरी के आधार पर, क्यों न केवल महीने के लिए 1 से 12 तक पूर्णांक लौटाएं, और मुख्य () फ़ंक्शन को स्विच स्टेटमेंट या यदि-और सीढ़ी का उपयोग करने का निर्णय लेने के लिए दें? यह निश्चित रूप से जाने का सबसे अच्छा तरीका नहीं है - चार * होगा - लेकिन इस तरह एक वर्ग के संदर्भ में मुझे लगता है कि यह संभवतः सबसे सुरुचिपूर्ण है।


3

आप कॉल करने वाले में सरणी बना सकते हैं, जो कि मुख्य कार्य है, और सरणी को कैली में पास करें जो कि आपका myFunction () है। इस प्रकार myFunction स्ट्रिंग को सरणी में भर सकता है। हालाँकि, आपको myFunction () घोषित करने की आवश्यकता है

char* myFunction(char * buf, int buf_len){
  strncpy(buf, "my string", buf_len);
  return buf;
}

और मुख्य कार्य में, myFunction को इस तरह से बुलाया जाना चाहिए:

char array[51];
memset(array, 0, 51); /* All bytes are set to '\0' */
printf("%s", myFunction(array, 50)); /* The buf_len argument  is 50, not 51. This is to make sure the string in buf is always null-terminated (array[50] is always '\0') */

हालांकि, एक पॉइंटर अभी भी उपयोग किया जाता है।


2

आपका फ़ंक्शन रिटर्न प्रकार एक एकल वर्ण ( char) है। आपको वर्ण सरणी के पहले तत्व के लिए एक सूचक लौटना चाहिए। यदि आप पॉइंटर्स का उपयोग नहीं कर सकते हैं, तो आप खराब हो गए हैं। :(


2

या इस बारे में कैसे:

void print_month(int month)
{
    switch (month)
    {
        case 0:
            printf("January");
            break;
        case 1:
            printf("february");
            break;
        ...etc...
    }
}

और कॉल करें कि महीने के साथ आप कहीं और गणना करते हैं।


1
+1 जो ओपी ने नहीं पूछा, लेकिन शायद यही वह काम है जो आप करने की अपेक्षा करते हैं, क्योंकि वह पॉइंटर्स का उपयोग नहीं कर सकता है।
विटिम .us

यहां तक ​​कि प्रिंटफ पॉइंटर्स का उपयोग करता है। एक पॉइंटर एक चाकू की तरह है - जीने और काम करने के लिए आवश्यक है, लेकिन आपको इसे संभाल कर रखना होगा और कटने के लिए तेज साइड का उपयोग करना होगा या आपके पास एक बुरा समय होगा। फ़ंक्शन परिभाषा में रिक्त स्थान रखने का दुर्भाग्यपूर्ण एक नया सी प्रोग्रामर के लिए एक मस्तिष्क बग है। char * func (char * s); char func (char * s); char func * char * s); सभी समान हैं, लेकिन सभी अलग दिखते हैं, और भ्रम की स्थिति के लिए, * चर के लिए डी-संदर्भ ऑपरेटर भी हैं जो पॉइंटर्स हैं।
क्रिस रीड ने

1

A charकेवल एक एकल-बाइट वर्ण है। यह न तो वर्णों को संग्रहीत कर सकता है, न ही यह एक संकेतक है (जो आपके पास स्पष्ट रूप से नहीं हो सकता है)। इसलिए आप पॉइंटर्स का उपयोग किए बिना अपनी समस्या को हल नहीं कर सकते (जो char[]कि चीनी के लिए है)।


1

यदि आप वास्तव में पॉइंटर्स का उपयोग नहीं कर सकते हैं, तो ऐसा कुछ करें:

char get_string_char(int index)
{
    static char array[] = "my string";
    return array[index];
}

int main()
{
    for (int i = 0; i < 9; ++i)
        printf("%c", get_string_char(i));
    printf("\n");
    return 0;
}

जादू नंबर 9 भयानक है, और यह अच्छी प्रोग्रामिंग का उदाहरण नहीं है। लेकिन आपको बात समझ आ गयी। ध्यान दें कि संकेत और सरणियाँ एक ही चीज़ (प्रकार) हैं, इसलिए यह थोड़ा धोखा है।


आमतौर पर अगर आपको होमवर्क समस्याओं के ऐसे समाधानों को लागू करने की आवश्यकता है, तो आपकी प्रारंभिक धारणाएं गलत हैं।
बजे

1

ठीक है, आपके कोड में आप एक String(C में जो कुछ भी नहीं है, लेकिन वर्णों के एक शून्य-समाप्त सरणी) को वापस करने की कोशिश कर रहे हैं , लेकिन आपके फ़ंक्शन का रिटर्न प्रकार जो आपके charलिए सभी परेशानी पैदा कर रहा है। इसके बजाय आपको इसे इस तरह लिखना चाहिए:

const char* myFunction()
{

    return "My String";

}

और यह हमेशा अच्छा होता constहै कि सी में पॉइंटर्स को सी में शाब्दिक रूप से निर्दिष्ट करते हुए अपने टाइप को क्वालिफाई करें क्योंकि सी में शाब्दिक रूप नहीं हैं।


0

आपका फ़ंक्शन प्रोटोटाइप बताता है कि आपका फ़ंक्शन एक चार्ट लौटाएगा। इस प्रकार, आप अपने फ़ंक्शन में एक स्ट्रिंग वापस नहीं कर सकते।


0
char* myFunction()
{
    return "My String";
}

सी में, स्ट्रिंग शाब्दिक स्थिर स्थिर मेमोरी क्लास के साथ सरणियाँ हैं, इसलिए इस सरणी के लिए एक सूचक लौटना सुरक्षित है। अधिक विवरण सी में एक स्ट्रिंग शाब्दिक के स्टैक ओवरफ्लो प्रश्न "लाइफ-टाइम" में हैं


0

समारोह से स्ट्रिंग लौटें

#include <stdio.h>

const char* greet() {
  return "Hello";
}

int main(void) {
  printf("%s", greet());
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.