Одна вещь, которая всегда интуитивно поражала меня как положительная особенность C (ну, собственно, его реализаций, таких как gcc, clang, ...), это то, что он не хранит скрытую информацию рядом с вашими переменными во время выполнения. Под этим я подразумеваю, что если вы, например, хотите переменную «x» типа «uint16_t», вы можете быть уверены, что «x» будет занимать только 2 байта пространства (и не будет содержать никакой скрытой информации, такой как ее тип и т. Д.). .). Точно так же, если вы хотите получить массив из 100 целых чисел, вы можете быть уверены, что он равен 100 целым числам.
Однако чем больше я пытаюсь придумать конкретные варианты использования этой функции, тем больше мне интересно, имеет ли она вообще какие-либо практические преимущества. Единственное, что я мог придумать, так это то, что ему явно требуется меньше оперативной памяти. Для ограниченных сред, таких как микросхемы AVR и т. Д., Это, безусловно, огромный плюс, но для повседневных случаев использования настольных ПК / серверов это, скорее всего, не имеет значения. Еще одна возможность, о которой я думаю, это то, что она может быть полезной / важной для доступа к аппаратному обеспечению или может отображать области памяти (например, для выхода VGA и т. П.) ...?
Мой вопрос: есть ли конкретные домены, которые либо не могут, либо могут быть очень громоздкими без этой функции?
PS Пожалуйста, скажите мне, если у вас есть лучшее название для этого! ;)
источник
virtual
функция-член. Таким образом, RTTI никогда не увеличивает размер каких-либо объектов, он только увеличивает двоичный файл на постоянную.T *
всегда имеет одинаковый размер иT
может содержать скрытое поле, указывающее на vtable. И никакой компилятор C ++ никогда не вставлял vtables в объекты, которые им не нужны.Ответы:
Есть несколько преимуществ, очевидное - во время компиляции, чтобы гарантировать, что такие вещи, как параметры функции, соответствуют передаваемым значениям.
Но я думаю, что вы спрашиваете о том, что происходит во время выполнения.
Имейте в виду, что компилятор создаст среду выполнения, которая встраивает знания о типах данных в выполняемые им операции. Каждый кусок данных в памяти не может быть самоописуемым, но сам по себе код знает, что это за данные (если вы правильно выполнили свою работу).
Во время выполнения все немного по-другому, чем вы думаете.
Например, не думайте, что при объявлении uint16_t используются только два байта. В зависимости от процессора и выравнивания слов он может занимать 16, 32 или 64 бита в стеке. Вы можете обнаружить, что ваш массив шорт потребляет гораздо больше памяти, чем вы ожидали.
Это может быть проблематично в определенных ситуациях, когда вам нужно ссылаться на данные с определенными смещениями. Это происходит при обмене данными между двумя системами с разной архитектурой процессора, либо по беспроводной связи, либо через файлы.
C позволяет вам определять структуры с уровнем детализации на уровне битов:
Эта структура имеет длину в три байта, с коротким определением, начинающимся с нечетного смещения. Он также должен быть упакован, чтобы быть точно таким, как вы его определили. В противном случае компилятор выравнивает слова по словам.
Компилятор сгенерирует код за кулисами, чтобы извлечь эти данные и скопировать в регистр, чтобы вы могли делать с ним полезные вещи.
Теперь вы можете видеть, что каждый раз, когда моя программа обращается к члену структуры myMessage, она будет знать, как именно ее извлечь и работать с ней.
Это может стать проблематичным и трудным для управления при обмене данными между различными системами с разными версиями программного обеспечения. Вы должны тщательно спроектировать систему и код, чтобы обе стороны имели одно и то же определение типов данных. Это может быть довольно сложным в некоторых условиях. Здесь вам нужен лучший протокол, который содержит данные с самоописанием, такие как буфер протокола Google .
Наконец, вы должны спросить, насколько это важно в среде настольных компьютеров и серверов. Это действительно зависит от того, сколько памяти вы планируете использовать. Если вы занимаетесь чем-то вроде обработки изображений, вы можете использовать большой объем памяти, что может повлиять на производительность вашего приложения. Это определенно всегда проблема во встроенной среде, где память ограничена и нет виртуальной памяти.
источник
short
. Но это единовременное требование для начала массива, остальные автоматически выровнены правильно благодаря последовательности.uint8_t padding: 6;
как и первые два бита. Или, точнее, просто комментарий//6 bits of padding inserted by the compiler
. Структура, как вы ее написали, имеет размер как минимум девять байтов, а не три.Вы столкнулись с одной из причин, по которой это полезно: отображение внешних структур данных. К ним относятся отображаемые в памяти видеобуферы, аппаратные регистры и т. Д. Они также включают в себя данные, передаваемые без изменений за пределы программы, такие как SSL-сертификаты, IP-пакеты, изображения JPEG, и почти любая другая структура данных, которая постоянно существует вне программы.
источник
C является языком низкого уровня, почти переносимым ассемблером, поэтому его структуры данных и языковые конструкции близки к металлу (структуры данных не имеют скрытых затрат - кроме дополнения, выравнивания и ограничений размера, наложенных аппаратным обеспечением и ABI ). Так что C действительно не имеет встроенной динамической типизации. Но если вам это нужно, вы можете принять соглашение, согласно которому все ваши значения являются агрегатами, начиная с некоторой информации о типе (например, с некоторого
enum
...); Использованиеunion
-s и (для массива-подобных вещей) гибкого элемента массива вstruct
содержащий также размер массива.(при программировании на C вы несете ответственность за определение, документирование и выполнение полезных соглашений - в частности, предварительных и постусловий и инвариантов; также динамическое выделение памяти в C требует
free
объяснения соглашений о том, кто должен иметьmalloc
зону с кучей памяти)Таким образом, для представления значений, которые представляют собой целые числа в виде блоков, или строки, или какой-либо символ , подобный Схеме , или векторы значений, вы концептуально будете использовать теговое объединение (реализованное как объединение указателей) - всегда начиная с типа type - например:
Чтобы получить динамический тип некоторого значения
Вот «динамическое приведение» к векторам:
и «безопасный метод доступа» внутри векторов:
Обычно вы определяете большинство коротких функций, описанных выше, как
static inline
в некотором заголовочном файле.Кстати, если вы можете использовать сборщик мусора от Boehm, вы сможете довольно легко кодировать его в каком-то высокоуровневом (но небезопасном) стиле, и таким образом выполняется несколько интерпретаторов Scheme. Вариативный векторный конструктор может быть
и если у вас есть три переменные
вы можете построить вектор из них, используя
make_vector(3,v1,v2,v3)
Если вы не хотите использовать сборщик мусора Boehm (или проектировать свой собственный), вы должны быть очень осторожны с определением деструкторов и документированием того, кто, как и когда память должна быть
free
-d; посмотрите этот пример. Таким образом, вы могли бы использоватьmalloc
(но затем проверить его на отказ) вместоGC_MALLOC
вышеупомянутого, но вам нужно тщательно определить и использовать некоторую функцию деструктораvoid destroy_value(value_t)
Сила C в том, чтобы быть достаточно низкоуровневым, чтобы сделать возможным выполнение кода, подобного описанному выше, и определить ваши собственные соглашения (особенно для вашего программного обеспечения).
источник