Гарантировано ли, что тип T [x] [y] имеет тот же формат памяти, что и T [x * y] в C?
До сих пор это было так, но после того, как я узнал, что компилятор может использовать данные для выравнивания для требований к архитектуре, например, я сомневаюсь. Поэтому мне интересно, имеет ли char[4][3]
тот же формат памяти, что и char[12]
. Может ли компилятор добавить прописку после части char[3]
, чтобы она была выровнена, так что весь массив принимает на самом деле 16 байт?
Фоновая история о том, что функция библиотеки берет связку строк с фиксированной длиной в параметре char*
, поэтому он ожидает непрерывный буфер без paddig, а длина строки может быть нечетной. Поэтому я думал, что объявляю массив char[N_STRINGS][STRING_LENGTH]
, а затем удобно заполняю его и передаю его функции, отбросив его до char*
. Пока это работает. Но я не уверен, что это решение переносимо.
Ответы
Ответ 1
Массив из M элементов типа A имеет все его элементы в смежных положениях в памяти, без заполнения байтов вообще.
Этот факт не зависит от природы А.
Теперь, если A - это тип "массив из N элементов, имеющих тип T", то каждый элемент в массиве T-типа снова будет иметь N смежных позиций в памяти. Все эти блоки из N объектов типа T также хранятся в смежных положениях.
Таким образом, результатом является существование в памяти элементов M * N типа T, хранящихся в смежных положениях.
Элемент [i][j]
массива сохраняется в позиции i*N+j
.
Ответ 2
Пусть рассмотрим
T array[size];
array[0]; // 1
1
формально определяется как:
Определение индексного оператора [] состоит в том, что E1[E2]
является идентичный (*((E1)+(E2)))
в соответствии с §6.5.2.1, пункт 2, взятый из стандартного черновика C N1570. При применении к многомерным массивам "массив, элементы которого являются массивами", мы имеем:
Если E является n-мерным массивом (n ≥ 2) с размерами i × j × ... × k
, тогда E (используется как отличное от lvalue) преобразуется в указатель к (n - 1) -мерному массиву с размерами j × . . . × k.
Следовательно, данный
E = T array[i][j]
и S = array[i][j]
, S
сначала преобразуется в указатель на одномерный массив размером j
, а именно T (*ptr)[j] = &array[i]
.
Если унарный * оператор применяется к этому указателю явно или неявно в результате подписи, результатом является ссылочный (n - 1) -мерный массив, который сам по себе преобразуется в указатель, если используется иначе, чем lvalue.
и это правило применяется рекурсивно. Мы можем заключить, что для этого массив n-dimensional
должен быть выделен смежно.
Из этого следует, что массивы хранятся в строчном порядке (последний индекс меняется быстрее).
в терминах логической компоновки.
Так как char [12]
должен храниться смежно и, следовательно, должен char [3][4]
, и поскольку они имеют одинаковое выравнивание, они должны быть совместимы, несмотря на то, что они являются технически разными типами.
Ответ 3
То, что вы называете типами, это не типы. Тип T
, который вы укажете в названии, будет (в данном случае) указателем на char.
Вы правы, что, когда дело доходит до структур, выравнивание является фактором, который может привести к добавлению дополнения, что может означать, что ваша структура занимает больше байтов, чем кажется на первый взгляд.
Сказав, что при распределении массива массив будет смежным в памяти. Помните, что при индексировании в массив array[3]
эквивалентно *(array + 3)
.
Например, следующая программа должна распечатать 12
:
#include <stdio.h>
int main() {
char array[4][3];
printf("%zu", sizeof(array));
return 0;
}
Ответ 4
Строго говоря, 2-D массив представляет собой массив указателей на 1-D массивы. В общем, вы не можете больше предполагать.
Я бы предположил, что если вы хотите непрерывный блок любого типа, тогда объявите смежный блок 1D, а не надейтесь на какой-либо конкретный макет из компилятора или среды выполнения.
Теперь компилятор, вероятно, выделит смежный блок для 2-D массива, когда он заранее знает размеры (т.е. они постоянны во время компиляции), но это не строгая интерпретация.
Помните int main( int argc, char **argv ) ;
Это char **argv
представляет собой массив указателей на указатели char.
В более общем программировании вы можете, например, malloc()
каждая строка в 2D-массиве отдельно и свопинг-строка так же просто, как замена значений этим указателям. Например:
char **array = NULL ;
array = malloc( 2 * sizeof( char * ) ) ;
array[0] = malloc( 24 ) ;
array[1] = malloc( 11 ) ;
strcpy( array[0], "first" ) ;
strcpy( array[1], "second" ) ;
printf( "%s\n%s\n", array[0], array[1] ) ;
/* swap the rows */
char *t = array[0] ;
array[0] = array[1] ;
array[1] = t ;
printf( "%s\n%s\n", array[0], array[1] ) ;
free( array[0] ) ;
free( array[1] ) ;
free( array ) ;