Вопрос по string, c, char – разница между char * и char [] с помощью strcpy ()

5

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

void cut_str(char* entry, int offset) {
    strcpy(entry, entry + offset);
}

char  works[128] = "example1\0";
char* doesnt = "example2\0";

printf("output:\n");

cut_str(works, 2);
printf("%s\n", works);

cut_str(doesnt, 2);
printf("%s\n", doesnt);

// output:
// ample1
// Segmentation: fault

Я чувствую, что в char * / char [] есть что-то важное, чего я здесь не получаю.

Этот вопрос часто задают здесь. Пожалуйста, смотрите, например, / Stackoverflow.com вопросы / 10186765 / ... а также / Stackoverflow.com вопросы / 4090434 / ... Jim Balter
Возможный дубликат В чем разница между char s [] и char * s в C? Bo Persson

Ваш Ответ

3   ответа
11

doesnt указывает на память, которая принадлежит строковой константе и поэтому недоступна для записи.

Когда ты это сделаешь

char  works[128] = "example1\0";

компилятор Копии содержимое недоступной для записи строки в доступный для записи массив.\0стати, @ не требуется.

Но когда ты это сделаешь,

char* doesnt = "example2\0";

компилятор оставляет указатель, указывающий на недоступную для записи область памяти. Опять таки,\0 будет вставлен компилятором.

Если вы используетеgcc, вы можете предупредить вас об инициализации записиchar * со строковыми литералами. Опция-Wwrite-strings. Вы получите предупреждение, которое выглядит следующим образом:

 warning: initialization discards qualifiers from pointer target type

Правильный способ объявить свойdoesnt указатель выглядит следующим образом:

const char* doesnt = "example2\0";
"компилятор копирует содержимое не записываемой строки в массив для записи" - не совсем. «example1 \ 0» здесь не является строкой, недоступной для записи, это строковый литерал - чисто синтаксический элемент. В этом контексте он используется в качестве инициализатора. Jim Balter
"Вы должны были получить предупреждение о символе * notnt = ... line" - Нет, правда, нет. К счастью, нет компилятора. Jim Balter
Правильно, это вопрос C (и ответ), а не C ++. Jim Balter
@ JimBalter g ++ делает: «предупреждение: не рекомендуется преобразовывать строковую константу в« char * »». Это не совсем компилятор C, но я не придумывал его:) dasblinkenlight
@ JimBalter Я отредактировал ответ, чтобы объяснить, как вы получаете предупреждение отgcc. Наверное, так много слов для "Нет компилятора" ... dasblinkenlight
4

char[] а такжеchar * очень похожи, так что вы правы в этом. Разница заключается в том, что происходит, когда объекты типов инициализируются. Ваш объектworks, типаchar[], в стеке выделено 128 байтов переменной памяти. Ваш объектdoesnt, типаchar *, В стеке нет места для хранения.

Где именно строкаdoesnt хранится не определяется стандартом C, но, скорее всего, он хранится в неизменяемом сегменте данных, загружаемом, когда ваша программа загружается для выполнения. Это не переменное хранилище. Таким образом, segfault, когда вы пытаетесь изменить его.

«Разница заключается» - есть и другие существенные различия между массивами и указателями. Проблема здесь не в этом, а в том, что она не указывает на строковую константу, а в стандарте говорится, что результаты ее изменения не определены. Jim Balter
3

Это выделяет 128 байтов в стеке и использует имяworks чтобы сослаться на его адрес:

char works[128];

Такworks - указатель на доступную для записи память.

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

char * doesnt = "example2\0";

Вы можете записать данные вworks, потому что это указывает на доступную для записи память. Вы не можете записывать данные вdoesnt, потому что он указывает на постоянную память.

Также обратите внимание, что вам не нужно заканчивать строковые литералы символом"\0", поскольку все строковые литералы неявно добавляют нулевой байт в конец строки.

Нет,works является объектом массива, а не указателем. (Его имя распадается на выражение указателя в большинстве контекстов.) Keith Thompson
«Это выделяет 128 байтов в стеке» - мы не знаем класс хранилища; это может быть внешним. И, как отмечает Кит, массивы не являются указателями. (Одним из следствий является то, чтоsizeof(works) != sizeof((char*)works)). Jim Balter

Похожие вопросы