Я оставил это как комментарий, но так как это может быть непонятно, я опубликую свой ответ в коде ...
Как указано в моем комментарии, нет смысла выделять массив указателей - on64-битная машина, это будет 6 указателей, каждый из которых требует 8 байтов для указания на 7-байтовый блок данных - всего 104 байта (игнорируя добавленное заполнение распределителя для каждого выделения).
Вместо этого может быть достаточно одного выделения, выделяя 42 байта для хранения всех "волновых" строк и их байта NUL в одном блоке памяти (сохранение памяти при улучшении локальности).
int main(void) {
/* Assuming string "hello" */
const char *org = "hello";
/* Calculate length only once and store value */
const size_t len = strlen(org);
const size_t len_with_nul = len + 1;
/* Allocate `len` strings in a single allocation */
char *buf = malloc(len * len_with_nul);
/* Copy each string to it's place in the buffer */
for (size_t i = 0; i < len; ++i) {
/* position in the buffer */
char *pos = buf + (i * len_with_nul);
/* copy the NUL as well */
memcpy(pos, org, len_with_nul);
/* Wave... */
pos[i] = toupper(pos[i]);
}
/* Print result */
for (size_t i = 0; i < len; i++) {
char *pos = buf + (i * len_with_nul);
printf("s = %s\n", pos);
}
/* Free buffer */
free(buf);
return 0;
}
РЕДАКТИРОВАТЬ - Почему лучше использовать один блок памяти? :
В этом случае мы выделяем один «блок» памяти (blob / slice). Это дает ряд преимуществ:
Мы выполняем одно выделение и освобождение вместо большего количества выделений и освобождений.
Это повышает скорость за счет выполнения меньшего количества действий.
Мы также улучшаем локальность памяти, , которая минимизирует пропуски кэша ЦП и повышает производительность .
Мы используем меньше памяти.
Каждое выделение памяти имеет свою цену - нам нужен указатель для хранения адреса памяти для выделенной памяти. Указатель «стоит» 8 байтов на 64-битной машине и 4 байта на 32-битной машине.
Используя одно выделение, мы «платим» меньше.
Это верно, даже если мы игнорируемметаданные, прикрепленные к выделенному блоку памяти (для которого требуется память из распределителя памяти).
Следует отметить, что C на самом деле не заботится о содержимом блока памяти, это все нули и единицы . Значение, данное этим нулям и единицам, оставлено на усмотрение разработчика.
Даже функция printf
не заботится о содержимом памяти, которую она читает - она просто читает память в соответствии с форматированием, в котором она находиласьразработчик должен следовать инструкциям (%s
сообщает функции, что память связана со строкой, заканчивающейся NUL).
Существуют некоторые проблемы с выравниванием памяти, которые зависят от процессора и системы, но это не такприменять к однобайтовым строкам. Они применяются к многобайтовым типам (таким как short
, int
и long
). Поэтому нам не нужно беспокоиться о них в этом примере.
В этом смысле это в основном означает, что разработчик может свободно управлять памятью и содержимым по своему усмотрению (оставляя в стороне выравнивание памяти).
Это не означает, что всегда лучше выделять один блок памяти (если вам нужно использовать realloc
, вы можете предпочесть более мелкие блоки) ... но обычнолучше один блок памяти.