C ++ veraltete Konvertierung von String-Konstante in 'char *'

154

Ich habe eine Klasse mit einem private char str[256];

und dafür habe ich einen expliziten Konstruktor:

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

Ich nenne es als:

myClass obj("example");

Wenn ich dies kompiliere, erhalte ich die folgende Warnung:

veraltete Konvertierung von String-Konstante in 'char *'

Warum passiert dies?

mkamthan
quelle
1
Sie sollten strncpy(str, func, 255)anstelle strcpy(str, func)für eine sicherere Kopie verwenden. Und vergessen Sie dann nicht, das '\ 0' am Ende des Strings hinzuzufügen, da strncpy es nicht hinzufügt.
Patrice Bernassola
2
Noch sicherer zu sagen "strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Warren Young
3
Ich glaube nicht, dass das oben Gesagte die Warnung gibt, die Sie zitiert haben, obwohl ich mir sicher bin, dass dies ein ziemlich ähnlicher Code wäre. Um aussagekräftige Antworten zu erhalten, sollten Sie ein minimales Kompilierungsbeispiel veröffentlichen, das die Warnung erzeugt.
sbi
3
@Patrice, Warren: Verwenden Sie nicht strncpy, es ist keine sicherere Version von strcpy. Verwenden Sie strcpy_s (oder implementieren Sie es erneut).
Steve Jessop
Ich habe das Problem, es zeigt nur diese Probleme für einen -X86-Build und nicht für normale Solaris- oder ARM- (Ziel-) Builds, daher ignoriere ich dies. Es konnte immer noch kein Fix gefunden werden, da normalerweise auch für meinen Beispielcode keine Warnung angezeigt wird. Danke euch allen!
Mkamthan

Antworten:

144

Dies ist eine Fehlermeldung, die angezeigt wird, wenn Sie eine Situation wie die folgende haben:

char* pointer_to_nonconst = "string literal";

Warum? Nun, C und C ++ unterscheiden sich in der Art des String-Literal. In C ist der Typ ein Array von char und in C ++ ein konstantes Array von char. In jedem Fall dürfen Sie die Zeichen des String-Literal nicht ändern, daher ist die Konstante in C ++ keine wirkliche Einschränkung, sondern eher eine Typensicherheitssache. Eine Umstellung von const char*auf char*ist aus Sicherheitsgründen in der Regel ohne explizite Besetzung nicht möglich. Aus Gründen der Abwärtskompatibilität mit C ermöglicht die Sprache C ++ jedoch weiterhin die Zuweisung eines Zeichenfolgenliteral zu a char*und gibt eine Warnung aus, dass diese Konvertierung veraltet ist.

Irgendwo fehlen Ihnen ein oder mehrere consts in Ihrem Programm, um die Richtigkeit der Konstanten zu gewährleisten. Der Code, den Sie uns gezeigt haben, ist jedoch nicht das Problem, da diese Art der veralteten Konvertierung nicht durchgeführt wird. Die Warnung muss von einem anderen Ort gekommen sein.

sellibitze
quelle
17
Angesichts der Ansicht und der Abstimmungen zu dieser Frage ist es bedauerlich, dass das OP niemals Code bereitgestellt hat, der das Problem tatsächlich demonstriert.
Shafik Yaghmour
1
Sie können das Problem mit dem OP-Code reproduzieren, indem Sie das constaus dem MyClassKonstruktor löschen. Anschließend können Sie es beheben, indem Sie die constRückseite hinzufügen .
Theodore Murdock
145

Die Warnung:

veraltete Konvertierung von String-Konstante in 'char *'

wird gegeben, weil Sie irgendwo (nicht in dem Code, den Sie gepostet haben) etwas tun wie:

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

Das Problem ist, dass Sie versuchen, ein Zeichenfolgenliteral (mit Typ const char[]) in zu konvertieren char*.

Sie können a in konvertieren const char[], const char*da das Array in den Zeiger zerfällt. Sie machen jedoch eine veränderbare Konstante.

Diese Konvertierung ist wahrscheinlich aus Gründen der C-Kompatibilität zulässig und gibt nur die erwähnte Warnung aus.

fnieto - Fernando Nieto
quelle
96

Als Antwort nein. 2 von fnieto - Fernando Nieto beschreibt klar und korrekt, dass diese Warnung gegeben wird, weil Sie irgendwo in Ihrem Code (nicht in dem von Ihnen geposteten Code) etwas tun wie:

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

Wenn Sie Ihren Code jedoch auch vor Warnungen schützen möchten, nehmen Sie einfach entsprechende Änderungen an Ihrem Code vor:

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

Das heißt, wirf einfach die stringKonstante auf (char *).

sactiw
quelle
17
Alternativ machen Sie die Funktion: void foo (const char * str)
Caprooja
3
@Caprooja Ja, wenn Sie den Parameter als 'Zeiger auf eine Konstante' deklarieren, funktioniert dies auch in diesem Fall. Mit dieser Änderung kann der Benutzer den an der Adresse gespeicherten Wert jedoch nicht mehr ändern / neu zuweisen, indem er den Zeiger 'str' verwendet, den der Benutzer möglicherweise im Implementierungsteil ausführt. Darauf sollten Sie also achten.
Sactiw
1
@sactiw Gibt es Gründe, so zu bleiben, void foo(char* str)wie es ist? Ich dachte , wir können nicht modity strin footrotzdem, auch der Parameter als nicht-const geschrieben.
kgf3JfUtW
37

Es gibt 3 Lösungen:

Lösung 1:

const char *x = "foo bar";

Lösung 2:

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

Lösung 3:

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

Arrays können auch anstelle von Zeigern verwendet werden, da ein Array bereits ein konstanter Zeiger ist.

Anilbey
quelle
7
Für Lösung 3 gibt es strdup. Im Gegensatz zu Ihrem Code wird Platz für das abschließende NUL-Zeichen zugewiesen und die Zuordnung nicht überschritten.
Ben Voigt
2
Lösung 2 ist zu vermeiden.
Leichtigkeitsrennen im Orbit
Tatsächlich kann Lösung 2 sein: char * x = static_cast <char *> ("foo bar") in C ++.
Kehe CAI
3
Anil werden Sie jemals die Kommentare in Ihre Antwort integrieren? Lösung 3 ist immer noch gefährlich falsch.
Leichtigkeitsrennen im Orbit
@LightnessRacesinOrbit Können Sie bitte eine Antwort geben? Ich verstehe nicht, warum Sie sagen, dass die Lösungen 2 und 3 vermieden werden sollen und gefährlich falsch sind.
Gladclef
4

Tatsächlich ist ein String-Konstanten-Literal weder ein const char * noch ein char *, sondern ein char []. Es ist ziemlich seltsam, aber in den c ++ - Spezifikationen niedergeschrieben. Wenn Sie es ändern, ist das Verhalten undefiniert, da der Compiler es möglicherweise im Codesegment speichert.

dan ionescu
quelle
5
Ich würde sagen, es ist const char [], weil man es als Wert nicht ändern kann.
fnieto - Fernando Nieto
3

Vielleicht können Sie dies versuchen:

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

foo("Hello")

Für mich geht das

Alen Lee
quelle
2

Ich löse dieses Problem, indem ich dieses Makro irgendwo am Anfang des Codes hinzufüge. Oder füge es hinzu <iostream>, hehe.

 #define C_TEXT( text ) ((char*)std::string( text ).c_str())
TWOPIR
quelle
8
"Oder füge es in <iostream> hinzu." Was?!
Leichtigkeitsrennen im Orbit
Es gab ", hehe", das aus irgendeinem Grund herausgeschnitten wurde (impliziert, dass es ein Witz war)
Someguynamedpie
C_TEXTist in Ordnung für einen Funktionsaufruf ( foo(C_TEXT("foo"));), schreit aber nach undefiniertem Verhalten, wenn der Wert in einer Variablen gespeichert ist wie char *x = C_TEXT("foo");- jede Verwendung von x(abgesehen von der Zuweisung) ist undefiniertes Verhalten, da der Speicher, auf den es zeigt, freigegeben wurde.
Martin Bonner unterstützt Monica
1

Ein Grund für dieses Problem (das noch schwerer zu erkennen ist als das Problem, mit char* str = "some string"dem andere erklärt haben) ist, dass Sie es verwenden constexpr.

constexpr char* str = "some string";

Es scheint, als würde es sich ähnlich verhalten const char* strund daher keine Warnung auslösen, wie es zuvor char*aufgetreten ist, sondern es verhält sich stattdessen wie char* const str.

Einzelheiten

Konstantenzeiger und Zeiger auf eine Konstante. Der Unterschied zwischen const char* strund char* const strkann wie folgt erklärt werden.

  1. const char* str: Deklarieren Sie str als Zeiger auf ein const char. Dies bedeutet, dass die Daten, auf die dieser Zeiger zeigt, konstant sind. Der Zeiger kann geändert werden, aber jeder Versuch, die Daten zu ändern, würde einen Kompilierungsfehler auslösen.
    1. str++ ;: GÜLTIG . Wir ändern den Zeiger und nicht die Daten, auf die verwiesen wird.
    2. *str = 'a';: UNGÜLTIG . Wir versuchen, die Daten, auf die verwiesen wird, zu ändern.
  2. char* const str: Deklarieren Sie str als konstanten Zeiger auf char. Dies bedeutet, dass der Punkt jetzt konstant ist, die Daten, auf die auch gezeigt wird, jedoch nicht. Der Zeiger kann nicht geändert werden, aber wir können die Daten mit dem Zeiger ändern.
    1. str++ ;: UNGÜLTIG . Wir versuchen, die Zeigervariable zu ändern, die eine Konstante ist.
    2. *str = 'a';: GÜLTIG . Wir versuchen, die Daten, auf die verwiesen wird, zu ändern. In unserem Fall verursacht dies keinen Kompilierungsfehler, sondern einen Laufzeitfehler , da die Zeichenfolge höchstwahrscheinlich in einen schreibgeschützten Abschnitt der kompilierten Binärdatei verschoben wird. Diese Aussage wäre sinnvoll, wenn wir dynamisch Speicher zugewiesen hätten, z. char* const str = new char[5];.
  3. const char* const str: Deklarieren Sie str als const-Zeiger auf ein const-Zeichen. In diesem Fall können wir weder den Zeiger noch die Daten ändern, auf die verwiesen wird.
    1. str++ ;: UNGÜLTIG . Wir versuchen, die Zeigervariable zu ändern, die eine Konstante ist.
    2. *str = 'a';: UNGÜLTIG . Wir versuchen, die Daten zu ändern, auf die dieser Zeiger zeigt, der ebenfalls konstant ist.

In meinem Fall war das Problem, dass ich erwartet hatte, mich so constexpr char* strzu verhalten const char* str, und nichtchar* const str , da es visuell näher an dem ersteren zu sein scheint.

Außerdem unterscheidet sich die für generierte Warnung constexpr char* str = "some string"geringfügig von char* str = "some string".

  1. Compiler-Warnung für constexpr char* str = "some string":ISO C++11 does not allow conversion from string literal to 'char *const'
  2. Compiler-Warnung für char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.

Trinkgeld

Sie können C gibberish ish English converter verwenden , um CDeklarationen in leicht verständliche englische Anweisungen umzuwandeln und umgekehrt. Dies ist ein Ceinziges Tool und unterstützt daher keine Dinge (wie constexpr), die exklusiv für sind C++.

Sahil Singh
quelle
0

Ich habe auch das gleiche Problem. Und was ich einfach getan habe, ist nur const char * anstelle von char * hinzuzufügen. Und das Problem gelöst. Wie andere oben erwähnt haben, handelt es sich um einen kompatiblen Fehler. C behandelt Strings als char-Arrays, während C ++ sie als const char-Arrays behandelt.

dilantha111
quelle
0

Für das, was es wert ist, finde ich diese einfache Wrapper-Klasse hilfreich beim Konvertieren von C ++ - Zeichenfolgen in 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
quelle
-1

Im Folgenden wird die Lösung veranschaulicht: Weisen Sie Ihre Zeichenfolge einem variablen Zeiger auf ein konstantes Array von Zeichen zu (eine Zeichenfolge ist ein konstanter Zeiger auf ein konstantes Array von Zeichen - plus Längeninformationen):

#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
quelle