dize sabitinden 'char *' a kullanımdan kaldırıldı


16

Bu hata ne anlama geliyor? Hiçbir şekilde çözemiyorum.

uyarı: dize sabitinden 'char *' a kullanımdan kaldırıldı [-Wwrite-strings]


Bu soru Arduino değil StackOverflow üzerinde olmalıdır :)
Vijay Chavda

Yanıtlar:


26

Benim alışkanlık olduğu gibi, bu hatayı neden ve nerede içine arka plan teknik bilgi biraz vereceğim.

C dizelerini başlatmanın dört farklı yolunu inceleyeceğim ve aralarındaki farkların ne olduğunu göreceğim. Söz konusu dört yol şunlardır:

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";

Şimdi bunun için "i" harfini "Tho" bir metindir "haline getirmek için üçüncü harfi" o "olarak değiştirmek isteyeceğim. Bu, her durumda (düşünürsünüz) aşağıdakiler tarafından gerçekleştirilebilir:

text[2] = 'o';

Şimdi dizeyi bildirmenin her yolunun ne yaptığını ve bu text[2] = 'o';ifadenin bir şeyi nasıl etkileyeceğini inceleyelim.

İlk en sık görülen yol: char *text = "This is some text";. Bu kelimenin tam anlamıyla ne anlama geliyor? Aslında, C, kelimenin tam anlamıyla " textSalt okunur (kod) alanda tutulan bu dize değişmezine bir okuma-yazma işaretçisi olarak adlandırılan bir değişken oluşturun" anlamına gelir . Seçeneği -Wwrite-stringsetkinleştirdiyseniz, yukarıdaki soruda görüldüğü gibi bir uyarı alırsınız.

Temel olarak bu "Uyarı: Yazamadığınız bir alana okuma-yazma noktası olan bir değişken yapmaya çalıştınız" anlamına gelir. Üçüncü karakteri "o" olarak ayarlamayı denerseniz, aslında salt okunur bir alana yazmaya çalışırsınız ve işler hoş olmaz. Linux ile sonuçlanan geleneksel bir bilgisayarda:

Segmentasyon hatası

Şimdi ikinci bir: char text[] = "This is some text";. Kelimenin tam anlamıyla, C'de, "char" türünde bir dizi oluşturun ve "Bu bir metin \ 0" verileriyle başlatın. Dizinin boyutu, verileri depolayacak kadar büyük olacaktır ". Bu aslında RAM tahsis eder ve çalışma zamanında "Bu bir metin \ 0" değerini kopyalar. Uyarı yok, hata yok, mükemmel bir şekilde geçerli. Ve verileri düzenlemek istiyorsanız bunu yapmanın doğru yolu . Komutu çalıştırmayı deneyelim text[2] = 'o':

Thos bir metin

Mükemmel çalıştı. İyi.

Şimdi üçüncü yol: const char *text = "This is some text";. Tekrardan anlamı: "Bu metin için salt okunur bellekte salt okunur bir işaretçi olan " metin "adlı bir değişken oluşturun ." İşaretçinin ve verilerin artık salt okunur olduğunu unutmayın. Hata yok, uyarı yok. Test komutumuzu çalıştırmayı denersek ne olur? Pekala, yapamayız. Derleyici şimdi zeki ve kötü bir şey yapmaya çalıştığımızı biliyor:

hata: salt okunur konumun atanması '* (metin + 2u)'

Derlenmeyecek bile. Salt okunur belleğe yazmaya çalışmak artık korunuyor çünkü derleyiciye işaretçimizin salt okunur bellek olduğunu söyledik. Tabii ki, değil sahip salt okunur bellek işaret edilecek, ancak bellek (RAM) okuma-yazma işaret edin eğer hafıza hala derleyici tarafından yazılan olmaktan korunmuş olacaktır.

Nihayet son şekli: const char text[] = "This is some text";. Yine, daha önce olduğu gibi [], RAM'de bir dizi tahsis eder ve verileri ona kopyalar. Ancak, şimdi bu salt okunur bir dizidir. İşaretçi olarak etiketlendiği için yazamazsınız const. Yazmaya çalışmak şununla sonuçlanır:

hata: salt okunur konumun atanması '* (metin + 2u)'

Yani, nerede olduğumuzun kısa bir özeti:

Bu form tamamen geçersizdir ve her ne pahasına olursa olsun kaçınılmalıdır. Her türlü kötü şeyin kapısını açar:

char *text = "This is some text";

Verileri düzenlenebilir hale getirmek istiyorsanız bu form doğru formdur:

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

Düzenlenmeyecek dizeler istiyorsanız bu form doğru formdur:

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

Bu form RAM'i boşa harcıyor ama kullanımları var. En iyi şimdilik şimdilik unut.

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

6
Arduinos'ta (en azından AVR tabanlı olanlar), dize değişmezlerinin RAM gibi yaşadıklarını belirtmek gerekir PROGMEM, PSTR()ya da gibi bir makro ile bildirmedikçe F(). Böylece, const char text[]daha fazla RAM kullanmaz const char *text.
Edgar Bonet

Teensyduino ve daha birçok yeni arduino uyumlu, otomatik olarak dize değişmezlerini kod alanına yerleştirir, bu nedenle panonuzda F () gerekip gerekmediğini kontrol etmeye değer.
Craig.

@ Craig.Feied Genel olarak F () ne olursa olsun kullanılmalıdır. "İhtiyacı olmayanlar" bunu basit bir (const char *)(...)döküm olarak tanımlama eğilimindedir . Tahtanın ihtiyacı yoksa gerçek bir etkisi yoktur, ancak kodunuzu bir panoya bağlarsanız büyük bir tasarruf sağlar.
Majenko

5

Makenko'nun mükemmel cevabını detaylandırmak için, derleyicinin sizi bu konuda uyarmasının iyi bir nedeni var. Bir test çizimi yapalım:

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

Burada iki değişkenimiz var: foo ve bar. Birini değiştiriyorum setup () bunlardan , ama sonuç ne olduğunu görmek:

Thos is some text
Thos is some text

Onlar de değişti!

Aslında gördüğümüz uyarılara bakarsak:

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

Derleyici bunun tehlikeli olduğunu biliyor ve doğru! Bunun nedeni, derleyicinin (makul olarak) dize sabitlerinin değişmemesini beklemesidir (sabit oldukları için). Bu nedenle "This is some text", kodunuzda dize sabitine birçok kez başvurursanız , hepsine aynı belleği ayırmasına izin verilir . Şimdi birini değiştirirseniz, hepsini değiştirirsiniz!


Vay canına! Kim bilebilirdi ki ... Bu son ArduinoIDE derleyicileri için hala geçerli mi? Sadece bir ESP32 üzerinde denedim ve tekrarlanan GuruMeditation hatalarına neden oluyor .
not2qubit

@ not2qubit Ben sadece Arduino 1.8.9 üzerinde test ve orada doğrudur.
Nick Gammon

Uyarılar bir nedenden dolayı var. Bu sefer aldım: uyarı: ISO C ++ 'dize' sabit bir dize dönüştürme yasaklar [-Wwrite-strings] char bar = "Bu bazı metin"; - FORBIDS güçlü bir kelimedir. Bunu yapmanız yasak olduğu için, derleyici iki değişken üzerinde aynı dizgiyi paylaşabilir ve paylaşabilir. Yapmayın yasak şeyleri! (Ayrıca, uyarıları okuyun ve ortadan kaldırın). :)
Nick Gammon

Yani böyle boktan bir kodla karşılaşırsanız ve gün hayatta kalmak istiyorsanız. Bir ilk beyanı misiniz *foove *barkullanan farklı dize "sabitleri" , Bunu engellemek? : Ayrıca, nasıl gibi hiç bir dizeleri koyarak değil itibaren bu farklı char *foo;?
not2qubit

1
Farklı sabitler yardımcı olabilir, ama şahsen oraya hiçbir şey koymam ve daha sonra her zamanki gibi veri koymam (örneğin new, strcpyve ile delete).
Nick Gammon

4

Bir işlevin a aldığı yere bir dize sabiti geçirmeye çalışmayı bırakın char*ya da const char*yerine bir işlev alacak şekilde değiştirin .

"Random string" gibi dize sabitlerdir.


"Rastgele karakterler" gibi bir metin sabit bir karakter midir?
Federico Corazza

1
Dize değişmezleri dize sabitleridir.
Ignacio Vazquez-Abrams

3

Misal:

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

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

void loop ()
  {
  }  // end of loop

Uyarı:

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

İşlev foo bir char * (bu nedenle değiştirebileceği) bekler, ancak değiştirilmemesi gereken bir dizgi değişmezi iletiyorsunuz.

Derleyici bunu yapmamanız konusunda sizi uyarıyor. Kullanımdan kaldırıldığında, bir uyarıdan gelecekteki derleyici sürümünde hataya dönüşebilir.


Çözüm: Foo'nun bir const char almasını sağlayın *:

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

Anlamıyorum. Şunu musunuz edemez değiştirilebilir?

Eski C (ve C ++) sürümleri yukarıdaki örneğim gibi kod yazmanıza izin verir. fooAşağıya geçirdiğiniz bir şeyi yazdıran bir işlev (gibi ) yapabilir ve ardından değişmez bir dize (ör. foo ("Hi there!");) Geçirebilirsiniz.

Ancak char *argüman olarak alan bir fonksiyonun argümanını değiştirmesine izin verilir (yani Hi there!, bu durumda değiştirebilir ).

Yazmış olabilirsiniz, örneğin:

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

Ne yazık ki, bir değişmez değeri geçerek, potansiyel olarak şu değişmez değeri değiştirdiniz, böylece "Merhaba!" şimdi "Güle güle" ve bu iyi değil. Aslında daha uzun bir dizeye kopyaladıysanız, diğer değişkenlerin üzerine yazabilirsiniz. Veya bazı uygulamalarda erişim ihlali olur çünkü "Merhaba!" salt okunur (korumalı) RAM'e yerleştirilmiş olabilir.

Derleyici yazarlar bu kullanımı aşamalı olarak reddediyorlar , böylece bir değişmezi geçtiğiniz işlevler bu argümanı olarak bildirmelidir const.


İşaretçi kullanmam sorun olur mu?
Federico Corazza

Ne tür bir sorun? Bu özel uyarı, bir dize sabitini bir char * işaretçisine dönüştürmektir. Detaylandırabilir misin?
Nick Gammon

@Nick: Ne demek istiyorsun "(..) değişmez bir dizgi değişmez geçiyorsun". Anlamıyorum. can notDeğiştirilmeyi mi kastediyorsun ?
Mads Skjern

Cevabımı değiştirdim. Majenko bu noktaların çoğunu cevabında ele aldı.
Nick Gammon

1

Bu derleme hatası var:

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

                         ^

Lütfen bu satırı değiştirin:
#define TIME_HEADER "T" // Header tag for serial time sync message

bu çizgi ile:
#define TIME_HEADER 'T' // Header tag for serial time sync message

ve derleme iyi gidiyor.


3
Bu değişiklik, "T" harfini tek bir karakter dizesinden, büyük harf T için ASCII kodu değeri olan tek bir karaktere dönüştürür.
dlu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.