GCC fprintf: это должно быть int (32 бита) или char (8 бита)? - PullRequest
0 голосов
/ 28 июня 2018

Какой из кодов будет действительным? Я получаю ноль символов на выходных данных и задаюсь вопросом, вызвано ли это неправильным обращением с символами как целыми числами или наоборот. Должен быть напечатан спецификатор% c, я просто не уверен насчет последнего аргумента.

int c;
fprintf(stdout, "%c", c);
int n;
fprintf(stdout, "%c", n);

Тот же вопрос относительно scanf, это должен быть тип char или int или оба?

1 Ответ

0 голосов
/ 28 июня 2018

Оба в порядке. Стандарт ISO C11 утверждает в 7.21.6.1 The fprintf function /9 (все различные вызовы *printf и *scanf определены в терминах fprintf и fscanf), что:

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

Но в разделе для %c (/8) также говорится (мой акцент):

Если модификатор длины l отсутствует, аргумент int преобразуется в unsigned char, и результирующий символ записывается.

Фактически, передача int или char не имеет значения, поскольку продвижение аргументов по умолчанию выполняется для функций типа varargs за пределами эллипсов (согласно 6.5.2.2 Function calls /6 и /7 и 6.3 Conversions)

... целочисленные преобразования выполняются для каждого аргумента, а аргументы с типом float повышаются до двойного. Это называется продвижением аргументов по умолчанию.

Многоточие в объявлении прототипа функции останавливает преобразование типа аргумента после последнего объявленного параметра. Аргумент по умолчанию продвижение по службе выполняется по последним аргументам.

Если int может представлять все значения исходного типа (как ограничено шириной для битового поля), значение преобразуется в целое; в противном случае он конвертируется в беззнаковое целое. Они называются целочисленными акциями.

Итак, поскольку fprintf определяется так:

int fprintf(FILE * restrict stream, const char * restrict format, ...);

это означает, что аргумент char будет в любом случае повышен до int.


fscanf - это другое дело. В 7.21.6.2 The fscanf function /12 (снова мой акцент) говорится:

Если модификатор длины l отсутствует, соответствующий аргумент должен быть указатель на начальный элемент массива символа , достаточно большой, чтобы принять последовательность. Нулевой символ не добавляется.

Это означает, что вы должны указывать адрес символа (или символьного массива). Если вы предоставите int, а он не того же размера, вы, вероятно, увидите только часть из этого int измененного, а остальное останется как какое-то произвольное значение. Например, для восьмибитного байта, четырехбайтовой int системы с прямым порядком байтов:

+------+------+------+------+
| 0x12 | 0x34 | 0x56 | 0x78 | <- Initial value of int 0x12345678
+------+------+------+------+
| 0x36 | .... | .... | .... | <- Read character '6' (0x36 in ASCII)
+------+------+------+------+
| 0x36 | 0x34 | 0x56 | 0x78 | <- Final value of int 0x36345678
+------+------+------+------+

Вы можете видеть, что другие байты int остаются нетронутыми (представлены ....), в результате чего окончательное значение int является чем-то other чем 0x36.

...