Подтвердить что ты не робот

Можно ли изменить строку char в 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 работал нормально).

4b9b3361

Ответ 1

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

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

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

Ответ 2

Нет, вы не можете изменить его, так как строка может быть сохранена в постоянной памяти. Если вы хотите изменить его, вы можете использовать массив, например,

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

Ответ 3

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

char *foo = "hello world";

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

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

Ответ 4

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

Ответ 5

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

printf("Hello, World\n");

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

Объявление:

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

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

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

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

char *p = "abc";

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

Ответ 6

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

#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-correctness, которые могут показаться интересными (я надеюсь:)).

Надеюсь, это поможет. Удачи!

Ответ 7

Вы также можете использовать 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");

Ответ 8

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

Например (псевдоним С++):

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;

Ответ 9

char *a = "stack overflow";
char *b = "new string, it real";
int d = strlen(a);

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