Можно ли изменить строку char в C? - PullRequest
58 голосов
/ 18 июня 2009

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

Вот что я пробовал:

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:

Спасибо всем за ваши ответы. Теперь это имеет больше смысла. Особенно важно, почему иногда это работало нормально, а иногда не работало. Потому что иногда я передаю указатель на символ, а иногда массив (символ работает нормально).

Ответы [ 9 ]

129 голосов
/ 18 июня 2009

Когда вы пишете «строку» в исходном коде, она записывается непосредственно в исполняемый файл, потому что это значение необходимо знать во время компиляции (существуют инструменты, позволяющие разделить программное обеспечение и найти в нем все строки простого текста). ). Когда вы пишете 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!)

22 голосов
/ 18 июня 2009

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

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
9 голосов
/ 18 июня 2009

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

char *foo = "hello world";

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

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

6 голосов
/ 18 июня 2009

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

3 голосов
/ 18 июня 2009

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

printf("Hello, World\n");

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

Декларация:

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

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

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

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

char *p = "abc";

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

2 голосов
/ 08 января 2014

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

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

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

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

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

0 голосов
/ 18 июня 2009
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 голосов
/ 18 июня 2009

Вам необходимо скопировать строку в другой, не только для чтения, буфер памяти и изменить его там. Используйте 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...