Вопрос относительно продвижения аргумента C

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

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


А. Тип персонажа

  1. Из книги 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',

  1. Итак, следующее, что я сделал после прочтения этого, конечно, следуйте тому, что он упоминает, то есть я пытаюсь сохранить слово FATE на переменную с char grade и попробуйте скомпилировать и посмотреть, что будет храниться с помощью printf(), но вместо того, чтобы получить характер 'E' распечатал, что я получаю 'F',

  2. Значит ли это, что в книге есть какая-то ошибка? ИЛИ я что-то не так понял?

  3. Из приведенных выше предложений есть строка, которая говорит, что C рассматривает символьные константы как тип int, Поэтому, чтобы попробовать это, я назначаю номер больше 255, (бывший 356) к char тип.

  4. поскольку 356 находится в диапазоне 32-бит int (Я использую Windows 7), поэтому я ожидаю, что он будет распечатан 356 когда я использую %d спецификатор.

  5. Но вместо печати 356, это дает мне 100, который является последним 8-битным значением.

  6. Почему это происходит? я думал char == int == 32-bits? (Хотя он упоминает раньше, что char только байт).


B. Int и плавающий тип

  1. Я понимаю, когда число хранится в переменной в short Тип - это передача переменной или любой неявной функции прототипа, она будет автоматически повышена до int тип.

  2. Это также происходит с типом с плавающей запятой, когда число с плавающей запятой с float тип передан, он будет преобразован в double тип, поэтому нет спецификатора для float типа, но вместо этого есть только %f за double а также %Lf за long double,

  3. Но почему есть спецификатор для 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 бит.

Другие вопросы по тегам