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

Изменение строковых констант?

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

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

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

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

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

4b9b3361

Ответ 1

Простейшим решением является изменение объявления str на

char str[] = "abc";

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

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

Ответ 2

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

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";

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

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

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

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


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