Подтвердить что ты не робот

Что было с историческим супом typedef для целых чисел в программах на C?

Это, возможно, вопрос, который, вероятно, должен быть известен.

Пятнадцать лет назад или около того, много кода на C, на которые я смотрел бы, имели массу целочисленных typedefs в платформенных #ifdef s. Кажется, каждая программа или библиотека, на которую я смотрел, имели свой собственный, несовместимый суп-суп. Я не знал много о программировании в то время, и казалось, что причудливая куча обручей, чтобы проскочить, просто сообщила компилятору, какое целое вы хотели использовать.

Я собрал рассказ в своем уме, чтобы объяснить, что такое эти typedef, но я действительно не знаю, правда ли это. Я предполагаю, что в основном, когда C был впервые разработан и стандартизирован, не было осознано, насколько важно было иметь возможность платформы - независимо получить целочисленный тип определенного размера, и, следовательно, все исходные C-целые типы могут быть разных размеров на разных платформах. Таким образом, каждый, кто пытается написать переносимый код C, должен был сделать это сам.

Это правильно? Если да, то как программисты ожидали использовать C-целые типы? Я имею в виду, что на низкоуровневом языке с большим количеством бит-скрипов, не важно ли говорить "это 32-битное целое число"? А так как язык был стандартизован в 1989 году, наверняка возникла мысль, что люди будут пытаться писать переносимый код?

4b9b3361

Ответ 1

Когда C начали компьютеры были менее однородными и гораздо менее связанными, чем сегодня. Было видно, что для переносимости важнее, чтобы типы int были натуральными размерами для компьютера. Требование точно 32-битного целочисленного типа в 36-битной системе, вероятно, приведет к неэффективному коду.

И затем появилась повсеместная сеть, в которой вы работаете с определенными полями размера по проводам. Теперь интероперабельность выглядит совсем по-другому. И "октет" становится фактическим квантом типов данных.

Теперь вам нужны ints точных кратных 8 бит, так что теперь вы получаете суп typedef, а затем, в конце концов, стандартный догоняет, и у нас есть стандартные имена для них, и суп не нужен.

Ответ 2

Раньше успех был обусловлен гибкостью адаптации почти ко всем существующим вариантам архитектуры @John Hascall с помощью: 1) собственные целые размеры 8, 16, 18, 24, 32, 36 и т.д. Бит,
2) разновидности со знаком целочисленных чисел: 2 дополнения, 1 дополнение, целое число со знаком и 3) различные конечные, большие, маленькие и другие.

По мере разработки кодирования алгоритмы и обмен данными были выдвинуты для большей однородности и, следовательно, для типов, которые соответствовали 1 и 2 выше по платформам. Кодеры катились как typedef int int32 внутри #if .... Много вариаций, которые создали суп, как отмечено OP.


C99 представил (u)int_leastN_t, (u)int_fastN_t, (u)intmax_t, чтобы сделать портативные, но несколько минимальные типы ширины битовой ширины. Эти типы необходимы для N = 8,16,32,64.

Также введены полу-необязательные типы (см. ниже **), такие как (u)intN_t, у которого есть дополнительные атрибуты, они должны быть 2 дополнениями и без заполнения. Именно эти популярные типы настолько широко желательны и используются для измельчения цельного супа.


как программисты ожидали использовать C-целые типы?

Записывая гибкий код, который не сильно зависит от ширины бита. Является довольно легко кодировать strtol(), используя только LONG_MIN, LONG_MAX без учета кодирования по ширине бит /endian/integer.

Однако многие задачи кодирования обязывают точные типы ширины и 2 дополнения для простого высокопроизводительного кодирования. Лучше в этом случае отказаться от переносимости до 36-битных машин и 32-битных значений знаковых величин и придерживаться 2 N wide (2 дополнения для подписанных) целых чисел. Приходят на ум различные CRC и криптоалгоритмы и форматы файлов. Таким образом, потребность в типах фиксированной ширины и заданный (C99) способ сделать это.


Сегодня есть еще gotchas, которым все еще нужно управлять. Пример. Обычные рекламные акции int/unsigned теряют контроль, так как эти типы могут быть 16, 32 или 64.


**

Эти типы являются необязательными. Однако, если реализация предоставляет целочисленные типы с шириной 8, 16, 32 или 64 бита, без битов заполнения и (для подписанных типов), которые имеют представление с двумя дополнениями, оно должно определять соответствующие имена typedef. C11 7.20.1.1 Целочисленные типы точной ширины 3

Ответ 3

Я помню этот период, и я виновен в том, чтобы сделать то же самое!

Один вопрос был размером int, он может быть таким же, как short, или long или между ними. Например, если вы работали с форматами двоичных файлов, было необходимо, чтобы все выровнялось. Байт упорядочивает сложные вещи. Многие разработчики шли по ленивому маршруту и ​​просто делали fwrite, вместо того, чтобы выделять номера отдельно побайтовыми. Когда машины обновились до более длинных длин слов, все ад сломался. Так что typedef было легко взломать, чтобы это исправить.

Если производительность была проблемой, как это часто было тогда, int гарантировалось, что машина будет самой быстрой в натуральном размере, но если вам нужно 32 бита, а int было короче этого, вы были в опасности опрокидывание.

На языке C, sizeof() не должен быть разрешен на этапе препроцессора, что усложнило ситуацию, потому что вы не могли сделать #if sizeof(int) == 4, например.

Лично некоторые из оснований также просто работали с мышлением ассемблерного языка и не желали абстрагироваться от понятия того, что означают short, int и long. В то время ассемблер часто использовался в C.

В настоящее время существует множество не двоичных форматов файлов, JSON, XML и т.д., где не имеет значения, что такое двоичное представление. Кроме того, многие популярные платформы рассчитаны на 32-разрядный int или дольше, что обычно достаточно для большинства целей, поэтому меньше проблем с опрокидыванием.

Ответ 4

C - продукт начала 1970-х годов, когда вычислительная экосистема была совсем другой. Вместо того, чтобы миллионы компьютеров разговаривали друг с другом по расширенной сети, у вас было, может быть, сто тысяч систем по всему миру, каждая из которых выполняла несколько монолитных приложений, практически не поддерживая связь между системами. Вы не могли предположить, что любые две архитектуры имели одинаковые размеры слова или представляли собой целые числа со знаком таким же образом. Рынок все еще был достаточно мал, чтобы не было необходимости в стандартизации, компьютеры не разговаривали друг с другом (много), и никто, хотя и не очень заботился о переносимости.

Если да, то как программисты ожидали использовать C-целые типы?

Если вы хотите написать максимально переносимый код, то вы не предполагали ничего, кроме того, что гарантировал стандарт. В случае int это означало, что вы не предполагали, что он может представлять что-либо за пределами диапазона [-32767,32767], и вы не предположили, что он будет представлен в 2 дополнениях, и вы не предположили, что это была конкретная (он может быть шире, чем 16 бит, но все же только представляет собой 16-битный диапазон, если он содержит любые биты заполнения).

Если вы не заботитесь о переносимости, или делаете вещи, которые по своей сути не переносятся (обычно это бит-сплетение), вы использовали все типы, удовлетворяющие вашим требованиям.

Я делал в основном высокоуровневое программирование приложений, поэтому я меньше беспокоился о представлении, чем о диапазоне. Тем не менее, я иногда нуждался в том, чтобы окунуться в двоичные представления, и он всегда укусил меня в задницу. Я помню, как писал код в начале 90-х, который должен был запускаться на классических MacOS, Windows 3.1 и Solaris. Я создал множество констант перечисления для 32-битных масок, которые отлично работали на блоках Mac и Unix, но не удалось скомпилировать их в окне Windows, потому что в Windows размер int был всего 16 бит.

Ответ 5

C был разработан как язык, который можно портировать как можно большему числу машин, а не как язык, который позволил бы запускать большинство видов программ без изменений на таком диапазоне машин. Для большинства практических целей типы C были:

  • 8-битный тип, если он доступен, или самый маленький тип, который содержит не менее 8 бит.

  • 16-разрядный тип, если он доступен, или самый маленький тип, который содержит не менее 16 бит.

  • 32-разрядный тип, если он доступен, или какой-то тип, который имеет не менее 32 бит.

  • Тип, который будет 32 бита, если системы могут обрабатывать такие вещи, как эффективно, как 16-битные, или 16 бит в противном случае.

Если для кода нужны 8, 16 или 32-битные типы и вряд ли будут использоваться на машинах, которые их не поддерживали, не было никакой особой проблемы с таким кодом в отношении char, short и long как 8, 16 и 32 бит соответственно. Единственными системами, которые не отображали эти имена для этих типов, были те, которые не могли поддерживать эти типы и не могли бы с пользой обращаться с кодом, который их требовал. Такие системы будут ограничены написанием кода, который был написан для совместимости с типами, которые они используют.

Я думаю, что C, возможно, лучше всего рассматривать как рецепт преобразования спецификаций системы в языковые диалекты. Система, использующая 36-разрядную память, на самом деле не сможет эффективно обрабатывать один и тот же языковой диалект, как система, использующая память на октете, но программист, который изучает один диалект, сможет узнать другое, просто узнав, какие целые представления последний использует. Гораздо полезнее сказать программисту, которому нужно написать код для 36-битной системы: "Эта машина похожа на другие машины, кроме char - 9 бит, short - 18 бит, а long - 36 бит", чем сказать: "Вы должны использовать язык ассемблера, потому что другим языкам потребуются целые типы, которые эта система не может эффективно обрабатывать".

Ответ 6

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

Но что, собственно, "родной размер слова"? Почти всегда это означает, что размер регистра CPU, который совпадает с Арифметическим логическим блоком (ALU), может работать с.

В встроенных средах все еще есть такие вещи, как 8 и 16-битные процессоры (есть ли еще 4-разрядные контроллеры PIC?). Там есть горы 32-битных процессоров. Таким образом, концепция "родного размера слова" жива и хорошо для разработчиков C.

С 64-разрядными процессорами часто поддерживается поддержка 32-разрядных операндов. На практике использование 32-битных целых чисел и значений с плавающей запятой часто может быть быстрее, чем полный размер слова.

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

Но два общих шаблона использования остаются: агностический код размера для улучшения скорости (int, короткий, длинный) или фиксированный размер (int32_t, int16_t, int64_t) для правильности или совместимости там, где это необходимо.