Гарантировано ли, что тип 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 ) ;