Имеет ли C тип строки?
Недавно я начал программировать на C, исходя из Java и Python. Теперь, в моей книге, я заметил, что для создания программы "Hello World" синтаксис выглядит примерно так:
char message[10]
strcpy(message, "Hello, world!")
printf("%s\n", message);
Теперь, этот пример использует массив char, и я подумал - что случилось с строками? Почему я не могу использовать один из них? Может быть, есть другой способ сделать это?
Ответы
Ответ 1
C не имеет и никогда не имел родной тип строки. По соглашению язык использует массивы char
, завершающиеся нулевым char, то есть с '\0'
. Функции и макросы в языковых стандартных библиотеках обеспечивают поддержку массивов символов с нулевым символом, например strlen выполняет итерацию по массиву char
, пока не встретится с '\0'
и strcpy копии из исходной строки до тех пор, пока она не встретит '\0'
.
Использование строк с нулевым завершением в C отражает тот факт, что C был только немного более высоким, чем язык ассемблера. Строки с нулевым завершением уже поддерживались в то время в языке ассемблера для PDP-10 и PDP-11.
Стоит отметить, что это свойство строк C приводит к множеству неприятных ошибок переполнения буфера, включая серьезные недостатки безопасности. Например, если вы забудете нулевое завершение символьной строки, переданной как исходный аргумент в strcpy
, функция будет продолжать копировать последовательные байты из того, что происходит в памяти за конец строки источника, пока не произойдет столкновение 0
, потенциально переписывая любую ценную информацию, следует за местоположением строки назначения в памяти.
В вашем примере кода строковый литерал "Hello, world!" будет скомпилирован в массив длиной 14 байтов char
. Первые 13 байт будут содержать буквы, запятую, пробел и восклицательный знак, а последний байт будет содержать символ нулевого терминатора '\0'
, автоматически добавленный для вас компилятором. Если бы вы получили доступ к последнему элементу массива, вы найдете его равным 0
. Например:.
const char foo[] = "Hello, world!";
assert(foo[12] == '!');
assert(foo[13] == '\0');
Однако в вашем примере message
имеет длину всего 10 байтов. strcpy
собирается записать все 14 байтов, включая нуль-терминатор, в память, начиная с адреса message
. Первые 10 байтов будут записаны в память, выделенную в стеке для message
, а оставшиеся четыре байта будут просто записаны в конец стека. Следствием написания этих четырех лишних байтов в стеке трудно предсказать в этом случае (в этом простом примере это может не повредить вещь), но в коде реального мира это обычно приводит к нарушению ошибок доступа к данным или нарушениям доступа к памяти.
Ответ 2
В C
нет типа string
. Вы должны использовать массивы char.
Кстати, ваш код не будет работать, потому что размер массива должен позволять всему массиву соответствовать плюс один дополнительный нулевой символ завершения.
Ответ 3
В C строка просто представляет собой массив символов, заканчивающийся нулевым байтом. Таким образом, char*
часто произносится как "строка", когда вы читаете код C.
Ответ 4
Чтобы отметить это на указанных вами языках:
Java:
String str = new String("Hello");
Python:
str = "Hello"
Как Java, так и Python имеют понятие "строка", C не имеет понятия "строка". C имеет массивы символов, которые могут поступать "только для чтения" или манипулировать.
С
char * str = "Hello"; // the string "Hello\0" is pointed to by the character pointer
// str. This "string" can not be modified (read only)
или
char str[] = "Hello"; // the characters: 'H''e''l''l''o''\0' have been copied to the
// array str. You can change them via: str[x] = 't'
Символьный массив представляет собой последовательность смежных символов с уникальным символом сторожевого знака в конце (обычно это NULL-терминатор '\0'
). Обратите внимание, что дозорный символ автоматически добавляется к вам в случаях выше.
Ответ 5
C не поддерживает тип строки первого класса.
С++ имеет std::string
Ответ 6
C не имеет собственного типа данных String, такого как Java.
Только мы можем объявить тип данных String в C с использованием символьного массива или указателя символа
Например:
char message[10];
or
char *message;
Но вам нужно объявить хотя бы:
char message[14];
чтобы скопировать "Привет, мир!". в переменную сообщения.
- 13: длина "Привет, мир!"
- 1: для символа '\ 0', который идентифицирует конец строки
Ответ 7
Во-первых, вам не нужно все это делать. В частности, strcpy
является избыточным - вам не нужно копировать строку только в printf
. Ваш message
можно определить с помощью этой строки.
Во-вторых, вам не хватило места для этого "Привет, мир!". string (message
должно быть не менее 14 символов, что дает дополнительный номер для нулевого терминатора).
О том, почему, однако, это история. В ассемблере нет строк, только байтов, слов и т.д. У Паскаля были строки, но из-за этого были проблемы со статической типизацией - string[20]
был другим типом, который string[40]
. Были языки даже в первые дни, которые избегали этой проблемы, но это вызвало косвенные и динамические накладные расходы распределения, которые в то время были гораздо более эффективными.
C просто решил избежать накладных расходов и оставаться на очень низком уровне. Строки - это массивы символов. Массивы очень тесно связаны с указателями, указывающими на их первый элемент. Когда типы массивов "распадаются" на типы указателей, информация о размере буфера теряется из статического типа, поэтому вы не получаете старые проблемы с строкой Pascal.
В С++ существует класс std::string
, который позволяет избежать многих из этих проблем - и имеет динамические накладные расходы распределения, но в наши дни мы обычно не заботимся об этом. И в любом случае std::string
- это класс библиотеки - там обрабатывается массив символов C-стиля.