Всегда ли во время выполнения C ++ требуется malloc ()?

У меня есть приложение на C ++, работающее на голом металле, которое я хочу сделать как можно меньше.

Я нигде не использую динамическое выделение памяти. Я не использую функции STL. Я также переопределил все варианты «удалить» и «новый» с пустыми функциями. Тем не менее, когда я смотрю на отсортированный список символов, я вижу, что malloc () по-прежнему является одним из самых больших элементов в моем скомпилированном двоичном файле. Я мог бы уменьшить свой двоичный файл примерно на 25%, если бы смог избавиться от него.

Требует ли среда выполнения C ++, как правило, malloc () для работы закулисного типа?

(Я использую вилку Xilinx для gcc для архитектуры Microblaze, если это имеет значение)

5

Решение

Опора программы на malloc() может встречаться как в C, так и в C ++, даже если программа не использует их напрямую. Это качество реализации для компилятора и стандартной библиотеки, а не требование стандартов.

Это действительно зависит от того, как оба кода запуска компилятора (код, который настраивает так main() можно назвать) работает и как реализован стандартный код библиотеки.

Например, в C и C ++ код запуска (в размещенных средах) должен собирать информацию об аргументах командной строки (возможно, копировать в некоторый выделенный буфер), подключаться к стандартным файлам / потокам (например, std::cout а также std::cin в C ++ и `stdout и stdin в C). Любая из этих вещей может включать динамическое выделение памяти (например, для буферов, связанных со стандартными потоками) или выполнять код, который фактически не нужен программе.

4

Другие решения

C ++ имеет два вида реализаций: размещенный и автономный. Размещенные реализации предполагают, что malloc присутствует и часто используют его для внутренних целей. Автономные реализации предполагают, что только new функция присутствует, потому что она поддерживает ключевое слово C ++ new, но легко убедиться, что эта функция не вызывается.

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

g++ -ffreestanding -e _entry program.cpp

program.cpp возможно:

extern "C" int entry()
{
return 0;
}

extern "C" необходимо предотвратить искажение имени в C ++, что может затруднить определение имени entry во время ссылки. Тогда не используйте new, std::string, потоковый ввод / вывод, STL или тому подобное, и избегать at_exit,

Это должно дать вам контроль над кодом запуска и очистки и ограничить то, на что неявно может рассчитывать компилятор, будучи доступным из стандартной библиотеки. Обратите внимание, однако, что это может быть сложной средой. Вы не только предотвратите инициализацию куч, потоков ввода-вывода и т. П., Но также предотвратите установку исключений, RTTI и вызов статических конструкторов объектов хранения, среди прочего. Вам придется писать код или использовать библиотеки, чтобы вручную выбрать несколько функций C ++, которые вы, возможно, захотите использовать. Если вы идете по этому пути, вы можете просмотреть эту вики http://wiki.osdev.org/C%2B%2B. Вы можете, по крайней мере, вызвать глобальные конструкторы, что довольно легко сделать.

объяснение

Стандарт

В C ++ есть понятие «автономной реализации», в которой доступно меньше заголовков.

3.6.1 Основная функция [basic.start.main]

1 [snip] Это определяется реализацией, требуется ли программе в автономной среде для определения основной функции.

17.6.1.3 Отдельно стоящие реализации [соответствие]

1 Определены два вида реализаций: размещенный и автономный (1.4). Для размещенной реализации это
Международный стандарт описывает набор доступных заголовков.

2 Автономная реализация имеет определенный набор реализаций заголовков. Этот набор должен включать как минимум
заголовки показаны в таблице 16.

3 Поставляемая версия заголовка <cstdlib> должен объявить как минимум функции abort, atexit, at_quick_exit, exit и quick_exit (18.5). [Надрез]

Таблица 16 списков ciso646, cstddef, cfloat, limits, climits, cstdint, cstdlib, new, typeinfo, exception, initializer_list, cstdalign, cstdarg, cstdbool, type_traits, а также atomic,

Большинство из приведенных выше заголовков содержат простые определения типов, констант и шаблонов. Единственные, которые могут показаться проблемными typeinfo, exception, cstdlib, а также new, Первые два поддерживают RTTI и исключения соответственно, которые, как вы можете убедиться, отключены дополнительными флагами компилятора. cstdlib а также new Вы можете просто игнорировать, не звоня exit/at_exit и не используя new выражения. Причина избегания at_exit это может вызвать new внутренне. Ничто в отдельном фрагменте не должно вызывать cstdlib или же new,

Варианты

Самый важный вариант выше -e _entry, который дает вам контроль над тем, что запускается при запуске вашей программы.

-ffreestanding говорит компилятору и стандартной библиотеке (точнее, ее автономному фрагменту) не предполагать, что вся стандартная библиотека присутствует (даже если она все еще присутствует). Это может помешать генерации удивительного кода. Обратите внимание, что эта опция на самом деле не ограничивает какие заголовки доступны для вы. Вы все еще можете использовать iostreamНапример, хотя это может быть плохой идеей, если вы также изменили точку входа. Что он делает, так это предотвращает код, поддерживающий отдельно стоящая Заголовки от вызова чего-либо за пределами отдельно стоящая заголовки и не позволяет компилятору генерировать любые такие вызовы неявно.

2

Если вы используете какой-либо STL лайк std::lib или же std::map, Или даже std::cout, за сценой будет динамическое распределение памяти.

0

Это всегда будет нуждаться в malloc. Потому что двоичный файл должен быть загружен, а также общие библиотеки.

-1