Co oznacza ten błąd? Nie mogę tego rozwiązać w żaden sposób.
ostrzeżenie: przestarzała konwersja stałej stałej na „char *” [-Wwrite-strings]
programming
arduino-ide
c
Federico Corazza
źródło
źródło
Odpowiedzi:
Jak zwykle, przekażę trochę podstawowych informacji technicznych na temat przyczyn i przyczyn tego błędu.
Sprawdzę cztery różne sposoby inicjowania ciągów C i zobaczę, jakie są między nimi różnice. Oto cztery omawiane sposoby:
Teraz chcę zmienić trzecią literę „i” na „o”, aby „Thos to jakiś tekst”. Można to we wszystkich przypadkach (można by pomyśleć) osiągnąć poprzez:
Teraz spójrzmy na to, co robi każdy sposób deklarowania łańcucha i jak to
text[2] = 'o';
zdanie wpłynie na różne rzeczy.Najpierw najczęściej postrzegane sposób:
char *text = "This is some text";
. Co to dosłownie znaczy? Cóż, w C oznacza dosłownie „Utwórz zmienną o nazwie,text
która jest wskaźnikiem odczytu i zapisu do tego literału łańcuchowego, który jest przechowywany w przestrzeni tylko do odczytu (kodu).”. Jeśli masz włączoną opcję-Wwrite-strings
, otrzymasz ostrzeżenie, jak pokazano w powyższym pytaniu.Zasadniczo oznacza to „Ostrzeżenie: Próbowano utworzyć zmienną, która jest odczytem i zapisem, wskazuje na obszar, do którego nie można pisać”. Jeśli spróbujesz, a następnie ustawisz trzeci znak na „o”, w rzeczywistości próbowałbyś napisać w obszarze tylko do odczytu i nic nie będzie przyjemne. Na tradycyjnym komputerze z systemem Linux, który powoduje:
Teraz druga:
char text[] = "This is some text";
. Dosłownie w języku C oznacza to „Utwórz tablicę typu„ char ”i zainicjuj ją danymi„ To jest tekst \ 0 ”. Rozmiar tablicy będzie wystarczająco duży, aby przechowywać dane”. Tak więc faktycznie przydziela pamięć RAM i kopiuje do niej wartość „To jest tekst \ 0” w czasie wykonywania. Bez ostrzeżeń, bez błędów, całkowicie poprawne. I właściwy sposób, aby to zrobić, jeśli chcesz móc edytować dane . Spróbujmy uruchomić polecenietext[2] = 'o'
:Działa idealnie. Dobry.
Teraz trzeci sposób:
const char *text = "This is some text";
. Znów dosłowne znaczenie: „Utwórz zmienną o nazwie„ tekst ”, która jest wskaźnikiem tylko do odczytu dla tych danych w pamięci tylko do odczytu.”. Pamiętaj, że zarówno wskaźnik, jak i dane są teraz tylko do odczytu. Bez błędów, bez ostrzeżeń. Co się stanie, jeśli spróbujemy uruchomić nasze polecenie testowe? Nie możemy. Kompilator jest teraz inteligentny i wie, że próbujemy zrobić coś złego:Nawet się nie skompiluje. Próba zapisu do pamięci tylko do odczytu jest teraz chroniona, ponieważ powiedzieliśmy kompilatorowi, że naszym wskaźnikiem jest pamięć tylko do odczytu. Oczywiście nie musi to wskazywać na pamięć tylko do odczytu, ale jeśli wskażesz na pamięć do odczytu i zapisu (RAM), pamięć ta będzie nadal chroniona przed zapisaniem przez kompilator.
Wreszcie ostatnia forma:
const char text[] = "This is some text";
. Ponownie, podobnie jak poprzednio[]
, przydziela tablicę w pamięci RAM i kopiuje do niej dane. Jednak teraz jest to tablica tylko do odczytu. Nie możesz do niego pisać, ponieważ wskaźnik do niego jest oznaczony jakoconst
. Próba napisania do niego powoduje:Krótkie podsumowanie tego, gdzie jesteśmy:
Ten formularz jest całkowicie nieprawidłowy i należy go unikać za wszelką cenę. Otwiera drzwi do wszelkiego rodzaju złych rzeczy:
Ten formularz jest odpowiedni, jeśli chcesz, aby dane były edytowalne:
Ten formularz jest odpowiedni, jeśli chcesz ciągów, które nie będą edytowane:
Ta forma wydaje się marnować pamięć RAM, ale ma swoje zastosowania. Na razie jednak najlepiej o tym zapomnij.
źródło
PROGMEM
,PSTR()
alboF()
. Dlategoconst char text[]
nie zużywa więcej pamięci RAM niżconst char *text
.(const char *)(...)
castingu. Brak rzeczywistego efektu, jeśli płyta go nie potrzebuje, ale duża oszczędność, jeśli następnie przeniesiesz swój kod do płyty, która tego potrzebuje.Aby rozwinąć doskonałą odpowiedź Makenko, istnieje dobry powód, dla którego kompilator ostrzega o tym. Zróbmy szkic testowy:
Mamy tutaj dwie zmienne, foo i bar. Zmieniam jeden z tych w setup (), ale widzę, jaki jest wynik:
Oni obaj dostał zmieniło!
W rzeczywistości, jeśli spojrzymy na ostrzeżenia, zobaczymy:
Kompilator wie, że jest to podejrzane i ma rację! Powodem tego jest to, że kompilator (rozsądnie) oczekuje, że stałe łańcuchowe się nie zmieniają (ponieważ są stałymi). Dlatego jeśli
"This is some text"
wielokrotnie odwołujesz się do stałej ciągu w swoim kodzie, możesz przydzielić tę samą pamięć do nich wszystkich. Teraz, jeśli zmodyfikujesz jeden, zmodyfikujesz wszystkie!źródło
*foo
i*bar
użycie różnych „ ciągów ” ciągów zapobiegnęłoby temu? W jaki sposób różni się to od niewprowadzania żadnych ciągów, takich jakchar *foo;
:?new
,strcpy
idelete
).Albo przestań próbować przekazać stałą ciągu, w którym funkcja przyjmuje wartość
char*
, albo zmień funkcję, aby zajęła wartośćconst char*
zamiast.Ciąg jak „ciąg losowy” to stałe.
źródło
Przykład:
Ostrzeżenie:
Funkcja
foo
oczekuje znaku * (który może zatem zmodyfikować), ale przekazujesz literał łańcuchowy, którego nie należy modyfikować.Kompilator ostrzega, aby tego nie robić. Nieaktualne może zmienić się z ostrzeżenia w błąd w przyszłej wersji kompilatora.
Rozwiązanie: Ustaw foo jako const char *:
Starsze wersje C (i C ++) pozwalają pisać kod tak jak mój przykład powyżej. Możesz stworzyć funkcję (jak
foo
), która wypisze coś, co do niej przekażesz, a następnie przekaże ciąg literalny (np.foo ("Hi there!");
)Jednak funkcja, która przyjmuje
char *
jako argument, może modyfikować swój argument (tj.Hi there!
W tym przypadku modyfikować ).Być może napisałeś na przykład:
Niestety, przekazując literał, potencjalnie zmodyfikowałeś teraz literał tak, aby „Cześć!” jest teraz „Do widzenia”, co nie jest dobre. W rzeczywistości, jeśli skopiowałeś dłuższy ciąg, możesz zastąpić inne zmienne. Lub w niektórych implementacjach może dojść do naruszenia zasad dostępu, ponieważ „Cześć!” mógł zostać umieszczony w pamięci RAM tylko do odczytu (chronionej).
Dlatego autorzy kompilatorów stopniowo przestają stosować to użycie, więc funkcje, do których przekazujesz literał, muszą zadeklarować ten argument jako
const
.źródło
can not
być zmodyfikowany?Mam ten błąd kompilacji:
Proszę zamienić ten wiersz:
#define TIME_HEADER "T" // Header tag for serial time sync message
z tą linią:
#define TIME_HEADER 'T' // Header tag for serial time sync message
i kompilacja idzie dobrze.
źródło