स्ट्रिंग निरंतर से 'चार *' में परिवर्तित रूपांतरण


16

इस त्रुटि का क्या अर्थ है? मैं इसे किसी भी तरह से हल नहीं कर सकता।

चेतावनी: स्ट्रिंग निरंतर से 'char *' में रूपांतरण


यह सवाल स्टैकऑवरफ्लो पर होना चाहिए, न कि अरुडिनो :)
विजय चावड़ा

जवाबों:


26

जैसा कि मेरा अभ्यस्त है, मैं इस त्रुटि के whys और जहां कहीं भी पृष्ठभूमि तकनीकी जानकारी का एक सा प्रदान करने जा रहा हूँ।

मैं C स्ट्रिंग्स को इनिशियलाइज़ करने के चार अलग-अलग तरीकों का निरीक्षण करने जा रहा हूँ और देखता हूँ कि उनके बीच क्या अंतर हैं। ये चार तरीके हैं:

char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";

अब इसके लिए मैं तीसरे अक्षर "i" को "o" में बदलकर "Thos is some text" बनाना चाहता हूं। वह, सभी मामलों में (आप सोचेंगे), द्वारा प्राप्त किया जा सकता है:

text[2] = 'o';

अब आइए देखें कि स्ट्रिंग घोषित करने का प्रत्येक तरीका क्या करता है और यह text[2] = 'o';कथन किस प्रकार चीजों को प्रभावित करेगा।

सबसे पहले देखा जाने वाला तरीका char *text = "This is some text";:। इसका शाब्दिक अर्थ क्या है? खैर, सी में, इसका शाब्दिक अर्थ है "एक चर बनाएं textजो इस स्ट्रिंग शाब्दिक के लिए एक रीड-राइट पॉइंटर है जो रीड-ओनली (कोड) स्पेस में आयोजित किया जाता है।" यदि आपके पास विकल्प -Wwrite-stringsचालू है तो आपको एक चेतावनी मिलती है जैसा कि ऊपर दिए गए प्रश्न में देखा गया है।

मूल रूप से इसका मतलब है कि "चेतावनी: आपने एक ऐसा चर बनाने की कोशिश की है जो आपके द्वारा लिखे गए क्षेत्र में पढ़ने-लिखने के बिंदु पर नहीं है"। यदि आप कोशिश करते हैं और फिर तीसरे चरित्र को "ओ" पर सेट करते हैं, तो आप वास्तव में केवल पढ़ने के लिए लिखने की कोशिश कर रहे हैं और चीजें अच्छी नहीं होंगी। लिनक्स के साथ एक पारंपरिक पीसी पर जिसके परिणामस्वरूप:

विभाजन दोष

अब दूसरा वाला char text[] = "This is some text";:। शाब्दिक रूप से, C में, इसका मतलब है कि "एक प्रकार का वर्ण बनाएं" char "और इसे डेटा के साथ प्रारंभ करें" यह कुछ है \ 0 "। सरणी का आकार डेटा को संग्रहीत करने के लिए पर्याप्त बड़ा होगा"। ताकि वास्तव में RAM आवंटित हो जाए और रनटाइम पर "यह कुछ टेक्स्ट \ 0" है। कोई चेतावनी, कोई त्रुटि नहीं, पूरी तरह से वैध। और अगर आप डेटा को संपादित करने में सक्षम होना चाहते हैं तो इसे करने का सही तरीका है । आइए कमांड चलाने की कोशिश करें text[2] = 'o':

थोस कुछ पाठ है

यह काम किया, पूरी तरह से। अच्छा।

अब तीसरा तरीका const char *text = "This is some text";:। फिर शाब्दिक अर्थ: पाठ "है कि एक" एक चर बुलाया बनाएँ " केवल पढ़ने के लिए । केवल पढ़ने के लिए स्मृति में इस डेटा के लिए सूचक"। ध्यान दें कि सूचक और डेटा दोनों अब केवल-पढ़ने के लिए हैं। कोई त्रुटि नहीं, कोई चेतावनी नहीं। यदि हम अपना परीक्षण कमांड चलाते हैं और चलाते हैं तो क्या होता है? खैर, हम नहीं कर सकते। कंपाइलर अब बुद्धिमान है और जानता है कि हम कुछ बुरा करने की कोशिश कर रहे हैं:

त्रुटि: केवल-पढ़ने के लिए स्थान '* (पाठ + 2u)'

यह भी संकलन नहीं होगा। केवल पढ़ने के लिए लिखने की कोशिश करने वाली मेमोरी अब संरक्षित है क्योंकि हमने कंपाइलर को बताया है कि हमारा पॉइंटर केवल-पढ़ने के लिए मेमोरी है। बेशक, इसे केवल-पढ़ने वाली मेमोरी की ओर इंगित करने की आवश्यकता नहीं है , लेकिन यदि आप इसे पढ़ने-लिखने वाली मेमोरी (रैम) की ओर इंगित करते हैं, तो मेमोरी को कंपाइलर द्वारा लिखे जाने से भी संरक्षित किया जाएगा।

अंत में अंतिम रूप const char text[] = "This is some text";:। दोबारा, जैसे पहले इसके साथ []रैम में एक सरणी आवंटित करता है और इसमें डेटा को कॉपी करता है। हालाँकि, अब यह केवल-पढ़ने के लिए एक सरणी है। आप इसे नहीं लिख सकते क्योंकि इसके लिए सूचक को टैग किया गया है const। इसे लिखने का प्रयास करने का परिणाम है:

त्रुटि: केवल-पढ़ने के लिए स्थान '* (पाठ + 2u)'

तो, हम कहाँ हैं का एक त्वरित सारांश:

यह फ़ॉर्म पूरी तरह से अमान्य है और इसे हर कीमत पर टाला जाना चाहिए। यह सभी प्रकार की बुरी चीजों के लिए दरवाजा खोलता है:

char *text = "This is some text";

यदि आप डेटा को संपादन योग्य बनाना चाहते हैं तो यह फ़ॉर्म सही फ़ॉर्म है:

char text[] = "This is some text";

यदि आप ऐसे तार चाहते हैं जो संपादित नहीं किए जाएंगे, तो यह फ़ॉर्म सही रूप है:

const char *text = "This is some text";

यह फ़ॉर्म रैम के लिए बेकार है लेकिन इसके उपयोग हैं। हालांकि यह अभी के लिए भूल जाओ।

const char text[] = "This is some text";

6
यह ध्यान देने योग्य है कि Arduinos (कम से कम AVR- आधारित वाले) पर, स्ट्रिंग शाब्दिक रैम में रहते हैं, जब तक कि आप उन्हें मैक्रो के साथ घोषित नहीं करते हैं PROGMEM, PSTR()या F()। इस प्रकार, की const char text[]तुलना में अधिक रैम का उपयोग नहीं करता है const char *text
एडगर बोनट

Teensyduino और कई अन्य हाल ही में arduino-compatibles स्वचालित रूप से कोड स्थान में स्ट्रिंग शाब्दिक स्थान रखते हैं, इसलिए यह देखने के लायक है कि आपके बोर्ड पर F () की आवश्यकता है या नहीं।
क्रेग.फैड

@ क्रेग.फ़ाइएड में सामान्य एफ () की परवाह किए बिना उपयोग किया जाना चाहिए। जिन्हें "ज़रूरत" नहीं है, वे इसे एक साधारण (const char *)(...)कास्टिंग के रूप में परिभाषित करते हैं । कोई वास्तविक प्रभाव यदि बोर्ड को इसकी आवश्यकता नहीं है, लेकिन एक बड़ी बचत यदि आप अपने कोड को एक बोर्ड पर पोर्ट करते हैं जो करता है।
Majenko

5

मैकेंको के उत्कृष्ट जवाब पर विस्तार से बताने के लिए, एक अच्छा कारण है कि कंपाइलर आपको इस बारे में चेतावनी देता है। चलो एक परीक्षण स्केच बनाते हैं:

char *foo = "This is some text";
char *bar = "This is some text";

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo [2] = 'o';     // change foo only
  Serial.println (foo);
  Serial.println (bar);
  }  // end of setup

void loop ()
  {
  }  // end of loop

हमारे यहां दो वेरिएबल्स हैं, फू और बार। मैं सेटअप में उनमें से एक को संशोधित करता हूं (), लेकिन देखें कि परिणाम क्या है:

Thos is some text
Thos is some text

वे दोनों बदल गए!

वास्तव में अगर हम चेतावनियों को देखते हैं:

sketch_jul14b.ino:1: warning: deprecated conversion from string constant to char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to char*’

संकलक जानता है कि यह डोडी है, और यह सही है! इसका कारण यह है, कि संकलक (यथोचित) को उम्मीद है कि स्ट्रिंग स्थिरांक नहीं बदलते (क्योंकि वे स्थिरांक हैं)। इस प्रकार यदि आप "This is some text"अपने कोड में स्ट्रिंग को लगातार कई बार संदर्भित करते हैं तो उन सभी को समान मेमोरी आवंटित करने की अनुमति है । अब यदि आप एक को संशोधित करते हैं, तो आप उन सभी को संशोधित करते हैं!


पवित्र धुआं! कौन जानता होगा ... यह अभी भी नवीनतम ArduinoIDE संकलक के लिए सच है? मैं सिर्फ एक ESP32 पर यह कोशिश की और यह दोहराया गुरुमंत्रित त्रुटियों का कारण बनता है ।
नॉटआउट २२

@ not2qubit मैंने सिर्फ Arduino 1.8.9 पर परीक्षण किया और यह वहां सच है।
निक गैमन

चेतावनी एक कारण के लिए वहाँ हैं। इस बार मुझे मिला: चेतावनी: ISO C ++ मना करने वाले स्ट्रिंग को 'char ' में परिवर्तित करता है [-Witeite-strings] char bar = "यह कुछ टेक्स्ट है"; - फॉरबिड्स एक मजबूत शब्द है। चूँकि आपको ऐसा करने के लिए मना किया गया है, कंपाइलर चारों ओर से घिसने के लिए स्वतंत्र है और एक ही स्ट्रिंग को दो चर पर साझा करता है। निषिद्ध बातें मत करो ! (इसके अलावा, चेतावनियों को पढ़ें और खत्म करें)। :)
निक गैमन

तो अगर आप इस तरह के भद्दे कोड में आते हैं, और दिन बचाना चाहते हैं। विभिन्न स्ट्रिंग "स्थिरांक" का प्रारंभिक घोषणा *fooऔर *barउपयोग करना , ऐसा होने से रोक सकता है? इसके अलावा, यह कैसे किसी भी तार डाल नहीं से अलग है, जैसे :? char *foo;
not2qubit

1
विभिन्न स्थिरांक मदद कर सकते हैं, लेकिन व्यक्तिगत रूप से मैं वहां कुछ भी नहीं डालूंगा , और बाद में सामान्य तरीके से डेटा डालूंगा (उदाहरण के साथ new, strcpyऔर delete)।
निक गैमन

4

या तो एक स्ट्रिंग स्थिरांक को पास करने की कोशिश करना बंद कर दें जहां कोई फ़ंक्शन एक फ़ंक्शन लेता है char*, या फ़ंक्शन को बदलता है ताकि यह const char*बदले में ले जाए ।

स्ट्रिंग "यादृच्छिक स्ट्रिंग" की तरह हैं।


क्या एक पाठ "यादृच्छिक वर्ण" एक निरंतर चार जैसा है?
फेडरिको कोराज़ा

1
स्ट्रिंग शाब्दिक स्ट्रिंग स्थिरांक हैं।
इग्नासियो वाज़क्वेज़-अब्राम्स

3

उदाहरण:

void foo (char * s)
  {
  Serial.println (s);
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo ("bar");
  }  // end of setup

void loop ()
  {
  }  // end of loop

चेतावनी:

sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’

फ़ंक्शन fooएक चार * की उम्मीद करता है (जो इसे संशोधित कर सकता है) लेकिन आप एक स्ट्रिंग शाब्दिक पारित कर रहे हैं, जिसे संशोधित नहीं किया जाना चाहिए।

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


समाधान: बनाओ फू ले एक कास्ट चार *:

void foo (const char * s)
  {
  Serial.println (s);
  }

मुझे नहीं मिला। क्या आपका मतलब संशोधित नहीं किया जा सकता है?

C (और C ++) के पुराने संस्करण आपको ऊपर दिए गए मेरे उदाहरण की तरह कोड लिखते हैं। आप एक फ़ंक्शन (जैसे foo) बना सकते हैं, जो आपके द्वारा नीचे जाने वाली किसी चीज़ को प्रिंट करता है, और फिर एक शाब्दिक स्ट्रिंग (उदाहरण के लिए foo ("Hi there!");) को पास करता है ।

हालाँकि, एक फ़ंक्शन जो char *एक तर्क के रूप में लेता है, उसे अपने तर्क को संशोधित करने की अनुमति है (यानी Hi there!इस मामले में संशोधित करें)।

आपने लिखा हो सकता है, उदाहरण के लिए:

void foo (char * s)
  {
  Serial.println (s);
  strcpy (s, "Goodbye");
  }

दुर्भाग्य से, एक शाब्दिक नीचे पारित करके, आपने अब उस शाब्दिक रूप से संशोधित किया है ताकि "हाय वहाँ!" अब "अलविदा" है जो अच्छा नहीं है। वास्तव में यदि आप एक लंबी स्ट्रिंग में कॉपी करते हैं, तो आप अन्य चर को अधिलेखित कर सकते हैं। या, कुछ कार्यान्वयनों पर आपको एक एक्सेस उल्लंघन मिलेगा क्योंकि "हाय वहाँ!" शायद केवल पढ़ने के लिए (संरक्षित) रैम में रखा गया है।

इसलिए संकलक-लेखक धीरे-धीरे इस उपयोग को चित्रित कर रहे हैं , ताकि जिन कार्यों से आप शाब्दिक रूप से गुजरते हैं, उन्हें इस तर्क के रूप में घोषित करना चाहिए const


क्या यह एक समस्या है अगर मैं एक पॉइंटर का उपयोग नहीं करता हूं?
फेडेरिको कोराज़ा

किस तरह की समस्या? यह विशेष चेतावनी एक स्ट्रिंग स्थिरांक को चार * पॉइंटर में बदलने के बारे में है। क्या आप विस्तार से समझा सकते हैं?
निक गैमन

@ निक: आपका क्या मतलब है "(..) आप एक स्ट्रिंग शाब्दिक पारित कर रहे हैं, जिसे संशोधित नहीं किया जाना चाहिए"। मुझे नहीं मिला। क्या आपका मतलब can notसंशोधित है?
मैड्स स्केजर्न

मैंने अपना उत्तर संशोधित कर दिया। मजेंको ने अपने जवाब में इनमें से अधिकांश बिंदुओं को कवर किया।
निक गैमन

1

मेरे पास यह संकलन त्रुटि है:

TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
   if(Serial.find(TIME_HEADER)) {

                         ^

कृपया इस पंक्ति को बदलें:
#define TIME_HEADER "T" // Header tag for serial time sync message

इस लाइन के साथ:
#define TIME_HEADER 'T' // Header tag for serial time sync message

और संकलन अच्छी तरह से चला जाता है।


3
यह परिवर्तन अक्षर कैपिटल T के लिए ASCII कोड के मान के साथ एक वर्ण स्ट्रिंग "T" से एकल वर्ण में परिभाषित करता है।
16
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.