Может ли sizeof (int) когда-либо быть 1 в размещенной реализации?

Мое мнение состоит в том, что реализация C не может удовлетворять спецификации определенных функций stdio (особенно fputc/fgetc), если sizeof(int)==1, так как int должен иметь возможность удерживать любое возможное значение unsigned char или EOF (-1). Правильно ли это рассуждение?

(Очевидно, что sizeof(int) не может быть 1, если CHAR_BIT равно 8 из-за минимального требуемого диапазона для int, поэтому мы неявно говорим только о реализациях с CHAR_BIT>=16, например DSP, где типичные реализации будет самостоятельной реализацией, а не размещенной реализацией и, следовательно, не требуется предоставлять stdio.)

Изменить. Прочитав ответы и ссылки на некоторые ссылки, некоторые соображения о том, как может быть допустимо, чтобы хостинговая реализация имела sizeof(int)==1:

Во-первых, некоторые цитаты:

7.19.7.1 (2-3):

Если индикатор конца файла для входного потока, на который указывает поток, не установлен, и следующий символ присутствует, функция fgetc получает этот символ как unsigned char преобразуется в int и продвигает соответствующий индикатор позиции для поток (если определено).

Если указатель конца файла для потока установлен или если поток находится в конце файла, то устанавливается конечный индикатор потока, и функция fgetc возвращает EOF. В противном случае Функция fgetc возвращает следующий символ из входного потока, на который указывает поток. Если возникает ошибка чтения, отображается индикатор ошибки для потока и функция fgetc возвращает EOF.

7.19.8.1 (2):

Функция fread читает в массив, на который указывает ptr, до элементов nmemb размер которого определяется размером, из потока, на который указывает поток. Для каждого объект, звонки по размеру передаются функции fgetc и результаты хранятся в порядке read, в массиве unsigned char, который точно накладывает объект. Фиксированное положение индикатор для потока (если определено) определяется числом успешно прочитанных символов.

Мысли:

  • Чтение unsigned char значений вне диапазона int может просто иметь undefined поведение, определенное реализацией в реализации. Это особенно неудобно, так как это означает, что использование fwrite и fread для хранения двоичных структур (что, в то время как оно приводит к непереносимым файлам, должно быть операцией, которую вы можете выполнять переносимо в любой отдельной реализации), могло бы работать, но тихо терпит неудачу. по существу всегда приводит к поведению undefined. Я согласен с тем, что в реализации может не быть пригодной для использования файловой системы, но гораздо сложнее принять, что в реализации может быть файловая система, которая автоматически вызывает носовых демонов, как только вы попытаетесь ее использовать, и не сможете определить, что она непригодна для использования. Теперь, когда я понимаю, что поведение определено по реализации, а не undefined, оно не так уж неудобно, и я думаю, что это может быть допустимая (хотя и нежелательная) реализация.

  • Реализация sizeof(int)==1 может просто определить, что файловая система будет пустой и доступной только для чтения. Тогда приложение не могло бы считывать какие-либо данные, написанные самим собой, только с устройства ввода на stdin, которое можно было бы реализовать, чтобы дать только положительные значения char, которые вписываются в int.

Изменить (снова): Из Обоснования C99, 7.4:

EOF традиционно является -1, но может быть любым отрицательным целым числом и, следовательно, отличным от любого допустимого символьного кода.

Это, по-видимому, указывает на то, что sizeof(int) может быть не 1 или, по крайней мере, это было намерением комитета.

Ответ 1

Реализация может соответствовать требованиям интерфейса для fgetc и fputc, даже если sizeof(int) == 1.

Интерфейс для fgetc говорит, что он возвращает символ, считанный как unsigned char, преобразованный в int. Нигде он не говорит, что это значение не может быть EOF, хотя ожидание, очевидно, верно, что чтение "обычно" возвращает положительные значения. Конечно, fgetc возвращает EOF при сбое чтения или конце потока, но в этих случаях также устанавливается индикатор ошибки файла или индикатор конца файла (соответственно).

Точно так же нигде не говорится, что вы не можете передать EOF в fputc, пока это произойдет, чтобы совпасть со значением unsigned char, преобразованным в int.

Очевидно, что программист должен быть очень осторожным на таких платформах. Возможно, это не полная копия:

void Copy(FILE *out, FILE *in)
{
    int c;
    while((c = fgetc(in)) != EOF)
        fputc(c, out);
}

Вместо этого вам нужно будет сделать что-то вроде (не проверено!):

void Copy(FILE *out, FILE *in)
{
    int c;
    while((c = fgetc(in)) != EOF || (!feof(in) && !ferror(in)))
        fputc(c, out);
}

Конечно, на платформах, где у вас будут реальные проблемы, есть те, где sizeof(int) == 1, а преобразование с unsigned char в int не является инъекцией. Я считаю, что это обязательно будет иметь место на платформах с использованием знака и величины или дополнения для представления целых чисел со знаком.

Ответ 2

Я помню тот же самый вопрос на comp.lang.c около 10 или 15 лет назад. Для этого я нашел здесь более актуальную дискуссию:

http://groups.google.de/group/comp.lang.c/browse_thread/thread/9047fe9cc86e1c6a/cb362cbc90e017ac

Я думаю, что есть два результирующих факта:

(a) Возможны реализации, где строгое соответствие невозможно. Например. sizeof (int) == 1 с отрицательными значениями с одним дополнением или знаковой величиной или битами заполнения в типе int, т.е. не все значения без знака char могут быть преобразованы в допустимое значение int.

(b) Типичная идиома ((c=fgetc(in))!=EOF) не переносима (кроме CHAR_BIT == 8), поскольку EOF не требуется отдельное значение.

Ответ 3

Я не считаю, что стандарт C напрямую требует, чтобы EOF отличался от любого значения, которое можно было бы прочитать из потока. В то же время, как представляется, само собой разумеющееся, что это будет. Некоторые части стандарта имеют противоречивые требования, которые, я сомневаюсь, могут быть выполнены, если EOF является значением, которое может быть прочитано из потока.

Например, рассмотрим ungetc. С одной стороны, в спецификации говорится (§7.19.7.11):

Функция ungetc выталкивает символ, указанный c (преобразуется в unsigned char) обратно на входной поток, на который указывает поток. Отточенные символы будут возвращенные последующими чтениями в этом потоке в обратном порядке их нажатия. [...] Гарантируется один символ отката.

С другой стороны, в нем также говорится:

Если значение c равно значению макроса EOF, операция завершается с ошибкой, и входной поток не изменяется.

Итак, если EOF - это значение, которое может быть прочитано из потока, и (например) мы читаем из потока, и сразу же используем ungetc, чтобы вернуть EOF в поток, мы получаем головоломку: вызов "гарантирован" для успеха, но также явно требуется для отказа.

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

В случае, если кто-то заботится, N1548 (текущий проект нового стандарта C) сохраняет те же требования.

Ответ 4

Было бы недостаточно, если номинальный char, который разделил бит-шаблон с EOF, был определен как нечувствительный? Если, например, CHAR_BIT было 16, но все допустимые значения занимали только 15 младших значащих битов (предположим, что 2s-дополнение представления знаковой величины int). Или все, что представимо в char, имеет смысл как таковой? Признаюсь, я не знаю.

Конечно, это был бы странный зверь, но мы позволяем нашим фантазиям идти сюда, верно?

R.. убедил меня, что это не сдержит. Поскольку размещенная реализация должна реализовывать stdio.h, и если fwrite должен иметь возможность привязывать целые числа на диске, тогда fgetc может возвращать любой битовый шаблон, который будет соответствовать char, и это не должно мешать возврату EOF. Что и требовалось доказать.

Ответ 5

Я не очень хорошо знаком с C99, но я не вижу ничего, что говорит, что fgetc должен производить полный диапазон значений char. Очевидным способом реализации stdio в такой системе было бы поместить 8 бит в каждый char, независимо от его емкости. Требование EOF равно

EOF

который расширяется до целого числа постоянное выражение, с типом int и отрицательное значение, которое возвращается несколько функций для указания end-of-file, то есть больше нет ввода из потока

Ситуация аналогична wchar_t и wint_t. В 7.24.1/2-3, определяющем wint_t и WEOF, в сноске 278 говорится

wchar_t и wint_t могут быть одного и того же целочисленного типа.

который, казалось бы, гарантировал бы, что "мягкий" диапазон проверки достаточен, чтобы гарантировать, что *EOF не находится в наборе символов.

Изменить:

Это не позволит бинарные потоки, поскольку в таком случае fputc и fgetc не требуется преобразовывать. (7.19.2/3) Двоичные потоки не являются необязательными; только их отличие от текстовых потоков является необязательным. Таким образом, казалось бы, это делает такую ​​реализацию несогласованной. Тем не менее, это было бы прекрасно использовать, если вы не пытаетесь записать двоичные данные за пределы 8-битного диапазона.

Ответ 6

Я думаю, что ты прав. Такая реализация не может отличить законное значение unsigned char от EOF при использовании fgetc/fputc в двоичных потоках.

Если есть такие реализации (этот поток, кажется, предполагает, что есть), они не являются строго соответствующими. Возможно иметь автономную реализацию с sizeof (int) == 1.

Для автономной реализации (C99 4) требуется только поддержка функций из стандартной библиотеки, как указано в этих заголовках: < float.h > , < iso646.h > , < limits.h > , < stdarg.h > , < stdbool.h > , < stddef.h > , и < & stdint.h GT;. (Примечание нет < stdio.h > ). Свободное владение может иметь больше смысла для DSP или другого встроенного устройства в любом случае.

Ответ 7

Вы предполагаете, что EOF не может быть фактическим символом в наборе символов. Если вы разрешите это, то sizeof (int) == 1 в порядке.

Ответ 8

Компилятор TI C55x, который я использую, имеет 16 бит char и 16 бит int и включает стандартную библиотеку. Библиотека просто принимает восьмибитовый набор символов, поэтому при интерпретации символа char значения > 255 не определяется; и при записи на устройство с 8-разрядным потоком, наиболее значимые 8 бит отбрасываются: например, при записи в UART, только младшие 8 бит передаются в регистр сдвига и вывод.