Как используется память и каков размер const char* const arr[][]? c

#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 . В игру вступают различные варианты оптимизации компилятора.

Будет ли использование памяти просто указателями на каждый элемент и содержимое каждого элемента?

Ну, в целом да.