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