как узнать размер структуры c
Как получить размер поля структуры в си?
Скажем есть структура:
берёшь и получаешь
А без рантайм вычислений никак? На стадии компиляции.
А где здесь рантайм? O_o
В последнем варианте наверное будет более корректно использовать decltype вместо sizeof. Но так как вопрос все равно о си, то юзай первый вариант или что-то в этом роде.
Ну да, сглупил. Всем откликнувшимся спасибо.
Не вместо, а вместе. Весь день какую-то дичь выдаю=)
Просто на C++, любой версии, без выбоенов с std::declval:
Да ну, хипстерство. Если уж так нужно узнать, то нехай развертывает до стандартных типов, а когда получится ровно то, что в примере топика, то есть long, то уж sizeof этого то узнать можно многими способами.
Если кому интересно, то кроскомпиляторный метод определения размера типа заключается в попытке скомпилить: switch(sizeof(TYPE)) < case 4: break; case sizeof(TYPE): break; >Как ругнётся, значить угадали :))
Что за сборище наркоманов в этом треде изобретает sizeof?
Это вне функции работать не будет, по-моему.
Кстати, интересная тема: изобрести sizeof. У меня получилось так:
Интересно, что здесь может пойти не так.
Интересно, что здесь может пойти не так.
6.5.3.2 (Address and indirection operators), параграф 4
The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.(сноска)
В сноске написано:
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
Несколько слов о размере структур в С/С++ и о том, почему так получилось
Ниже по тексту термином «платформа» будем называть любой заданный набор из процессора, компилятора и операционной системы, под которой скомпилированный код будет запускаться.
И на весь этот зоопарк существует один-единственный Стандарт языка C. Как же это удалось? Вот тут и начинается самое интересное.
Язык C даёт очень мало ограничений на то, какими именно должны быть его базовые типы (char, short, int, long, float, double). Задаётся минимальное количество бит для некоторых типов (например, тип «char» должен быть минимум 8 бит, «int»/«short» — 16, «long» — 32), задаётся, что размеры базовых типов кратны размеру char’а, а сам размер char’а тождественно равен единице.
И, для примера: у программ под MS-DOS размер int’а и short’а совпадал, а сами эти типы были 16-битными. Для платформы Win32 размер int’а уже стал другим — 32 бита.
Другой важный момент из «мира железа», который обязан был учитываться при разработке Стандарта — это то, как процессор работает с памятью. Если не углубляться в то, как функционирует шина адреса, шина данных и микросхемы памяти, то для подавляющего числа архитектур существует следующее правило: если читаем/записываем одной командой N-байтовую величину, то её адрес обязан быть кратным N. То есть, если в память записывается 4-байтовый int, адрес должен делиться нацело на 4. Аналогично для 2-байтовых short’ов и т.п.
Что происходит, если это правило не выполняется? На разных платформах — по-разному: на некоторых (ARM’ы, например) происходит прерывание работы процессора и управление передаётся в ядро ОС, на других (DSP от TI) процессор просто молча запишет по ближайшему кратному адресу (т.е. не туда, куда сказали, а рядом), а на платформе x86 процессор (для некоторых типов данных) сделает так, как подразумевал программист, за счёт некоторого падения производительности. Важно тут понять одно — все процессоры работают одинаково, когда адрес является выравненным, и кто во что горазд — при невыполнении этого требования. Именно поэтому определение выравнивания (alignment) определяется в самом начале Стандарта и постоянно упоминается при описании модели памяти языка.
К чему же это всё приводит с точки зрения программиста? Лучше всего это показать на примере. Допустим, у нас есть такая структура:
С точки зрения программиста (неопытного) размер этой структуры равен sizeof(int)+sizeof(char)=4+1=5 (подразумеваем, что размер int’а — 4 байта). Однако, что произойдёт, если объявить массив из нескольких элементов такого типа? В памяти они будут располагаться так:
Или словами: поле iiii первого элемента располагается по выравненному адресу, а для второго элемента это уже не выполняется. Т.е. при размере структуры Foo равном 5 нельзя выполнить требования модели памяти С.
Для того же, чтобы и овцы были целы, и волки сыты, компилятор вставляет «невидимые» (для программиста) дополнительных 3 байта в конец структуры (так называемые padding bytes). Это приводит к тому, что размер структуры становится равен 8 и в памяти массив начинает располагаться так:
Однако, это ещё не всё, что делает с этой структурой компилятор. Кроме того, что размер Foo равен 8, компилятор также запоминает, что минимальное требование по выравниванию для всей этой структуры — 4 байта. Отличия легко показать на следующем примере:
struct Test1 <
char c;
Foo foo;
>;
Здесь интересны такие моменты: размер и Foo и Bar — одинаковый и равен 8-ми байтам. Но требования по выравниваю у Foo — 4 байта, а у Bar — 1. Это приводит к тому, что в структуре Test1, между ‘c’ и ‘foo’ компилятор вставляет дополнительных 3 байта, чтобы гарантировать, что поле ‘foo’ всегда будет начинаться по адресу, кратному 4-м. В структуре же Test2 ничего такого делать не надо, в результате — sizeof(Test1) равен 12, а sizeof(Test2) — 9. То есть получили разный результат, комбинируя «кирпичики» одного и того же размера!
Узнать размер структуры
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Как узнать размер массива или структуры
Привет всем! Раньше писал на C++, сейчас потихоньку начал изучать C#. Есть какая-нибудь.
Как узнать размер строки как размер массива
const char* test_str = «01234\x00 789»; //очевидно, что strlen(test_str) == 5; sizeof(test_str).
Размер структуры
Добрый день! Дорогие форумчане, помогите если не трудно. такая проблема: есть структура.
Размер структуры
Всем доброго дня! Не могу понять. Вот код. struct T < char i; int a; int b; int c;.
А зачем вам размер managed структуры? Всё правильно он требует.
Добавлено через 8 минут
Оптимизации чего? Как вы определили что данная оптимизация вообще требуется?
Да, но есть четкие правила, по которым определяется управляемая структура или нет.
Конечно зависит, размер указателей то разный.
Значит не полностью, раз sizeof и Marshal.SizeOf () с ними не работают.
Добавлено через 6 минут
Еще есть Unsafe.SizeOf из пакета System.Runtime.CompilerServices.Unsafe.
Почти идентичный код на C++ работает в 2,5 раза быстрее чем C#, и там, и там включены почти все возможные оптимизации, используются указатели, минимизировано число операций, не вызываются функции (call), целочисленная арифметика, предварительный расчет часто повторяющихся формул и т.д.
Тогда такой вопрос: может ли структура быть неуправляемой без слова unsafe? Я вроде думал что нет. Если да, то правильно ли я понял, что если внутри используются только значимые стандартные типы и/или другие неуправляемые структуры, то она считается неуправляемой? Где можно про это почитать подробно, со всеми нюансами?
Любые ссылки (например строка, массив, делегат) по сути являются указателями если являются полем структуры.
Например размер этой структуры на x64 равен 8 (из-за ссылки на массив):
struct Foo
<
public byte[] data;
public Foo(int x)
<
data = new byte[100];
>
>
Определить размер структуры
Нужно определить размер следующей структуры
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Определить размер структуры
Здравствуйте. Как определить размер структуры без использования функции sizeof()?
Размер структуры
Помогите узнать размер структуры: enum UnitType < Swordsman, // мечник Archer, //.
вообще-то эти данные прицеплены к структуре (при записи в файл их размер будет иметь значение)
Добавлено через 8 минут
Не говори ерунды, эти данные находятся в памяти, выделенной динамически, и не могут быть записаны вместе с записью структуры.
Добавлено через 6 минут
не могут, но должны быть записаны
там может и не быть данных
надо определить размер всего, что относится к этой структуре
Добавлено через 2 минуты
я про размер структуры говорил (изначально)
её размер никак не может быть 4 или 8
Добавлено через 6 минут
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Размер структуры
Добрый день! Дорогие форумчане, помогите если не трудно. такая проблема: есть структура.
Размер структуры
Всем доброго дня! Не могу понять. Вот код. struct T < char i; int a; int b; int c;.
StructLayout и размер структуры
Доброго времени суток. Подскажите, почему экземпляр структуры MyStruct занимает 4 байта, а.
Работа со структурами в C#
Расположение
В большинстве случаев вы можете описать и использовать структуру без знания о том, как она реализована — особенно как расположены в памяти ее поля. Если вы должны создать структуру для использования ее другими приложениями, или сами должны использовать чужую структуру, то в этом случае вопросы памяти становятся важными. Как вы думаете, каков размер следующей структуры?
Разумный ответ — 8 байт, просто сумма размеров всех полей. Однако если вы попытаетесь узнать размер структуры:
Для работы вам понадобится ссылка на InteropServices:
Для ручного расположения полей в памяти используется атрибут StructLayout. Например:
Это заставляет компилятор располагать поля последовательно, в порядке объявления, что и делает по умолчанию. Другими значениями атрибута являются значение Auto, которое позволяет компилятору самому определять порядок размещения полей, и значение Explicit, которое позволяет программисту указать размер каждого поля. Тип Explicit часто используется для последовательного расположения без упаковки, но в большинстве случаев проще использовать параметр Pack. Он сообщает компилятору о том, сколько должно выделяться памяти и как должны быть выровнены данные. Например, если вы укажете Pack=1, тогда структура будет организована таким образом, что каждое поле будет находиться в границах одного байта и может быть считано побайтно — т.е. никакого упаковывания не требуется. Если вы измените объявление структуры:
… то обнаружите, что теперь структура занимает ровно 8 байт, что отвечает последовательному расположению полей в памяти без дополнительных «упаковывающих» байт. Именно таким образом нужно работать с большинством структур, объявленных в Windows API и C/C++. В большинстве случаев вам не придется использовать другие значения параметра Pack. Если вы установите Pack=2, тогда обнаружите, что структура станет занимать 10 байт, потому что будет добавлено по одному байту к каждому однобайтовому полю, чтобы данные могли читаться кусками по 2 байта. Если установить Pack=4, размер структуры увеличится до 12 байт, чтобы структура могла быть прочитана блоками по 4 байта. Дальше значение параметра перестанет учитываться, потому что размер Pack игнорируется, если он равен или превышает выравнивание, использующееся в данном процессоре, и составляющее 8 байт для архитектуры Intel. Расположение структуры в памяти при разных значения Pack показано на рисунке:
Стоит также упомянуть, что может изменить способ упаковки структуры, изменяя порядок полей в ней. Например, при изменении порядка полей на:
… структуре не понадобится упаковка, она и так займет ровно 8 байт.
Если быть точным
Если вам нужно точно указать, сколько памяти будет выделено для каждого поля, используйте тип расположения Explicit. Например:
Так вы получите 8-байтовую структуры без дополнительны выравнивающих байтов. В данном случае это эквивалентно использованию Pack=1. Однако использование Explicit позволяет вам полностью контролировать память. Например:
Эта структура займет 16 байт, вместе с дополнительными байтами после поля b. До версии C# 2.0, тип Explicit использовался в основном для указания буферов с фиксированными размерами при вызове сторонних функций. Вы не можете объявить массив фиксированной длины в структуре, потому что инициализация полей запрещена.
Этот код выдаст ошибку. Если вам нужен массив длиной 10 байт, вот один из способов:
Таким образом, вы оставляете 10 байт для массива. Тут существует ряд интересных нюансов. Первое, почему нужно использовать смещение в 8 байт? Причина в том, что вы не можете начать массив с нечетного адреса. Если вы воспользуетесь смещением в 7 байт, то увидите ошибку времени выполнения, сообщающую о том, что структура не может быть загружена из-за проблем с выравниванием. Это важно, потому что при использовании Explicit вы можете столкнуться с проблемами, если не будете понимать, что вы делаете. Второй момент связан с тем, что в конец структуры добавляются дополнительные байты, чтобы размер структуры был кратен 8 байтам. Компилятор все еще участвует в том, как структура будет размещена в памяти. Конечно, на практике, любая внешняя структура, которую вы попытаетесь конвертировать в структуру C#, должна быть корректна выровнена.
Наконец, стоит упомянуть, что вы не можете обратиться к 10-байтовому массиву, используя имя массива (например, buffer[1]), потому что C# думает, что массиву не назначено значение. Поэтому если вы не можете использовать массив и это вызывает проблему с выравниванием, гораздо лучше объявить структуру так:
Для доступа к массиву придется воспользоваться арифметикой на указателях, что является unsafe кодом. Чтобы под структуру было выделено фиксированное количество байт, используйте параметр Size в атрибуте StructLayout:
Сейчас в C# 2.0 массивы фиксированного размера разрешены, поэтому все вышеприведенные конструкции в общем-то необязательны. Стоит заметить, что массивы фиксированной длины используют тот же механизм: выделение фиксированного числа байт и указатели (что тоже является небезопасным). Если вам нужно использовать массивы для вызова функций из библиотек, возможно, лучшим способом будет явный маршалинг массивов, который считается «безопасным». Давайте рассмотрим все три упомянутых способа.
Вызовы API
В качестве примера структуры, которая требует выравнивания, мы можем использовать функцию EnumDisplayDevices, которая определена следующим образом:
Это довольно просто конвертируется в C#:
Структура DISPLAY_DEVICE определена так:
Понятно, что она содержит четыре символьных массива с фиксированной длиной. Используя тип выравнивания Explicit, перепишем структуру в C#:
Обратите внимание на использования параметра Size для указания места, необходимого для хранения поля DeviceKey. Теперь если использовать эту структуру при вызове функции:
… то все, к чему вы можете обратиться напрямую — это первые символы массивов. Например, DeviceString содержит первый символ строки информации об устройстве. Если вы хотите получить остальные символы из массива, нужно получить указатель на DeviceString и использовать арифметику на указателях, чтобы пройти по массиву.
При использовании C# 2.0 самым простым решением является использовать в структуре массивы:
Обратите внимание, что структура должна быть помечена модификатором unsafe. Теперь после API вызова мы можем получить данные из массивов без использования указателей. Впрочем, неявно они все-таки используются, и любой код, обращающийся к массивам, должен быть помечен как небезопасный.
Третий и последний метод заключается в кастомном маршалинге. Многие C# программисты не понимают, что суть маршалинга заключается не только в том, как данные о типах передаются в библиотечные вызовы, — это еще и активный процесс, который копирует и изменяет управляемые данные. Например, если вы захотите передать ссылку на типизированный массив, вы можете передать его по значению, и система сконвертирует его в массив фиксированной длины и обратно в управляемый массив без дополнительных действий с вашей стороны.
В этом случае все, что нам остается сделать, это добавить атрибут MarshalAs, указывающий типа и размер массивов:
В этом случае при вызове библиотечной функции поля передаются путем создания неуправляемых массивов нужной длины внутри копии структуры, которая и передается в вызов. Когда функция завершает свою работу, неуправляемые массива конвертируются в управляемые символьные массивы и ссылки на них присваиваются полям структуры. В результате, после вызовы функции вы обнаружите, что структура содержит массива нужного размера, заполненные данными.
В случае вызова функций сторонных библиотек, использование кастомного маршалинга является лучшим решением, поскольку при этом используется безопасный код. Хотя вызов сторонних функций с помощью p/Invoke и не является безопасным в общем смысле.
Сериализация структур
Теперь, после того как мы рассмотрели довольно сложные вопросы, связанные с размещением структур в памяти, самое время узнать, как получить все байты, составляющие структуру. Иными словами, как сериализовать структуру? Существует много способов сделать это, чаще всего используется метод Marshal.AllocHGlobal для выделения памяти в куче под неуправляемый массив. После этого все делается функциями, работающими с памятью, такими как StructToPtr или Copy. Пример:
Фактически, надобность в стольких действиях отсутствует, проще переместить байты структуры напрямую в байтовый массив без использования промежуточного буфера. Ключевым объектом в этом способе является GCHandle. Он возвратит хэндл Garbage Collector’а, и вы можете использовать метод AddrOfPinnedObject для получения стартового адреса структуры. Метод RawSerialize может быть переписан следующим образом:
Этот способ проще и быстрее. Вы можете использовать эти же методы для десериализации данных из байтового массива в структуру, однако более полезно будет рассмотреть решение проблемы чтения структуры из потока.
Чтение структур из потоков
Иногда возникает потребность зачитать структуру, возможно написанную на другом языке, в C# структуру. Например, вам нужно прочитать bitmap-файл, который начинается с заголовка файла, затем следует заголовок битмапа и затем собственно битовые данные. Структура заголовка файла выглядит так:
Функция, которая будет читать любой поток, и возвращать структуру, может быть написана без использования обобщений:
Для передачи данных здесь используется GCHandle. Новое в этом коде — использование параметра, указывающего на тип структуры. К сожалению, нельзя использовать этот тип для возвращаемого значения, поэтому после вызова функции необходимо преобразовать ее результат:
Если мы хотим избежать преобразования, тогда нужно использовать обобщенный метод:
Обратите внимание, что теперь мы должны преобразовать объект, возвращаемый методом PtrToStructure, в самом методе, а не в месте вызова, который теперь выглядит следующим образом:
Приятно наблюдать, насколько лучше выглядит использование обобщенного метода.
Ручной маршалинг
Маршалинг так хорошо работает в подавляющем количестве случаев, что можно вообще забыть о его существовании. Однако если вы сталкиваетесь с чем-то необычным, вы можете удивиться, что происходит, когда маршалинг перестает работать. Например, некоторым API вызовам нужно передавать указатель на указатель на структуру. Вы уже знаете, как передать указатель на структуру — это просто передача по ссылке — и поэтому вам может показаться, что передать указатель на указатель тоже просто. Однако все сложнее, чем вы ожидаете. Давайте посмотрим.
В функции AVIFileCreateStream два последних параметра передаются как указатели на IntPtr и структуру соответственно:
Для вызова это функции вы бы написали:
Основываясь на предыдущих примерах, кажется более легким изменить передачу указателя на структуру самим указателем. Казалось бы, что может быть неверного в следующем объявлении:
Однако если вы попытаетесь передать адрес закрепленной (pinned) структуры:
Причина этой ошибки заключается в том, что хотя вы и передаете указатель на адрес начала структуры, эта структура располагается в управляемой памяти, а неуправляемый код не может получить к ней доступ. Мы забываем о том, что стандартный маршалинг делает еще кое-какую работу при создании указателей. Перед тем, как создать указатели, для всех параметров, передаваемых по ссылке, создаются полные копии в неуправляемой памяти. После окончания вызова данные из неуправляемой памяти копируются обратно в управляемую.
Написать подобную функцию, которая делает работу маршалинга, нетрудно и очевидно полезно:
Тут просто возвращается IntPtr на область в куче, которая содержит копию данных. Единственный неприятный момент заключается в том, что нужно помнить об освобождении выделенной памяти:
Приведенный код работает в точности как стадартный маршалинг. Однако не забудьте, что lpstruct передается по значению как integer. Для того чтобы скопировать результат обратно в структуру, понадобится еще один метод:
Теперь, после того как мы реализовали ручной маршалинг указателя в структуру, нам нужно получить указатель на указатель на структуру. К счастью, нам не потребуется писать нового кода, потому что наша функция преобразования структуры в указатель может преобразовывать любой тип данных в неуправляемый указатель — включая и сам указатель.
В качестве примера возьмем функцию AVISaveOption, т.к. она принимает два указателя на указатель в качестве параметров:
Фактически параметр ppavi — это указатель на хэндл (который в свою очередь является указателем), а ppOptions — указатель на указатель на структуру. Для вызова этого метода нам понадобится структура:
Определение этой структуры можно посмотреть в документации по стандарту AVI. На следующем шаге нам нужно получить маршализованный указатель на структуру:
… а затем указатель на указатель:
… а за ним указатель на хэндл:
Теперь вызов функции:
… где остальные параметры не представляют интереса, сведения о них могут быть найдены в документации.
После вызова функции все, что остается сделать, это переправить данные из неуправляемого буфера в структуру:
Обратите внимание, нужно использовать указатель на саму структуру, а не указатель на указатель! Ну и в конце освобождаем память:
Все это может показаться сложным. Использование указателей на указатели не является простой вещью, именно поэтому C# требует, чтобы код, работающий с указателями был помечен как небезопасный (unsafe).
С другой стороны, общие принципы работы довольно просты. Когда вы передаете что-то по ссылке, это содержимое копируется в неуправляемую память, и адрес на новый участок памяти передается в вызов функции.
Обычно стандартный маршалинг берет всю работу на себя. Однако если вам нужно что-то сверх этого, вы можете управлять всем копированием вручную.