C ++ przestarzała konwersja ze stałej łańcuchowej na „char *”

154

Mam klasę z rozszerzeniem private char str[256];

i do tego mam jawnego konstruktora:

explicit myClass(const char *func)
{
    strcpy(str,func);
}

Nazywam to tak:

myClass obj("example");

Kiedy to kompiluję, otrzymuję następujące ostrzeżenie:

przestarzała konwersja ze stałej łańcuchowej na „char *”

Dlaczego to się dzieje?

mkamthan
źródło
1
Powinieneś używać strncpy(str, func, 255)zamiast, strcpy(str, func)aby uzyskać bezpieczniejszą kopię. A potem nie zapomnij dodać „\ 0” na końcu ciągu, ponieważ strncpy go nie dodaje.
Patrice Bernassola
2
Jeszcze bezpieczniej jest powiedzieć „strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Warren Young
3
Nie wydaje mi się, aby powyższe ostrzeżenie było zacytowane, chociaż jestem pewien, że byłby to całkiem podobny kod. Aby uzyskać sensowne odpowiedzi, powinieneś opublikować minimalny, kompilujący przykład, który generuje ostrzeżenie.
sbi
3
@Patrice, Warren: nie używaj strncpy, to nie jest bezpieczniejsza wersja strcpy. Użyj (lub ponownie zaimplementuj) strcpy_s.
Steve Jessop
Mam problem, pokazuje on tylko te problemy dla kompilacji -X86, a nie dla normalnych kompilacji solaris lub ARM (docelowy), więc to ignoruję. Wciąż nie można znaleźć poprawki, ponieważ nie wyświetla ona normalnie ostrzeżenia również dla mojego przykładowego kodu. Dziękuję wam wszystkim!
mkamthan

Odpowiedzi:

144

To jest komunikat o błędzie, który pojawia się w przypadku wystąpienia takiej sytuacji:

char* pointer_to_nonconst = "string literal";

Czemu? Cóż, C i C ++ różnią się typem literału ciągu. W C jest to tablica znaków, aw C ++ stała tablica znaków. W każdym razie nie możesz zmieniać znaków literału ciągu, więc stała w C ++ nie jest tak naprawdę ograniczeniem, ale raczej kwestią bezpieczeństwa typu. Konwersja z const char*na nie char*jest generalnie możliwa bez wyraźnej obsady ze względów bezpieczeństwa. Jednak w celu zapewnienia zgodności wstecznej z C język C ++ nadal umożliwia przypisywanie literału ciągu znaków do a char*i wyświetla ostrzeżenie o tym, że ta konwersja jest przestarzała.

Więc gdzieś brakuje jednego lub więcej constw swoim programie dla stałej poprawności. Ale kod, który nam pokazałeś, nie stanowi problemu, ponieważ nie wykonuje tego rodzaju przestarzałej konwersji. Ostrzeżenie musiało nadejść z innego miejsca.

sellibitze
źródło
17
Szkoda, biorąc pod uwagę pogląd i głosowanie w tej sprawie, że PO nigdy nie dostarczył kodu, który faktycznie demonstruje problem.
Shafik Yaghmour
1
Możesz odtworzyć problem z kodem OP, usuwając constz MyClasskonstruktora ... następnie możesz go naprawić, dodając odwrotną conststronę.
Theodore Murdock
145

Ostrzeżenie:

przestarzała konwersja ze stałej łańcuchowej na „char *”

jest podawana, ponieważ gdzieś robisz (nie w opublikowanym kodzie) coś takiego:

void foo(char* str);
foo("hello");

Problem polega na tym, że próbujesz przekonwertować literał ciągu (z typem const char[]) na char*.

Możesz przekonwertować a const char[]na, const char*ponieważ tablica rozpada się na wskaźnik, ale to, co robisz, polega na przekształcaniu zmiennej w stałą.

Ta konwersja jest prawdopodobnie dozwolona dla zgodności z C i daje tylko wspomniane ostrzeżenie.

fnieto - Fernando Nieto
źródło
96

Jako odpowiedź nie. 2 autorstwa fnieto - Fernando Nieto jasno i poprawnie opisuje, że to ostrzeżenie jest wyświetlane, ponieważ gdzieś w kodzie, który robisz (nie w kodzie, który opublikowałeś) coś takiego:

void foo(char* str);
foo("hello");

Jeśli jednak chcesz, aby Twój kod był również wolny od ostrzeżeń, po prostu wprowadź odpowiednią zmianę w kodzie:

void foo(char* str);
foo((char *)"hello");

Oznacza to, że po prostu rzuć stringstałą na (char *).

sactiw
źródło
17
Alternatywnie, utwórz funkcję: void foo (const char * str)
Caprooja Kwietnia
3
@Caprooja Yes Zadeklarowanie parametru jako „wskaźnika do stałej” również zadziała w tym przypadku. Ale dzięki tej zmianie użytkownik nie może już zmienić / ponownie przypisać wartości przechowywanej pod adresem za pomocą wskaźnika „str”, który użytkownik może wykonywać w części implementacyjnej. Więc to jest coś, na co warto zwrócić uwagę.
sactiw
1
@sactiw Czy są jakieś powody, aby zachować void foo(char* str)to, co jest? Myślałem, że nie możemy modity strw fookażdym razie, nawet parametr jest zapisywany jako const.
kgf3JfUtW
37

Istnieją 3 rozwiązania:

Rozwiązanie 1:

const char *x = "foo bar";

Rozwiązanie 2:

char *x = (char *)"foo bar";

Rozwiązanie 3:

char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");

Tablice mogą być również używane zamiast wskaźników, ponieważ tablica jest już stałym wskaźnikiem.

anilbey
źródło
7
W przypadku rozwiązania 3 jest strdup. W przeciwieństwie do twojego kodu, przydzieli miejsce na kończący znak NUL i nie przekroczy alokacji.
Ben Voigt
2
Należy unikać rozwiązania 2.
Wyścigi lekkości na orbicie
Właściwie rozwiązaniem 2 może być: char * x = static_cast <char *> ("foo bar") w C ++.
Kehe CAI
3
Czy kiedykolwiek włączysz komentarze do swojej odpowiedzi? Rozwiązanie 3 jest nadal niebezpiecznie błędne.
Wyścigi lekkości na orbicie
@LightnessRacesinOrbit Czy możesz podać odpowiedź? Nie rozumiem, dlaczego mówisz, że należy unikać rozwiązań 2 i 3 i są one niebezpiecznie błędne.
Gladclef
4

W rzeczywistości ciąg znaków nie jest ani const char * ani char *, ale char []. Jest to dość dziwne, ale zostało zapisane w specyfikacji c ++; Jeśli go zmodyfikujesz, zachowanie jest niezdefiniowane, ponieważ kompilator może przechowywać je w segmencie kodu.

dan ionescu
źródło
5
Powiedziałbym, że jest to const char [], ponieważ jako wartość r nie możesz jej modyfikować.
fnieto - Fernando Nieto
3

Może możesz spróbować tego:

void foo(const char* str) 
{
    // Do something
}

foo("Hello")

Mi to pasuje

Alen Lee
źródło
2

Rozwiązuję ten problem dodając to makro gdzieś na początku kodu. Albo dodaj <iostream>, hehe.

 #define C_TEXT( text ) ((char*)std::string( text ).c_str())
TWOPIR
źródło
8
„Lub dodaj to w <iostream>” Co ?!
Wyścigi lekkości na orbicie
Było „, hehe”, które z jakiegoś powodu zostało usunięte (sugerowało, że to był żart)
Someguynamedpie
C_TEXTjest w porządku dla wywołania funkcji ( foo(C_TEXT("foo"));), ale woła o niezdefiniowane zachowanie, jeśli wartość jest przechowywana w zmiennej, jak char *x = C_TEXT("foo");- każde użycie x(poza przypisaniem) jest niezdefiniowanym zachowaniem, ponieważ pamięć, na którą wskazuje, została zwolniona.
Martin Bonner wspiera Monikę
1

Przyczyną tego problemu (który jest jeszcze trudniejszy do wykrycia niż problem char* str = "some string"- który inni wyjaśniali) jest używanie constexpr.

constexpr char* str = "some string";

Wygląda na to, że zachowywałby się podobnie const char* str, a więc nie powodowałby ostrzeżenia, jak to miało miejsce wcześniej char*, ale zamiast tego zachowuje się jak char* const str.

Detale

Stały wskaźnik i wskaźnik do stałej. Różnicę między const char* stri char* const strmożna wyjaśnić w następujący sposób.

  1. const char* str: Zadeklaruj str jako wskaźnik do znaku stałego. Oznacza to, że dane, na które wskazuje ten wskaźnik, są stałe. Wskaźnik można modyfikować, ale każda próba modyfikacji danych spowodowałaby błąd kompilacji.
    1. str++ ;: WAŻNY . Modyfikujemy wskaźnik, a nie wskazywane dane.
    2. *str = 'a';: INVALID . Staramy się modyfikować wskazywane dane.
  2. char* const str: Zadeklaruj str jako stały wskaźnik do znaku. Oznacza to, że punkt jest teraz stały, ale wskazywane dane również nie. Wskaźnik nie może być modyfikowany, ale możemy modyfikować dane za pomocą wskaźnika.
    1. str++ ;: INVALID . Próbujemy zmodyfikować zmienną wskaźnika, która jest stałą.
    2. *str = 'a';: WAŻNY . Staramy się modyfikować wskazywane dane. W naszym przypadku nie spowoduje to błędu kompilacji, ale spowoduje błąd wykonania , ponieważ łańcuch najprawdopodobniej trafi do sekcji tylko do odczytu skompilowanego pliku binarnego. To stwierdzenie miałoby sens, gdybyśmy mieli dynamicznie alokowaną pamięć, np. char* const str = new char[5];.
  3. const char* const str: Zadeklaruj str jako stały wskaźnik do stałego znaku. W tym przypadku nie możemy ani zmienić wskaźnika, ani wskazanych danych.
    1. str++ ;: INVALID . Próbujemy zmodyfikować zmienną wskaźnika, która jest stałą.
    2. *str = 'a';: INVALID . Próbujemy zmodyfikować dane wskazywane przez ten wskaźnik, który również jest stały.

W moim przypadku chodziło o to, że spodziewałem constexpr char* strsię zachowywać się jak const char* str, a nie char* const str, ponieważ wizualnie wydaje się bliższy temu pierwszemu.

Ponadto ostrzeżenie wygenerowane dla constexpr char* str = "some string"jest nieco inne niż char* str = "some string".

  1. Ostrzeżenie kompilatora dla constexpr char* str = "some string":ISO C++11 does not allow conversion from string literal to 'char *const'
  2. Ostrzeżenie kompilatora dla char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.

Wskazówka

Możesz użyć konwertera C bełkot ↔ angielski, aby przekonwertować Cdeklaracje na łatwo zrozumiałe angielskie instrukcje i odwrotnie. Jest to Cjedyne narzędzie i dlatego nie obsługuje rzeczy (takich jak constexpr), które są wyłączne dla C++.

Sahil Singh
źródło
0

Ja też mam ten sam problem. Po prostu dodałem const char * zamiast char *. I problem został rozwiązany. Jak wspomnieli inni powyżej, jest to kompatybilny błąd. C traktuje łańcuchy jako tablice typu char, podczas gdy C ++ traktuje je jako tablice typu const char.

dilantha111
źródło
0

Ze względu na swoją wartość uważam, że ta prosta klasa opakowująca jest pomocna przy konwertowaniu ciągów C ++ na char *:

class StringWrapper {
    std::vector<char> vec;
public:
    StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
    }

    char *getChars() {
        return &vec[0];
    }
};
bremen_matt
źródło
-1

Poniższy przykład ilustruje rozwiązanie, przypisz swój ciąg do wskaźnika zmiennej do stałej tablicy znaków (ciąg jest stałym wskaźnikiem do stałej tablicy znaków - plus informacje o długości):

#include <iostream>

void Swap(const char * & left, const char * & right) {
    const char *const temp = left;
    left = right;
    right = temp;
}

int main() {
    const char * x = "Hello"; // These works because you are making a variable
    const char * y = "World"; // pointer to a constant string
    std::cout << "x = " << x << ", y = " << y << '\n';
    Swap(x, y);
    std::cout << "x = " << x << ", y = " << y << '\n';
}
Howard Lovatt
źródło