Я написал следующую краткую программу на C (используя стандарт C90), изучая, как использовать буферизованный ввод:
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 10
int getLine(char s[]) {
int ch;
int chCount;
for (chCount = 0; (ch = getchar()) != '\n' && ch != EOF; ++chCount) {
if (chCount > MAXLINE) {
puts("Interrupted read");
break;
}
s[chCount] = ch;
}
return chCount;
}
int main(int argc, char *argv[]) {
char buf[MAXLINE];
int count;
while (1) {
count = getLine(buf);
printf("%s: %d\n", buf, count);
}
return EXIT_SUCCESS;
}
Если я выполню приведенную выше программу с тестовым вводом «один два три», я получу следующий вывод:
> one two three
Interrupted read
one two thr�#��6@&'o�: 11
ene two thr�#��6@&'o�: 1
Большая часть этого вывода ожидаема: длина входных данных превышает 11 символов, поэтому чтение разделено на две операции. Без проблем. Меня беспокоит, конечно, �#��6@&'o�. Я помню, как читал, что создание массива с использованием синтаксиса char buf[x] создаст массив, который перед записью будет заполнен ненужными данными, и это нормально. Однако, насколько я помню, это был разговор с вторым пилотом из Microsoft, так действительно ли этот факт правдив? Если да, то почему этот массив заполнен большим количеством мусора, чем заявленный размер? Важно ли, что длина этого вывода составляет 21 символ — двойная длина буфера плюс один?
Что меня больше смущает - и откуда взялось название этого вопроса - так это следующее поведение:
Если я изменю main() так, чтобы объявление буфера было следующим:
int main(int argc, char *argv[]) {
size_t size = MAXLINE;
char buf[size];
...
тогда вывод программы станет
> one two three
Interrupted read
one two thr: 11
ene two thr: 1
Это меня совершенно сбивает с толку. Я понимаю, что size_t имеет особое значение, поскольку он является типом, реализованным на платформе, как показано в этом посте stackoverflow, но почему то, что, казалось бы, всего лишь разница в размере типа, так радикально меняет поведение массива?
Подводя итог, мои вопросы заключаются в следующем:
int заявленный размер?int на size_t, кажется, полностью решает проблему?Вы не завершаете строку нулем и не ограничиваете объем выводимых данных (например, с помощью "%.10s"), поэтому получаете мусор. Вы должны оставить место для нулевого терминатора где-то вдоль строки.
Отсутствие завершения — не единственная проблема: if (chCount > MAXLINE) Уже слишком поздно. MAXLINE уже является недопустимым индексом. Напишите s[MAXLINE], прежде чем завершить цикл. Это может уже повредить соседнюю переменную.



Правда ли, что инициализация массива с помощью спецификатора фиксированного размера создает массив, который сначала заполняется ненужными данными?
Это зависит от того, какую продолжительность хранения получил этот массив. Локальные переменные, объявленные внутри функций, имеют автоматический срок хранения, то есть они не инициализируются и содержат «мусор». Это сделано намеренно, поскольку, например, нулевая инициализация требует дополнительного времени выполнения.
Почему массив заполнен большим количеством данных, чем позволяет его объявленный размер int?
Это не так, вы просто получаете к нему доступ за пределами границ при печати. В конце строк должен быть добавлен нулевой терминатор, иначе вы начнете получать доступ к данным за пределами границ. Как следует использовать массивы символов в качестве строк?
Важно ли, что длина этого вывода составляет 21 символ — двойная длина буфера плюс один?
Размер буфера тут ни при чем.
Почему изменение ничего, кроме типа спецификатора размера массива с int на size_t, кажется, полностью решает проблему?
Предположительно, потому что вы возитесь со структурой памяти стека, поскольку size_t занимает больше памяти. Не существует предсказуемого поведения при доступе к массиву за пределами его границ. И поэтому симптомы, вызванные этим, не имеют особого значения. Что такое неопределенное поведение и как оно работает?
Неопределенное поведение означает, что может случиться что угодно. Вы передаете незавершенную строку в printf.