Я видел несколько случаев использования fgets
(например, здесь), которые выглядят следующим образом:
char buff[7]="";
(...)
fgets(buff, sizeof(buff), stdin);
Интерес заключается в том, что если я предоставлю длинный ввод, например «aaaaaaaaaaaa», fgets
урежет его до «aaaaaa», потому что 7-й символ будет использоваться для хранения '\0'
.
Однако при этом:
int i=0;
for (i=0;i<7;i++)
{
buff[i]='a';
}
printf("%s\n",buff);
Я всегда буду получать 7 'a'
s, и программа не будет падать. Но если я попытаюсь написать 8 'a'
s, это будет.
Как я понял позже, причина этого в том, что, по крайней мере, в моей системе, когда я выделяю char buff[7]
(с =""
или без него), 8-й байт (считая с 1, а не с 0) устанавливается в 0. Из того, что я думаю, все сделано именно так, чтобы цикл for
с 7 операциями записи, за которым следует чтение в формате строки, мог быть успешным, независимо от того, был ли последний записываемый символ '\0'
или нет, и, таким образом, программисту не нужно было устанавливать сам последний '\0' при написании символов по отдельности.
Отсюда следует, что в случае
fgets(buff, sizeof(buff), stdin);
а затем предоставить слишком длинный ввод, результирующая buff
string автоматически будет иметь два символа '\0'
, один внутри массива и один сразу после него, который был записан системой.
Я также заметил, что выполнение
fgets(buff,(sizeof(buff)+17),stdin);
будет по-прежнему работать и выводить очень длинную строку без сбоев. Насколько я понял, это потому, что fgets
будет продолжать запись до sizeof(buff)+17
, а последним записываемым символом будет именно '\0'
, гарантируя, что любой предстоящий процесс чтения строки будет завершен правильно (хотя память все равно испорчена).
Но тогда как насчет fgets(buff, (sizeof(buff)+1),stdin);
? это израсходует все пространство, которое было правильно выделено в buff
, а затем запишет '\0'
сразу после него, таким образом перезаписав... '\0'
ранее записанное системой. Другими словами, да, fgets
вышло бы за пределы допустимого, но можно доказать, что при добавлении к длине записи только единицы программа никогда не рухнет.
Итак, в конце возникает вопрос: почему fgets
всегда заканчивает запись на '\0'
, когда другой '\0'
, размещенный системой сразу после массива, уже существует? почему бы не сделать так, как в записи на основе цикла for
один за другим, которая может получить доступ ко всему массиву и записать все, что хочет программист, ничего не подвергая опасности?
Большое спасибо за ваш ответ!
РЕДАКТИРОВАТЬ: действительно, доказательство невозможно, пока я не знаю, является ли этот 8-й '\0'
, который таинственным образом появляется при выделении buff[7], частью стандарта C или нет, особенно для строковых массивов. Если нет, то... просто повезло, что это работает :-)
fgets
. Если у вас естьchar a;
и вы передаете&a
с некоторым произвольным размером больше 1, вы ожидаете что-нибудь определенное? Это C-api, и, как и большинство, либо полезный, либо неопределенный в своем поведении, в зависимости от того, как вы его называете. - person WhozCraig   schedule 28.08.2013int[]
), а затем думаете о строке, т.е. как о слове, с вездесущий страх перед отсутствующим завершающим персонажем. Из-за этого стандарт мог включить этот 8-й '\0' в качестве жестко заданного параметра. Поскольку я не знаю деталей стандарта... Я задавал вопрос: является ли эта восьмая'\0'
частью стандарта C? - person MrBrody   schedule 28.08.2013buff[6]
в стеке есть неиспользуемый байт (поскольку следующую переменную необходимо выровнять по четной границе). Но на это нельзя полагаться... - person Jonathan Leffler   schedule 28.08.2013n
байт, вам гарантированоn
байта, но вы можете получить больше. - person agbinfo   schedule 29.08.2013char buff[7]
) без инициализации, то buff[6] оказывается чем угодно (32, 129 ... тем, что там оставалось раньше), но buff[7] был на удивление постоянным в своем значении. всегда 0. Вот что меня удивило! - person MrBrody   schedule 29.08.2013