12

До сих пор это было так, но после того, как я узнал, что компилятор может помещать данные, чтобы выровнять его для требований к архитектуре, например, я сомневаюсь. Поэтому мне интересно, имеет ли char[4][3] такой же формат памяти, что и char[12]. Может ли компилятор добавить прокладку после части char[3], чтобы она была выровнена так, что весь массив принимает на самом деле 16 байтов?Гарантируется ли тот факт, что тип T [x] [y] имеет тот же формат памяти, что и T [x * y] в C?

История вопроса о том, что функция библиотеки берет связку строк фиксированной длины в параметре char*, поэтому он ожидает непрерывный буфер без paddig, а длина строки может быть нечетной. Поэтому я подумал, что объявляю массив char[N_STRINGS][STRING_LENGTH], а затем удобно заполнил его и передал его функции, отбросив его до char*. Пока это работает. Но я не уверен, что это решение переносимо.

+4

Массив смежно распределен. Между элементами массива IMHO не должно быть никаких дополнений. – ameyCU

+4

C массивы должны быть непрерывными, без отступов между элементами массива. Поэтому ни 'char [4] [3]', ни 'char [12]' не может содержать отступы, а 'sizeof' будет' 12 * sizeof (char) 'для обоих. –

+3

Также см. [Матрицы Can C содержат отступы между элементами?] (Http://stackoverflow.com/questions/1066681/can-c-arrays-contain-padding-in-between-elements) –

ответ

3

Что вы имеете в виду, поскольку типы не являются типами. Тип T, упоминаемый в заголовке, будет (в данном случае) указателем на символ.

Вы правы, что, когда дело доходит до структур, выравнивание является фактором, который может привести к добавлению дополнения, что может означать, что ваша структура занимает больше байтов, чем кажется на первый взгляд.

Сказав, что при распределении массива массив будет смежным в памяти. Помните, что при индексировании в массив array[3] эквивалентен *(array + 3).

Например, следующая программа должна распечатать 12:

#include <stdio.h> 

int main() { 
    char array[4][3]; 
    printf("%zu", sizeof(array)); 
    return 0; 
} 
+1

Массивы * всегда * смежные в памяти, вот и вся идея. –

+3

'T' в заголовке - это тип, а не переменная. Остальная часть вопроса использует 'char' для' T'. – interjay

+0

@jens Хорошая точка, мое использование «статически» было излишним и, скорее всего, вводило читателя в заблуждение относительно отсутствия несуществующей дихотомии. – fvgs

-4

Строго говоря массив 2-D представляет собой массив указателей на 1-D массивов. В общем, вы не можете больше предполагать.

Я бы мнение, что если вы хотите непрерывного блок любого типа, то объявить непрерывный блок 1D, а не надеяться на какую-либо конкретную компоновку с компилятора или выполнения.

Теперь компилятор, вероятно, выделит смежный блок для 2-мерного массива, когда он заранее знает размеры (т. Е. Они постоянны во время компиляции), но это не строгая интерпретация.

int main(int argc, char **argv) ; Запомнить

Это char **argv представляет собой массив указателей на Чаре указатели.

В более общем программировании вы можете, например, 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) ; 
+0

Учитывая характер вопроса, здесь важно отметить, что массив символов, заданный 'array [0]', не является смежным с массивом символов, заданным 'array [1]'. Более того, не рекомендуется динамически выделять массивы, если они могут быть статически распределены. – fvgs

+8

"_ Строго говоря, 2-мерный массив представляет собой массив указателей на 1-мерные массивы.« Нет, это не так! Массивы _ ** не являются ** _ указателями. – edmz

+3

Не рекламируйте псевдо 2D-массивы, то есть указатели на указатели. Это очень неправильно и должно быть действительно изгнано из всех книг и вводных курсов. –

10

Массив элементов M типа A имеет все его элементы в смежных положениях в памяти, без заполнения байтов вообще. Этот факт не зависит от природы A.

Теперь, если A - это тип «массив из N элементов, имеющих тип T», то каждый элемент в массиве T-типа снова будет иметь N смежных позиций в памяти. Все эти блоки из N объектов типа T также хранятся в смежных положениях.

Таким образом, результатом является существование в памяти элементов M * N типа T, хранящихся в смежных положениях.

Элемент [i][j] массива хранится в позиции i*N+j.

8

Рассмотрим

T array[size]; 
array[0]; // 1 

1 формально определяется как:

Определение оператора индекса [] является то, что E1[E2] является идентичен (*((E1)+(E2)))

в §6.5 .2.1, пункт 2, взятый из стандартного проекта C N1570. При нанесении на многомерные массивы, «массив, элементы которого являются массивами», мы имеем:

Если Е является п-мерный массив (N ≥ 2) с размерами i × j × ... × k, то Е (используется как иное чем именующее) преобразуется в указатель к (п - 1) n-мерного массива с размерами j × . . . × k.

Поэтому, учитывая E = T array[i][j] и S = array[i][j], S сначала преобразуется в указатель на одномерный массив размера j, а именно T (*ptr)[j] = &array[i].

Если унарный оператор * применяются к этому указателю явно или неявно в результате индексации, то результата упоминаемого (п - 1) -мерный массив, который сам по себе является преобразованными в указатель, если используется иначе, чем lvalue.

и это правило применяется рекурсивно. Мы можем заключить, что для этого массив n-dimensional должен быть выделен смежно.

Из этого следует, что массивы хранятся в строчном порядке (последний индекс отличается самым быстрым).

с точки зрения логики.

С char [12] должен храниться смежно и поэтому должен быть char [3][4], и поскольку они имеют одинаковое выравнивание, они должны быть совместимы, несмотря на то, что они являются технически разными типами.

+1

Центральным моментом моего сомнения является следующее: Имеются ли реализации для прокладки * конца * массив, чтобы округлить его до выравнивания? Если ответ да, то я могу представить, что 'sizeof (char [3]) == 4' на выровненном словом процессоре, что упрощает обращение к элементам массива' char [4] [3] '. Это означает, что субмассивы содержат подразумеваемое дополнение, а если вы используете 'char [12]', то реализация требуется для упаковки данных без заполнения. – Calmarius

+0

Если мы говорим о выравнивании, '_Alignof (char [N])' должно быть '_Alignof (char)', которое равно 1. Поскольку 'sizeof (char [3]) == 3', вам нужно будет переосмыслите свои вопросы, поскольку исходная точка неверна. – edmz

+0

@black: задано 'struct x {char arr [3] [4]; }; struct y {char arr [4] [3]; }; будет ли стандарт гарантировать, что '_Alignof (struct x)' и '_Alignof (struct y)' будут равны? – supercat