it-swarm.com.de

Kann man eine Zeichenkette in C ändern?

Ich habe ein paar Stunden mit allen möglichen C-Tutorials und Büchern zu Zeigern zu kämpfen gehabt, aber was ich wirklich wissen möchte, ist, ob es möglich ist, einen Zeichenzeiger zu ändern, sobald er erstellt wurde.

Das habe ich versucht:

char *a = "This is a string";
char *b = "new string";

a[2] = b[1]; // Causes a segment fault

*b[2] = b[1]; // This almost seems like it would work but the compiler throws an error.

Gibt es eine Möglichkeit, die Werte in den Strings und nicht die Zeigeradressen zu ändern?

Vielen Dank

BEARBEITEN:

Vielen Dank an alle für Ihre Antworten. Das macht jetzt mehr Sinn. Es macht vor allem Sinn, warum es manchmal gut funktioniert hat und manchmal nicht. Manchmal habe ich einen Zeichenzeiger und manchmal ein Zeichenarray übergeben (das Zeichenarray funktionierte gut).

57
John Baker

Wenn Sie einen "String" in Ihren Quellcode schreiben, wird dieser direkt in die ausführbare Datei geschrieben, da dieser Wert zur Kompilierzeit bekannt sein muss (es gibt Tools, mit denen die Software auseinandergezogen werden kann und alle reinen Textzeichenfolgen darin gesucht werden können). Wenn Sie char *a = "This is a string" schreiben, befindet sich die Position von "Dies ist eine Zeichenfolge" in der ausführbaren Datei und die Position, auf die ein Punkt zeigt, in der ausführbaren Datei. Die Daten im ausführbaren Image sind schreibgeschützt.

Was Sie tun müssen (wie die anderen Antworten gezeigt haben), ist das Erstellen des Speichers an einem Ort, der nicht schreibgeschützt ist - auf dem Heap oder im Stack-Frame. Wenn Sie ein lokales Array deklarieren, wird für jedes Element dieses Arrays Platz auf dem Stack geschaffen, und das String-Literal (das in der ausführbaren Datei gespeichert ist) wird in diesen Space im Stack kopiert.

char a[] = "This is a string";

sie können diese Daten auch manuell kopieren, indem Sie Speicherplatz auf dem Heap reservieren und anschließend mit strcpy() ein Zeichenfolgenliteral in diesen Bereich kopieren.

char *a = malloc(256);
strcpy(a, "This is a string");

Wenn Sie mit malloc() Speicherplatz zuweisen, denken Sie daran, free() aufzurufen, wenn Sie damit fertig sind (lesen Sie: Speicherverlust).

Grundsätzlich müssen Sie nachverfolgen, wo sich Ihre Daten befinden. Immer wenn Sie eine Zeichenfolge in Ihre Quelle schreiben, ist diese Zeichenfolge schreibgeschützt (andernfalls würden Sie möglicherweise das Verhalten der ausführbaren Datei ändern. Stellen Sie sich vor, Sie schreiben char *a = "hello"; und änderten dann a[0] in 'c'.) Andernorts wurde printf("hello"); Wenn Sie das erste Zeichen von "hello" ändern möchten, und Ihr Compiler es nur einmal gespeichert hat (sollte), dann würde printf("hello");cello ausgeben.

124
Carson Myers

Nein, Sie können ihn nicht ändern, da die Zeichenfolge im Nur-Lese-Speicher gespeichert werden kann. Wenn Sie es ändern möchten, können Sie stattdessen ein Array verwenden, z.

char a[] = "This is a string";

Alternativ können Sie Speicher verwenden, indem Sie malloc verwenden, z.

char *a = malloc(100);
strcpy(a, "This is a string");
free(a); // deallocate memory once you've done
20

Viele Leute sind verwirrt über den Unterschied zwischen char * und char [] in Verbindung mit String-Literalen in C. Wenn Sie schreiben:

char *foo = "hello world";

... Sie zeigen foo tatsächlich auf einen konstanten Speicherblock (was der Compiler mit "Hallo Welt" in diesem Fall macht, ist implementierungsabhängig.)

Verwenden Sie stattdessen char [], um dem Compiler mitzuteilen, dass Sie ein Array erstellen und dieses mit dem Inhalt "hallo world" füllen möchten. foo ist der Zeiger auf den ersten Index des Char-Arrays. Beide sind Zeichenzeiger, aber nur char [] zeigt auf einen lokal zugewiesenen und veränderbaren Speicherblock.

8
Jeff Ober

Der Speicher für a & b wird von Ihnen nicht zugewiesen. Der Compiler kann einen schreibgeschützten Speicherplatz zum Speichern der Zeichen auswählen. Wenn Sie also versuchen, dies zu ändern, kann dies zu einem Fehler führen. Deshalb schlage ich vor, dass Sie selbst ein Zeichenfeld erstellen. So etwas wie: char a[10]; strcpy(a, "Hello");

6
Naveen

Es scheint, als wäre Ihre Frage beantwortet worden, aber Sie fragen sich vielleicht, warum char * a = "String" im Nur-Lese-Speicher gespeichert wird. Nun, es ist vom c99-Standard eigentlich undefiniert, aber die meisten Compiler wählen dies für Instanzen wie:

printf("Hello, World\n");

Standard c99 (pdf) [Seite 130, Abschnitt 6.7.8]:

Die Erklärung:

char s[] = "abc", t[3] = "abc";

definiert "plain" char-Array-Objekte s und t, deren Elemente mit Zeichenkettenliteralen ..__ initialisiert werden. Diese Deklaration ist identisch mit char 

s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

Der Inhalt der Arrays ist modifizierbar. Zum anderen die Erklärung

char *p = "abc";

definiert p mit dem Typ "Zeiger auf Char" und initialisiert es so, dass es auf ein Objekt mit dem Typ "Array von Char" der Länge 4 verweist, dessen Elemente mit einem Zeichenkettenliteral initialisiert werden. Wenn versucht wird, mit p den Inhalt des Arrays zu ändern, ist das Verhalten undefiniert.

3
Sweeney

Sie können auch strdup verwenden:

   The strdup() function returns a pointer to a new string which is a duplicate of the string  s.
   Memory for the new string is obtained with malloc(3), and can be freed with free(3).

Für dein Beispiel:

char *a = strdup("stack overflow");
2
Maxime Chéramy

Alle sind gute Antworten, die erklären, warum Sie keine String-Literale ändern können, weil sie schreibgeschützt sind. Wenn jedoch Push drängt, gibt es eine Möglichkeit, dies zu tun. Schauen Sie sich dieses Beispiel an:

#include <sys/mman.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int take_me_back_to_DOS_times(const void *ptr, size_t len);

int main()
{
    const *data = "Bender is always sober.";
    printf("Before: %s\n", data);
    if (take_me_back_to_DOS_times(data, sizeof(data)) != 0)
        perror("Time machine appears to be broken!");
    memcpy((char *)data + 17, "drunk!", 6);
    printf("After: %s\n", data);

    return 0;
}

int take_me_back_to_DOS_times(const void *ptr, size_t len)
{
    int pagesize;
    unsigned long long pg_off;
    void *page;

    pagesize = sysconf(_SC_PAGE_SIZE);
    if (pagesize < 0)
        return -1;
    pg_off = (unsigned long long)ptr % (unsigned long long)pagesize;
    page = ((char *)ptr - pg_off);
    if (mprotect(page, len + pg_off, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
        return -1;
    return 0;
}

Ich habe dies als Teil von meine etwas tieferen Gedanken über const-correctness geschrieben, was Sie vielleicht interessant finden würde (hoffe ich :)).

Ich hoffe es hilft. Viel Glück!

1
user405725

Sie müssen die Zeichenfolge in einen anderen, nicht schreibgeschützten Speicherpuffer kopieren und dort ändern. Verwenden Sie strncpy () zum Kopieren des Strings, strlen () zum Ermitteln der Stringlänge, malloc () und free () zum dynamischen Zuweisen eines Puffers für den neuen String.

Zum Beispiel (C++ wie Pseudocode):

int stringLength = strlen( sourceString );
char* newBuffer = malloc( stringLength + 1 );

// you should check if newBuffer is 0 here to test for memory allocaton failure - omitted

strncpy( newBuffer, sourceString, stringLength );
newBuffer[stringLength] = 0;

// you can now modify the contents of newBuffer freely

free( newBuffer );
newBuffer = 0;
0
sharptooth
char *a = "stack overflow";
char *b = "new string, it's real";
int d = strlen(a);

b = malloc(d * sizeof(char));
b = strcpy(b,a);
printf("%s %s\n", a, b);
0
luca