Можно ли поручить C не инициализировать глобальные массивы нулем?
Я пишу встроенное приложение, и почти вся моя оперативная память используется глобальными байт-массивами. Когда моя прошивка загружается, она начинается с перезаписи всего раздела BSS в оперативной памяти с нулями, что в моем случае совершенно не нужно.
Могу ли я каким-то образом указать компилятору, что ему не нужно инициализировать нуль определенные массивы? Я знаю, что это также можно решить, объявив их указателями и используя malloc (), но есть несколько причин, по которым я хочу этого избежать.
7 ответов:
Проблема заключается в том, что стандарт Cобеспечивает нулевую инициализацию статических объектов. Если компилятор пропустит его, он не будет соответствовать стандарту C.
На компиляторах встраиваемых систем обычно присутствует нестандартная опция "compact startup" или аналогичная. Если этот параметр включен, инициализация статических/глобальных объектов не будет происходить вообще нигде в программе. Как это сделать, зависит от вашего компилятора или, в данном случае, от вашего порта gcc.
Если вы упомянете, в какой системе вы находитесь используя, кто-то может предоставить решение для этого конкретного порта компилятора.
Это означает, что любая статическая/глобальная переменная (static storage duration), которую вы инициализируете явно, больше не будет инициализироваться. Вам придется инициализировать его во время выполнения, то есть вместо
static int x=1;вы должны будете написатьstatic int x; x=1;. Довольно часто встраиваемые программы на языке Си пишутся таким образом, чтобы сделать их совместимыми с компиляторами, где статическая инициализация отключена.
Оказалось, что линкер-скрипт, входящий в мою цепочку инструментов, имеет специальный раздел "noinit".
__attribute__ ((section (".noinit")))/ ** заставляет компилятор автоматически не обнулять заданную глобальную переменная при запуске, так что текущее содержимое ОЗУ сохраняется. В большинстве условий это значение будет случайным из-за поведение энергонезависимой памяти после отключения питания, но может использоваться в некоторых конкретных случаях. обстоятельства, как передача ценностей обратно после системного сторожевого пса сброс.
Таким образом, все глобальные переменные, помеченные этим атрибутом, не будут инициализированы нулем во время загрузки.
Стандарт C требует, чтобы глобальные данные были инициализированы до нуля.
Возможно, что некоторые производители встраиваемых систем предоставляют способ обойти эту опцию, но, безусловно, есть много типичных приложений, которые просто потерпели бы неудачу, если бы "инициализация до нуля" не была выполнена.
Некоторые компиляторы также позволяют иметь дополнительные разделы, которые могут иметь другие характеристики, чем раздел "bss".
Другой альтернативой является, конечно, "сделать свое собственное распределение". Поскольку это встроенная система, я полагаю, что у вас есть контроль над тем, как приложение и данные загружаются в оперативную память, в частности, какие адреса используются для этого.Таким образом, вы можете использовать указатель и просто использовать свой собственный механизм для назначения указателя области памяти, которая зарезервирована для того, для чего вам нужны большие массивы. Это позволяет избежать довольно сложного использования
malloc- и дает вам более или менее постоянный адрес, так что вам не нужно беспокоиться о том, чтобы найти, где ваш данные будут позже. Это, конечно, окажет небольшое влияние на производительность, так как добавляет еще один уровень косвенности, но в большинстве случаев это исчезает, как только массив используется в качестве аргумента функции, так как он распадается на указатель в этой точке в любом случае.
Есть несколько обходных путей, таких как:
- Удаление раздела BSS из двоичного файла или установка его размера равным 0 или 1. Это не будет работать, если загрузчик должен явно выделить память для всех разделов. Это будет работать, если загрузчик просто копирует данные в оперативную память.
- объявление массивов как
externв C-коде и определение символов (вместе с их адресами) либо в ассемблерном коде в отдельных файлах ассемблера, либо в скрипте компоновщика. Опять же, если память должна быть явно нет, это не сработает.- исправление или удаление соответствующего кода обнуления BSS либо в загрузчике, либо в коде запуска, который выполняется в вашей программе до
main().
Все встроенные компиляторы должны разрешать сегмент noinit. В компиляторе IAR AVR переменные, которые вы не хотите инициализировать, просто объявляются следующим образом:
_ _ no _ init uint16_t foo;
Наиболее полезная причина для этого состоит в том, чтобы позволить переменным сохранять свои значения над сторожевым псом или обнулением сброса, что, конечно, не происходит в компьютерных программах на языке Си, следовательно, его отсутствие в стандарте С.
Просто найдите инструкцию по компилятору для "noinit" или что-то в этом роде схожий.
Вы уверены, что двоичный формат действительно включает раздел BSS в двоичном файле? В двоичных форматах, с которыми я работал, BSS - это просто целое число, которое сообщает ядру / загрузчику, сколько памяти нужно выделить и обнулить.
В языке Си определенно нет общего способа получить неинициализированные глобальные переменные. Это будет функция вашей компиляторной / компоновочной / исполняющей системы и весьма специфичная для нее.
Comments