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