Основы
Массивы в c объявляются и доступны с помощью оператора []
. Так что
int ary1[5];
объявляет массив из 5 целых чисел. Элементы нумеруются с нуля, поэтому ary1[0]
- первый элемент, а ary1[4]
- последний элемент. Примечание 1: инициализация по умолчанию отсутствует, поэтому память, занятая массивом, может изначально содержать что угодно . Примечание 2: ary1[5]
обращается к памяти в неопределенном состоянии (которое может быть вам даже недоступно), поэтому не делайте этого!
Многомерные массивы реализованы в виде массива массивов (массивов (из ...)). Так
float ary2[3][5];
объявляет массив из 3 одномерных массивов по 5 чисел с плавающей запятой каждый. Теперь ary2[0][0]
- это первый элемент первого массива, ary2[0][4]
- последний элемент первого массива, а ary2[2][4]
- последний элемент последнего массива. Стандарт '89 требует, чтобы эти данные были смежными (с. A8.6.2 на стр. 216 моего K & R 2nd. Ed.), Но, похоже, не влияют на заполнение.
Попытка стать динамичной в более чем одном измерении
Если вы не знаете размер массива во время компиляции, вам нужно динамически распределить массив. Заманчиво попробовать
double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */
, который должен работать, если компилятор не дополняет распределение (вставьте дополнительное пространство между одномерными массивами). Может быть безопаснее идти с:
double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */
но в любом случае трюк приходит во время разыменования. Вы не можете написать buf[i][j]
, потому что buf
имеет неправильный тип. Вы также не можете использовать
double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */
потому что компилятор ожидает, что hdl4
будет адресом двойного адреса. Вы также не можете использовать double incomplete_ary4[][];
, потому что это ошибка;
Так что вы можете сделать?
- Выполните арифметику строк и столбцов самостоятельно
- Выделите и выполните работу в функции
- Использовать массив указателей (механизм, о котором говорит qrdl)
Сделай математику самостоятельно
Просто вычислите смещение памяти для каждого элемента следующим образом:
for (i=0; i<3; ++i){
for(j=0; j<3; ++j){
buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about
padding in this case */
}
}
Выделите и выполните работу в функции
Определите функцию, которая принимает необходимый размер в качестве аргумента и работает как обычно
void dary(int x, int y){
double ary4[x][y];
ary4[2][3] = 5;
}
Конечно, в этом случае ary4
является локальной переменной, и вы не можете ее вернуть: вся работа с массивом должна выполняться в функции, которую вы вызываете в функциях, которые it вызывает.
Массив указателей
Учтите это:
double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
hdl5[i] = malloc(5*sizeof(double))
/* Error checking */
}
Теперь hdl5
указывает на массив указателей, каждый из которых указывает на массив двойных чисел. Круто то, что вы можете использовать двумерную запись массива для доступа к этой структуре --- hdl5[0][2]
получает средний элемент первой строки --- но это, тем не менее, объект другого типа, чем двумерный массив, объявленный double ary[3][5];
.
Эта структура более гибкая, чем двумерный массив (поскольку строки не обязательно должны быть одинаковой длины), но доступ к ней, как правило, будет медленнее и требует больше памяти (вам нужно место для хранения промежуточных указателей).
Обратите внимание, что, поскольку я не настроил охрану, вам придется самостоятельно следить за размером всех массивов.
арифметика
c не поддерживает векторную, матричную или тензорную математику, вам придется реализовать ее самостоятельно или принести библиотеку.
Умножение с помощью скейлера, а также сложение и вычитание массивов одинакового ранга очень просты: просто зацикливайте элементы и выполняйте операцию по мере необходимости. Внутренние продукты так же прямо вперед.
Наружные продукты означают больше петель.