#c #arrays #memory #lookup #lookup-tables
#c #массивы #память #поиск #поисковые таблицы
Вопрос:
Я создаю таблицу поиска, которая будет использовать два ключа / индекса для доступа к const char * .
Возьмем, к примеру:
const char* const arr[2][3]={
{"a", "bb", "ccc" },
{"" , "" , "dddd"}
}
Меня беспокоит использование памяти такой таблицы, где реальная таблица, которую я планирую создать, будет иметь масштаб arr[~ 60][~ 2000] с большинством пустых элементов. Насколько я понимаю, это создает постоянный указатель на 2d массив const char* . Как выглядит макет памяти для этого и сколько памяти используется? Будет ли использование памяти просто указателями на каждый элемент и содержимое каждого элемента?
например (включая символы завершения) 2*3*pointer_size (2 3 4) (1 1 5)
Любая обратная связь очень ценится, спасибо.
Примечание: изначально я планировал использовать unordered_map для таблицы подстановки, но для этого требуется создание пользовательского хэша при использовании пары целых чисел в качестве ключей. Независимо от того, является ли мой текущий подход правильным, я хотел бы знать конкретные последствия того, что я пытаюсь сделать выше.
Комментарии:
1. Компиляторы «большой тройки» хороши в буквальном разделении строк. Это означает, что все одинаковые строки литералов (например, пустая строка
""
) будут представлять собой одну строку в памяти, и все указатели будут указывать на эту единственную строку.2. Так почему бы не проверить это с
sizeof(arr)
помощью? Подробный ответ на вопрос «как используется память» дать сложно — компилятор сильно оптимизирует, вы можете ожидать, что по крайней мере все те же strnigs,""
которые будут использовать одну и ту же память.3. вы также можете создать
unordered_map<int, unordered_map<int, const char*>>
без пользовательской хэш-функции илиmap<pair<int,int>, const char*>>
.4. @Someprogrammerdude Что это за компиляторы big 3? Спасибо
5. GCC, Clang и MSVC. Если у вас нет особых потребностей (какая-то специальная встроенная платформа, поддержка старых систем или тому подобное), вы, вероятно, никогда не вступите в контакт с какой-либо другой.
Ответ №1:
Как используется память
Вы никогда не знаете, пока программа не будет скомпилирована. Только проверка ассемблерного кода может дать вам какие-либо ответы. Чтобы быть уверенным, я обычно компилирую с некоторой частью кода и без нее, проверяю вывод size
утилиты GNU и сравниваю, чтобы увидеть разницу, которую фрагмент кода делает с результирующим исполняемым файлом.
каков размер const char* const arr[][] ?
Я полагаю, вы знаете ответ — это sizeof(const char *) * first_dimension * second_dimension
байты, как в приведенном вами примере.
Как выглядит макет памяти для этого
Прежде всего, представленный код недопустим — для массива слишком много инициализаторов [2][3]
. После исправления ошибки, т.е. удалив ненужный инициализатор (потому что мне лень рисовать еще 3 блока), расположение памяти может выглядеть следующим образом:
const char *arr[2][3]
------- ------ ------- ------ ------ ------
| 0x... | | | | | |
------- ------ ------- ------ ------ ------
| | | | | |
| | | | | ----------->--- --- --- --- ----
| | | | | | d | d | d | d | 0 |
| | | | | --- --- --- --- ----
| --- | ------ -
| | | |
v--- ---- | ---->--- --- ---v----
| a | 0 | | | c | c | c | 0 |
--- ---- | --- --- --- ----
|
-->--- --- ----
| b | b | 0 |
--- --- ----
Каждый блок представляет один элемент, каждый блок прямо под const char *arr[2][3]
ним представляет один const char *
указатель, тогда как везде каждый блок представляет один char
байт. Обратите внимание, что я нарисовал arr[1][0]
и arr[1][1]
до нулевого байта "ccc"
— компилятор может оптимизировать и объединить несколько строковых литералов. Компилятор также может этого не делать — как сказано выше, только проверка сгенерированной сборки даст представление о том, что происходит на самом деле.
Обратите внимание, что если массив не является изменяемым, предпочтите его создать const
. Это делается const char *const arr[2][3]
при компиляции для контроллеров с голым металлом, что позволит компилятору поместить весь массив во флэш-память, уменьшая как инициализацию bss
, так и использование оперативной памяти. Строковые литералы должны помещаться в раздел только для чтения, потому что они неизменяемы. В c вы также можете рассмотреть возможность добавления constexpr
(или consteval
) в зависимости от ваших потребностей.
Однако вы никогда не узнаете, пока не проверите сборку. Если вы поместите определение arr
внутри функции, компилятор может решить воссоздать весь массив (также включая строковые литералы) в стеке при вводе функции godbolt example . В игру вступают различные варианты оптимизации компилятора.
Будет ли использование памяти просто указателями на каждый элемент и содержимое каждого элемента?
Ну, в целом да.