Модифицировать строковые константы C?

Возможный дубликат:
Почему при записи в строку возникает ошибка сегментации?

Я хочу написать функцию, которая переворачивает заданную строку, переданную в нее. Но я не могу. Если я поставлю doReverse функция (см. код ниже) с массивом символов, мой код работает хорошо.

Я не могу понять, почему это не работает. Я могу получить доступ str[0] в doReverse, но я не могу изменить любое значение массива с помощью указателя на символ. Есть идеи?

void doReverse(char *str) {
    str[0] = 'b';
}

void main(void) {
    char *str = "abc";
    doReverse(str);
    puts(str);
}

Обновить:

Я знаю, как написать обратную функцию, передав ей массив символов:

void reverse1(char p[]) {
    int i, temp, y;

    for (i = 0, y = strlen(p); i < y; ++i, --y) {
        temp = p[y-1];
        p[y-1] = p[i];
        p[i] = temp;
    }
}

Но я хочу написать другую версию, которая получает указатель на символ в качестве параметра.

5 ответов

Решение

Самое простое решение - изменить объявление str в

char str[] = "abc";

Это делает str массив char, который инициализируется в строку "abc". В настоящее время у вас есть str как указатель на символ, инициализированный, чтобы указать на строку, описанную строковым литералом. Есть ключевое отличие: строковые литералы доступны только для чтения, чтобы дать компилятору максимальную гибкость в том, где их хранить; это UB, чтобы изменить их. Но массивы char изменчивы, и поэтому их можно модифицировать.

PS. main() возвращает int,

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

char *str = "abc";

str указатель хранится в стеке Инициализируется, чтобы указать на буквальную строку "abc", Эта литеральная строка будет сохранена в разделе данных вашего скомпилированного исполняемого файла и будет загружена в память при загрузке вашей программы. Этот раздел памяти предназначен только для чтения, поэтому при попытке изменить данные, на которые указывает str, вы получаете нарушение прав доступа.

char* str = malloc(sizeof(char) * 4);
strcpy(str, "abc");

Здесь str - тот же указатель стека, что и в первом примере. На этот раз он инициализируется так, чтобы указывать на 4-символьный блок памяти в куче, который вы можете читать и записывать. Сначала этот блок памяти неинициализирован и может содержать что угодно. strcpy читает блок памяти только для чтения, где хранится "abc", и копирует его в блок памяти чтения-записи, на который указывает str. Обратите внимание, что настройка str[3] = '\0' является избыточным, так как strcpy делает это уже.

Кроме того, если вы работаете в Visual Studio, используйте вместо этого strcpy_s, чтобы убедиться, что вы не перезаписываете свой буфер, если копируемая строка длиннее, чем вы ожидали.

char str[] = "abc"; 

Вот str теперь массив размещен в стеке. Размер компилятора будет точно соответствовать строковому литералу, используемому для его инициализации (включая терминатор NULL). Память стека предназначена для чтения и записи, поэтому вы можете изменять значения в массиве по своему усмотрению.

char str[4] = "abc";

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

Лорди, Лорди. Всем тем, кто предлагает способы действительно провести обмен, пожалуйста, внимательно прочитайте вопрос; нет ничего хуже, чем повторять и без того четко сформулированный вопрос. Независимо от метода, используемого для реализации обмена (временная замена, замена xor3 и т. Д.), Этот человек, похоже, слишком хорошо знаком с фундаментальной и довольно элементарной сущностью функции.

Однако, как уже объяснено, компилятор / компоновщик обычно помещает все строковые литералы в "константный сегмент данных" целевого исполняемого файла, который впоследствии связывается с недоступным для записи дескриптором MMU во время соответствующего вызова "load/exec". Все циклы записи ЦП, впоследствии выданные через этот дескриптор, автоматически перехватываются механизмом исключений MMU, что приводит к обязательному "segfault" или эквивалентному платформе эквиваленту. Само собой разумеется, старые платформы без MMU не будут демонстрировать такого поведения.

Хотя это эффективно обеспечивает поддержку во время выполнения для идиомы "константа / литерал" исходного языка, некоторые платформы исторически облегчали явные переопределения сегментов во время компиляции. Однако этот уровень поддержки постепенно снижается в пользу более жесткого / надежного уровня абстракции, что делает несостоятельными многие очевидные и часто полезные оптимизации. Поскольку время и истощение приводят к тому, что философия MC/ASM постоянно стареет до того, как слишком увлеченное поколение "Microsoft", программисты больше не считаются знающими или достаточно ответственными, чтобы принимать такие решения. Вместо многих изобретенных, если не сказать творческих, реализаций, которые я видел в качестве руководителя проекта, это ни в коем случае не плохая вещь.

Хотя этот пост быстро превращается в нарушение, не относящееся к теме, я чувствую себя несколько оправданным постоянным потоком вопросов, связанных сверху вниз, которые постепенно становятся эндемичными в нашей отрасли. Как начинающий программист на C - язык, изначально разработанный для дополнения низкоуровневой разработки, - я советую принять восходящий подход и дополнить ваши занятия небольшой разработкой внеклассного языка ассемблера. Поскольку алгоритмическая реализация, скорее всего, станет вашим основным направлением работы в качестве разработчика приложений, важно помнить, что современный дизайн ЦП претерпел однородную эволюцию за последние 30 лет; современные сверхбыстрые процессоры Intel - это не более чем суперскалярные усовершенствования CMOS 4/8-битных биполярных процессоров, которые я программировал, когда Земля была еще молода.

Вопреки распространенному мнению, программирование на ассемблере относительно легко выучить, и оно абсолютно необходимо при попытке согласовать высокоуровневые конструкции с проблемным или эзотерическим поведением. Если учесть бесконечные часы экспериментов, отладки, веб-поиска и спама на форумах, не может быть никаких сомнений в том, что подход "снизу вверх", безусловно, является путем наименьшего сопротивления.

Удачи в учебе.

Потому что это домашнее задание, я дам совет, но не буду публиковать полное решение.

Я предполагаю, что вы получаете нарушение прав доступа к str[0] = 'b'? Это потому, что "abc" является строковым литералом.

Скопируйте строку, на которую указывает строка str, перед вызовом reverse или получите reverse, чтобы выделить буфер и поместить туда обратную строку.

Имейте в виду, вам придется освободить всю память, которую вы выделяете.

Насколько я знаю, константные строки реализованы как массивы постоянных символов (или, в терминах C, const char [length]). Следовательно, вы не можете изменять его символы.

Попробуйте динамически выделить строку.

char* str = (char*)malloc(sizeof(char) * 4);
strcpy(str, "abc");
str[3] = '\0';

Конечно, не забудьте освободить память в конце вашей программы.


Редактировать: я не буду публиковать что-либо, касающееся изменения строки, потому что это ваша работа.

Другие вопросы по тегам