2009-03-15 12 views
2

Nachdem ich die FAQs und alles andere gelesen habe, bin ich immer noch verwirrt. Wenn ich einen char-Zeiger, der auf diese Weise initialisiert:Warum kann ich den Zeigern keine Werte zuweisen?

char *s = "Hello world!"

Der String ist im schreibgeschützten Speicher und ich kann es nicht so ändern:

*s = 'W'; 

zu machen „Wello Welt ! ". Das verstehe ich, aber ich kann nicht für mein Leben verstehen, wie man es NICHT schreibgeschützt macht. Muss ich ein Array anstelle eines Zeigers verwenden? Wie here?

Dies ist mein Code:

char *s = str; 
char *e = s; 
while (*e != '\0') 
e++; 
e--; 
char *temp; 
//Swop the string around 
while (s <= e) { 
    *temp = *s; 
    *s = *e; 
    *e = *temp; 
    e--; 
    s++; 
} 

Die Fehlermeldung ist nur ein Segmentierungsfehler. Entschuldigung im Voraus, wenn das eine wirklich blöde Frage ist.

Vielen Dank für die Hilfe. Nach der Einnahme von all Ihren Rat, ich diese:

void something(char * str) { 
    char *store = str; 
    char *s = new char[strlen(str) + 1]; //Allocate memory. Nice one. 
    strcpy(s, str); 
    char *e = new char[strlen(str) + 1]; 
    strcpy(e, str); 
    while (*e != '\0') 
     e++; 
    e--; 
    char temp; //no longer a pointer 
    while (s <= e) { 
     cout << *e; 
     temp = *s; 
     *s = *e; 
     *e = temp; 
     e--; 
     s++; 

    } 
    delete [] e; 
    delete [] s;   
} 

jedoch scheinen die Löschungen am Ende der Funktion ihre eigenen Zugriffsfehler zu verursachen. Warum?

Zum Zweck des Interesses: Die Fehler wurden aufgrund der Zugriffe auf die e und s Zeiger, nachdem sie inkrementiert wurden. Eine viel einfachere Lösung folgt daraus:

+0

Die Art, wie ich das lese, ist Ihr * s = 'W' nur ein Beispiel für etwas, von dem Sie wissen, dass Sie es nicht tun können? Es hat nichts mit dem unten stehenden Code zu tun, wozu Sie eigentlich Hilfe benötigen? Fragen Sie mich einfach, denn die meisten Antworten scheinen sich auf das erste Beispiel zu konzentrieren. – jalf

+0

Ja, ich weiß, dass ich das nicht tun kann :(Ich habe mich gefragt, wie ich das richtig machen soll. Ich habe alle Ratschläge implementiert, also vielleicht habe noch ein looksy? – pypmannetjies

+0

Mach was richtig? Ich bin immer noch ein wenig unklar, was deine Frage ist. Du versuchst eine Saite umzukehren, es scheint? Ich bin nur nicht sicher, ob das "* s = 'W'" Teil hatte irgendeine Relevanz für die Frage selbst. Auch wenn die Löschungen verursachen, segfolds, müssen Sie diese auch. – jalf

Antwort

6

Der einfachste Weg, um es zu ändern ist ein Array für Ihren Speicher zu erstellen, und kopieren Sie dann die Zeichenfolge hinein.

Zum Beispiel:

char buf[128]; 
const char *src = "Hello World"; 
strncpy(buf, src, 127); // one less - we always 0-terminate 
buf[127] = '\0'; 

// you can now modify buf 
buf[0] = 'W'; 

Der Grund Code nicht funktioniert, ist, dass Sie keine Speicher für die Kopie des Strings zugewiesen haben - Sie haben gerade einen zweiten Zeiger auf die gleiche Lese gemacht Nur Speicher. (Und dann versucht, es zu kopieren? Ich bin nicht ganz sicher, was der Rest des Codes tut.) Sie müssen irgendwo nicht-nur-lesen-Speicher abrufen, und es ist viel einfacher, die Standardbibliothek zu verwenden, um es zu kopieren diese neue Erinnerung, anstatt die Schleife selbst zu schreiben.

Im Fall, wenn Sie die Länge der Zeichenfolge vorher nicht wissen, können Sie auch malloc verwenden können (oder, noch besser, tun beantworten, was drschnz der sagt und verwenden neuen char []):

const char *src = "Hello world"; 
char *buf = malloc(strlen(src) + 1); // or = new char[strlen(src) + 1]; 
strcpy(buf, src); 
// you can now modify buf 
// later, you need to free it 
free(buf);        // or delete [] buf; 
auch

, wenn Sie C++ verwenden, können Sie nur verwenden, um eine std :: string:

std::string myString("Hello world"); 
myString[0] = "W"; 

Hoffnung, das hilft.

+0

'char * buf [128];' gibt ein Array von 128 Zeigern, nicht ein Array von 128 Zeichen, in Ihrem ersten Beispielblock. Entferne den Stern und du bist gut. –

+0

In Ihrem Beispiel gibt es einen Tippfehler: "char * buf [128];" sollte "char buf [128] sein;" (dh Sie haben ein Array von Zeigern auf Char's, anstatt eines Arrays von Char's) –

+0

Sie schlagen mich um 1 Sekunde>< –

7

Versuchen:

char src[] = "Hello world"; 
src[6]  = 'W'; 

-- // or 

char buffer[] = "Hello world"; 
char* src  = buffer; 
src[6]   = 'W'; 

Wenn Sie einen String in einen Puffer kopieren wollen, dann strcpy() oder strncpy()

char buffer[20]; 
char const* s = "Hello World" 

strcpy(s,buffer); 

Wenn Sie eine eigene Zeichenfolge Kopie schreiben muss, dann sollte es wie folgt aussehen:

char buffer[20]; 
char const* s = "Hello World"; 

// OK this is not the perfect solution but it is easy to read. 
for(int loop = 0;s[loop] != '\0';++loop) 
{ 
    buffer[loop] = s[loop]; 
} 
buffer[loop] = '\0'; 
+0

Sie sollten das const & und das int i innerhalb der for-Schleife überprüfen. Die Frage ist mit "C" markiert. –

+0

char const & s = "Hallo Welt"; // AFAIK das ist illegal (C oder C++). Sie können ein const char * nicht auf ein Zeichen const & (oder const char & or char const *) anwenden. Außerdem ist "loop" ein seltsamer Name für eine Variable. Ich schlage 'ich' vor. – strager

+0

Die Frage wurde ursprünglich mit C++ versehen. Jemand hat es geändert. – pypmannetjies

2

Der Zeiger ist nicht schreibgeschützt. (Die String-Daten selbst sind zwar, aber ein Zeiger, der auf sie zeigt, kann frei geändert werden.) Das Zuweisen eines Zeichens zu einem Zeiger führt jedoch nicht zu dem, was Sie erwarten.

Im Allgemeinen können Sie einem Punkt nur eine Adresse zuweisen. Sie können keine Werte zuweisen, nur die Adresse von Werten.

String-Literale (wie "Hallo Welt") sind die einzige Ausnahme, da Strings speziell sind. Wenn Sie einem Zeiger einen davon zuweisen, erhalten Sie einen Zeiger auf diesen String. Im Allgemeinen weisen Sie Adressen jedoch Zeigern zu.

Der andere Punkt ist, dass Zeichen in C++ integrale Datentypen sind. Sie können als ganze Zahlen ohne Gußteil behandelt werden. Ich kann int i = 'W' tun und der Compiler wird sich nicht beschweren.

Was passiert also, wenn Sie einem Zeiger 'W' zuweisen? Es nimmt 'W' als Integer-Wert an und nimmt an, dass dies eine Adresse ist. "W" hat den ASCII-Wert 127, so dass Sie den Zeiger so einstellen, dass er auf Adresse 127 zeigt, was keinen Sinn ergibt.

Ich sehe nicht, wie das mit Ihrem Code viel zu tun hat. Das Problem scheint dort zu sein, dass temp nicht auf gültige Daten zeigt. Sie deklarieren einen Zeiger, der auf eine nicht definierte Adresse zeigt. Und dann sagen Sie „, wo es auf die Punkte, ich möchte den Wert schreiben, dass s Punkte Die folgende Beschreibung ist etwas besser funktionieren.

char temp; // not a pointer. We want a character to store our temporary value in 
while (s <= e) { 
    temp = *s; // note, no * on temp. 
    *s = *e; 
    *e = temp; // note, no * on temp. 
    e--; 
    s++; 
} 

Wenn jedoch str verweist auf eine Stringliteral, wie‚Hallo Welt‘ , dann ist das nicht legal, weil die String-Daten selbst schreibgeschützt sind.Der Compiler kann es nicht erzwingen, aber dann haben Sie sich in undefiniertes Verhalten Land.Wenn Sie die Zeichenfolge ändern möchten, kopieren Sie es in ein lokaler Puffer, wie eine der anderen Antworten zeigte

Sie scheinen ein bisschen verwirrt über die Semantik von Zeigern.Vergeben Sie eine Adresse (oder etwas, das in eine Adresse umgewandelt werden kann, wie eine ganze Zahl) zu einem Zeiger Der Zeiger zeigt auf diese Adresse. Es ändert die angezeigten Daten nicht. Das Deklarieren eines Zeigers bedeutet nicht, dass er auf etwas Sinnvolles zeigt. Wenn Sie ein Zeichen speichern möchten, deklarieren Sie eine Zeichenvariable. Ein Zeiger speichert keine Daten, sondern nur Daten, die an anderer Stelle zugewiesen wurden.

bearbeiten Kommentare und Korrekturen zu Ihrem aktualisierten Code:

void something(const char * str) { // let the function take a pointer to a non-modifiable string, so add the const. Now it's clear that we're not allowed to modify the string itself, so we have to make a copy. 
    char *s = new char[strlen(str) + 1]; // Since the original string is const, we have to allocate a copy if we want to modify it - in C, you'd use malloc(strlen(str)) instead 
    strcpy(s, str); 
    char *e = s; // make e point to the start of the copied string (don't allocate two copies, since e and s are supposed to work on the same string 
    while (*e != '\0') { // add braces so it's clear where the loop starts and ends. 
     e++; 
    } 
    e--; 

    while (s <= e) { // the loop condition wouldn't work if s and e pointed to separate copies of the string 
     cout << *e; // why? I thought you just wanted to reverse the string in memory. Alternatively, if you just want to print out the string reversed, you don't need to do most of the rest of the loop body. In C, you'd use printf instead of *e 
     char temp = *s; // might as well declare the temp variable when you need it, and not before 
     *s = *e; 
     *e = temp; 
     e--; 
     s++; 

    } 
} 

nur als Referenz, und als Reaktion auf die Kommentare über C vs C++, hier ist, wie ich eine Funktion schreiben, würde eine Zeichenfolge umkehren in C++:

std::string revert_string(const std::string& str) { 
    return std::string(str.rbegin(), str.rend()); 
} 

Oder die Zeichenfolge in-Ort zurückzukehren:

std::string revert_string(const std::string& str) { 
    std::reverse(str.begin(), str.end()); 
} 
+0

Ich bin verwirrt :) Ich fange gerade mit C++ an, aber ich habe versucht, Temp früher zu ändern, und das hat das Problem nicht gelöst, also nahm ich an, dass es richtig war. Aber danke, es scheint jetzt zu funktionieren. – pypmannetjies

+0

Terminologie scheint hier verwirrt zu sein. Strings sind nicht besonders, wenn darauf hingewiesen wird. Es ist nur eines der Dinge, die Sie zur Laufzeit zum Zeiger greifen können. Der Compiler kann int nicht in int * ohne eine (böse) Umwandlung konvertieren. (Sie sollten ptrdiff_t oder was auch immer verwenden, wenn Sie das tun.) – strager

+0

Sorry, ich wollte nicht, dass Strings speziell waren, aber einfach, dass String-Literale etwas anders behandelt werden, und so sind sie das einzige, was wie ein Wert aussieht und kann einem Zeiger zugewiesen werden. Bei allem anderen müssen Sie explizit die Adresse des Wertes angeben. – jalf

1

Technisch gesehen, was Sie mehr haben, ist richtig so geschrieben:

const char *s = "Hello world!" 

Was Sie wollen, ist eigentlich haben, so etwas wie das:

char s[] = "Hello world!" 

Nach wenigen Zeilen, die Sie kann helfen, mehr Verständnis:

+0

Danke, aber ich weiß das. Ich muss wissen, ob es einen anderen Weg gibt, als ein Array zu verwenden. – pypmannetjies

+0

Was ist der Unterschied zwischen einem Zeiger, einer Zeichenfolge und einem Array? –

+0

@jleedev, Ein Zeiger zeigt auf etwas. Eine Zeichenfolge ist ein Array von Zeichen. Ein Array (der Datentyp) ist ein impliziter Zeiger (verhält sich nicht genau wie ein Zeiger) zu einem Array von Daten. – strager

0

Ihre Löschungen erzeugen Fehler, weil Sie den Zeiger geändert haben. Sie sollten den ursprünglichen Speicherort des neuen speichern und [] es löschen. Sie versuchen, einen Standort zu löschen, der nicht in der Zuordnungstabelle enthalten ist. Wenn Sie den Zeigerwert ändern möchten, machen Sie ein anderes Zeichen * temp = t; und benutze es, um über die Zeichenfolge zu iterieren.

0

Da ist # eine Funktion "strdup()", um eine Kopie eines Strings zu erstellen ... es stellt sicher, dass Sie nicht die "+1" in Ihrem malloc vergessen.

char* source = "Hello World"; 
char* dest = strdup(source); 
+0

Nicht Standard C (aber ist POSIX). Ich denke jedoch, dass das OP nach einer Konzeptlösung und nicht nach einer realen Lösung sucht. Er versucht zu verstehen, warum etwas so wirkt wie es tut. – strager

+0

Ok, es ist Posix. Trotzdem sollten Sie sich selbst schreiben, wenn es nicht verfügbar ist. Es ist nicht gut, den Code jedes Mal manuell zu schreiben. –