Вопрос относительно продвижения аргумента C
Хорошо, на самом деле я изучал, как использовать циклы, чтобы сделать мой код более эффективным, чтобы я мог использовать определенный блок кода, который должен повторяться, не печатая его снова и снова, и после попытки использовать то, что я изучил, так Что касается того, чтобы что-то программировать, я чувствую, что пришло время перейти к следующей главе, чтобы узнать, как использовать оператор управления, чтобы научить программу принимать решения.
Но дело в том, что, прежде чем я продвинусь к этому, у меня все еще есть несколько вопросов, которые нуждаются в помощи любого эксперта по предыдущим вещам. На самом деле речь идет о типе данных.
А. Тип персонажа
- Из книги C primer Plus 5th ed я извлекаю следующее:
Как ни странно, C рассматривает символьные константы как тип
int
скорее, чемchar
, Например, в системе ASCII с 32-разряднымint
и 8-битныйchar
, код:char grade = 'B';
представляет собой
'B'
в качестве числового значения 66, хранящегося в 32-битной единице,grade
завершается с сохранением 66 UB AB 8-битного устройства. Эта характеристика символьных констант позволяет определить символьную константу, такую как'FATE'
с четырьмя отдельными 8-битными кодами ASCII, хранящимися в 32-битном модуле. Тем не менее, пытаясь присвоить такую символьную константуchar
переменная приводит к использованию только последних 8 битов, поэтому переменная получает значение'E'
,
Итак, следующее, что я сделал после прочтения этого, конечно, следуйте тому, что он упоминает, то есть я пытаюсь сохранить слово
FATE
на переменную сchar grade
и попробуйте скомпилировать и посмотреть, что будет храниться с помощьюprintf()
, но вместо того, чтобы получить характер'E'
распечатал, что я получаю'F'
,Значит ли это, что в книге есть какая-то ошибка? ИЛИ я что-то не так понял?
Из приведенных выше предложений есть строка, которая говорит, что C рассматривает символьные константы как тип
int
, Поэтому, чтобы попробовать это, я назначаю номер больше255
, (бывший356
) кchar
тип.поскольку
356
находится в диапазоне 32-битint
(Я использую Windows 7), поэтому я ожидаю, что он будет распечатан356
когда я использую%d
спецификатор.Но вместо печати
356
, это дает мне100
, который является последним 8-битным значением.Почему это происходит? я думал
char == int == 32-bits
? (Хотя он упоминает раньше, что char только байт).
B. Int и плавающий тип
Я понимаю, когда число хранится в переменной в
short
Тип - это передача переменной или любой неявной функции прототипа, она будет автоматически повышена доint
тип.Это также происходит с типом с плавающей запятой, когда число с плавающей запятой с
float
тип передан, он будет преобразован вdouble
тип, поэтому нет спецификатора дляfloat
типа, но вместо этого есть только%f
заdouble
а также%Lf
заlong double
,Но почему есть спецификатор для
short
типа, хотя это также продвигается, но неfloat
тип? Почему они просто не дают спецификатор дляfloat
введите с модификатором как%hf
или что-то? Есть ли что-то логичное или техническое за этим?
4 ответа
Много вопросов в одном вопросе... Вот ответы на пару:
Эта характеристика символьных констант позволяет определить символьную константу, такую как "FATE", с четырьмя отдельными 8-битными кодами ASCII, хранящимися в 32-битном модуле. Однако попытка присвоить такую символьную константу переменной типа char приводит к используются только последние 8 битов, поэтому переменная получает значение "E".
На самом деле это поведение, определяемое реализацией. Так что да, в книге есть ошибка. Многие книги по Си написаны с предположением, что единственный компилятор Си в мире - это тот, который автор использовал при тестировании примеров.
Компилятор, который использует автор, рассматривал символы в "FATE" как байты целого числа, причем "F" является самым значимым байтом, а "E" - наименее значимым. Ваш компилятор обрабатывает символы в литерале как байты целого числа, где 'F' является наименее значимым байтом, а 'E' - наиболее значимым. Например, первый метод заключается в том, как MSVC обрабатывает значение, а MinGW (компилятор GCC, ориентированный на Windows) обрабатывает литерал вторым способом.
Поскольку отсутствует спецификатор формата для printf()
что ожидает float
на спецификаторах, которые ожидают double
- это потому, что значения переданы printf()
для форматирования являются частью списка переменных аргумента (...
в printf()
прототип). Об этих аргументах нет информации о типах, поэтому, как вы упоминали, компилятор всегда должен их продвигать (из C99 6.5.2.2/6 "Вызовы функций"):
Если выражение, которое обозначает вызываемую функцию, имеет тип, который не включает в себя прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы с типом float повышаются до двойного. Это называется продвижением аргументов по умолчанию.
И C99 6.5.2.2/7 "Вызовы функций"
Многоточие в объявлении прототипа функции останавливает преобразование типа аргумента после последнего объявленного параметра. Повышение аргументов по умолчанию выполняется на конечных аргументах.
Таким образом, фактически невозможно передать float
в printf()
- он всегда будет повышен до double
, Вот почему спецификаторы формата для значений с плавающей запятой ожидают double
,
Также из-за автоматического продвижения, которое будет применяться к short
Я честно не уверен, если h
спецификатор для форматирования short
строго необходимо (хотя это необходимо для использования с n
спецификатор, если вы хотите получить количество символов, записанных в поток, помещенный в short
). Это может быть в C, потому что он должен быть там, чтобы поддержать n
спецификатор, исторические причины или что-то, о чем я просто не думаю.
Первый char
по определению имеет ширину ровно 1 байт. Тогда стандарт более или менее гласит, что размеры должны быть:
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
Точные размеры варьируются (за исключением char
) по системе и компилятору, но в 32-битной Windows размеры с GCC и VC (AFAIK):
sizeof(short) == 2 (byte)
sizeof(int) == sizeof(long) == 4 (byte)
Ваше наблюдение "F" по сравнению с "E" в этом случае является типичной проблемой порядка байтов (маленький или большой порядок, как "слово" хранится в памяти).
Теперь, что происходит с вашей ценностью? У вас есть переменная шириной 8 бит. Вы назначаете большее значение ('FATE'
или же 356
), но компилятор знает, что ему разрешено хранить только 8 битов, поэтому он обрезает все остальные биты.
Ответ A: 3.) Это связано с разным порядком байтов в архитектуре ЦП с большим и меньшим порядком байтов. Вы получаете первый байт с прямым порядком байтов (т.е. x86) и последний байт с большим байтовым процессором (то есть PPC). На самом деле вы всегда получаете младший 8 бит, когда преобразование из int в char выполняется, но символы в int хранятся в обратном порядке.
7.) char может содержать только 8 бит, поэтому все остальное усекается в тот момент, когда вы присваиваете int переменной char, и никогда не может быть восстановлено из переменной char позже.
К B: 3.) Иногда вам может потребоваться вывести только младшие 16 битов переменной типа int независимо от того, что находится в старшей половине. Для определенных оптимизаций нередко упаковывать несколько целочисленных значений в одну переменную. Это хорошо работает для целочисленных типов, но не имеет особого смысла для типов с плавающей запятой, которые напрямую не поддерживают побитовые операции, что может быть причиной того, что в printf нет отдельного спецификатора типа для float.
char
длиной 1 байт. Битовая длина байта может составлять 8, 16, 32 бита. В компьютерах общего назначения длина символа обычно составляет 8 бит. Таким образом, максимальное число, которое может представлять символ, зависит от длины символа. Чтобы проверить длину символа проверки limits.h
заголовочный файл определяется как CHAR_BIT
в этом файле.
char x = 'FATE'
будет зависеть, вероятно, от порядка следования байтов, который машина / компилятор будет интерпретировать как "FATE". Так что это зависит от системы / компилятора. Кто-то, пожалуйста, подтвердите / исправьте это.
Если ваша система имеет 8-битный байт, то, когда вы делаете c = 360
только младшие 8 бит двоичного представления 360 будут храниться в переменной, потому что char
данные всегда выделяются 1 байтом памяти. Так %d
напечатает 100, потому что старшие биты были потеряны, когда вы присвоили значение переменной, а остались только младшие 8 бит.