Можно ли изменить строку char в C?


Я боролся в течение нескольких часов со всеми видами учебников C и книг, связанных с указателями, но то, что я действительно хочу знать, можно ли изменить указатель char после его создания.

Это то, что я пробовал:

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.

Так есть ли способ, чтобы изменить значения внутри строки, а не указатель?

спасибо

EDIT:

спасибо всем за ваши ответы. Это имеет больше смысла сейчас. Это особенно имеет смысл, почему иногда он работает а иногда не работает. Потому что иногда я бы передал указатель char, а в других случаях массив char (массив char работал нормально).

9 55

9 ответов:

когда вы пишете "строку" в исходном коде, она записывается непосредственно в исполняемый файл, потому что это значение должно быть известно во время компиляции (есть инструменты, доступные для разделения программного обеспечения и поиска всех строк обычного текста в них). Когда вы пишете char *a = "This is a string", местоположение "это строка" в исполняемый файл, и расположение точек, в исполняемый. Данные в исполняемый файл только для чтения.

что вам нужно сделать (как и другие ответы есть указано) создает эту память в месте, которое не только читается-в куче или в кадре стека. Если вы объявляете локальный массив, то пространство создается в стеке для каждого элемента этого массива, а строковый литерал (который хранится в исполняемом файле) копируется в это пространство в стеке.

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

вы также можете скопировать эти данные вручную, выделив некоторую память в куче, а затем используя strcpy() скопировать строковый литерал в пространство.

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

всякий раз, когда вы выделяете пространство с помощью malloc() не забудьте позвонить free() когда вы закончите с ним (читай: утечка памяти).

в принципе, вы должны отслеживать, где ваши данные. Всякий раз, когда вы пишете строку в своем источнике, эта строка доступна только для чтения (в противном случае вы потенциально изменили бы поведение исполняемого файла-представьте, что вы написали char *a = "hello"; а потом изменил a[0] до 'c'. Потом где-то еще написал printf("hello");. Если бы Вы были разрешено изменять первый символ "hello", и ваш компилятор сохранил его только один раз (он должен), а затем printf("hello"); выводит cello!)

нет, вы не можете изменить его, так как строки могут храниться в памяти только для чтения. Если вы хотите изменить его, вы можете использовать массив вместо этого, например,

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

или поочередно, вы можете выделить память с помощью malloc например

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

многие люди путаются в разнице между char* и char[] в сочетании со строковыми литералами в C. Когда вы пишете:

char *foo = "hello world";

...вы фактически указываете foo на постоянный блок памяти (фактически, то, что компилятор делает с "hello world" в этом случае, зависит от реализации.)

использование char [] вместо этого сообщает компилятору, что вы хотите создать массив и заполнить его содержимым "hello world". Foo это указатель на первый индекс массива. Они оба являются указателями char, но только char[] будет указывать на локально выделенный и изменяемый блок памяти.

память для a & b не выделяется вами. Компилятор может выбрать место в памяти только для чтения для хранения символов. Поэтому, если вы попытаетесь изменить его, это может привести к ошибке seg. Поэтому я предлагаю вам создать массив символов самостоятельно. Что-то вроде: char a[10]; strcpy(a, "Hello");

похоже, что на ваш вопрос был дан ответ, но теперь вы можете задаться вопросом, почему char *a = "String" хранится в памяти только для чтения. Ну, на самом деле он не определен стандартом c99, но большинство компиляторов выбирают его таким образом для таких экземпляров, как:

printf("Hello, World\n");

стандарт c99 (pdf) [стр. 130, раздел 6.7.8]:

объявление:

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

определяет "простые" объекты массива символов s и t, элементы которых инициализируются символьной строкой константы. Это объявление идентично char

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

содержимое массивов можно изменять. С другой стороны, декларация

char *p = "abc";

определяет p с типом " указатель на char "и инициализирует его, чтобы указать на объект с типом" массив char " длиной 4, элементы которого инициализируются символьным строковым литералом. Если сделана попытка использовать p для изменения содержимого массива, поведение не определено.

вы также можете использовать strdup:

   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).

для примера:

char *a = strdup("stack overflow");

все хорошие ответы, объясняющие, почему вы не можете изменить строковые литералы, потому что они помещаются в память только для чтения. Однако, когда дело доходит до драки, есть способ сделать это. Проверьте этот пример:

#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;
}

Я написал это как часть мои несколько более глубокие мысли о const-правильности, который вы могли бы найти интересным (я надеюсь :)).

надеюсь, что это помогает. Удачи Вам!

вам нужно скопировать строку в другой, не только для чтения буфер памяти и изменить его там. Используйте strncpy () для копирования строки, strlen () для определения длины строки, malloc () и free () для динамического выделения буфера для новой строки.

например (C++ как псевдокод):

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;
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);