Массивы в C: объявление, инициализация, доступ, многомерные массивы и передача в функции
Что такое массив?
Массив — непрерывная область памяти, хранящая фиксированное число элементов одного типа. Элементы доступны по индексу (0-базированный).
Ключевые свойства:
- Размер фиксирован на этапе компиляции (кроме VLA — C99).
- Элементы лежат подряд в памяти:
&arr[i+1] == &arr[i] + sizeof(T). - Имя массива в выражениях «распадывается» в указатель на первый элемент (
arr == &arr[0]).
Объявление и определение
тип имя[размер]; // неинициализированный (мусор, если локальный)
тип имя[размер] = {значения}; // с инициализатором
тип имя[] = {значения}; // размер выводится из инициализатора
Примеры
int scores[100]; // 100 int, неинициализированы
double prices[5] = {10.5, 20.0, 30.0}; // 3 инициализированы, остальные 0
char name[] = "Hello"; // размер 6 (включая \0)
int matrix[3][4] = { // 2D массив
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
Инициализация
- Лишние инициализаторы — ошибка.
- Меньше инициализаторов — остальные обнуляются (для статических/глобальных всегда, для автоматических — если есть хотя бы один инициализатор).
- Обозначенные инициализаторы (C99):
int arr[10] = {[5] = 100, [2] = 50};.
Доступ к элементам
int arr[5] = {10, 20, 30, 40, 50};
int x = arr[2]; // чтение: 30
arr[2] = 99; // запись
// arr[5] — UB (выход за границы!)
Границы НЕ проверяются ни компилятором, ни runtime. Выход за границы — UB (краш, повреждение данных, уязвимость).
Массивы и указатели
В большинстве контекстов arr преобразуется в int * (указатель на первый элемент). Исключения:
sizeof(arr)— размер всего массива в байтах.&arr— указатель на массив целиком (int (*)[5]).- Инициализация строковым литералом:
char s[] = "abc";.
int arr[5] = {1,2,3,4,5};
int *p = arr; // p указывает на arr[0]
p[2] == arr[2]; // true, эквивалентно *(p+2)
*(arr + 2) == 3; // арифметика указателей
Многомерные массивы
Массив массивов. В памяти — единый непрерывный блок (row-major order).
int matrix[3][4]; // 3 строки, 4 столбца = 12 int
matrix[1][2] = 42; // строка 1, столбец 2
// Инициализация
int m[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// Доступ через указатели
int (*row)[4] = matrix; // указатель на массив из 4 int (строку)
int *flat = &matrix[0][0]; // указатель на первый элемент (плоский вид)
flat[5] == matrix[1][1]; // true (индекс 1*4+1=5)
VLA (Variable Length Arrays) — C99, опционально в C11+
void func(int n) {
int vla[n]; // размер определяется во время выполнения
// работает как обычный массив, но на стеке!
// ОСТОРОЖНО: большие n → stack overflow
}
Не рекомендуется для больших/непредсказуемых размеров. Используйте malloc.
Передача массивов в функции
Массив передаётся как указатель на первый элемент. Размер не передаётся автоматически.
Варианты сигнатуры (эквивалентны):
void func(int arr[]); // как документация: ожидается массив
void func(int *arr); // реальность: указатель
void func(int arr[10]); // размер игнорируется компилятором
void func(int arr[static 10]); // C99: обещание, что arr указывает минимум на 10 элементов (оптимизация)
Передача размера
void print_array(int *arr, size_t n) {
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
}
int main() {
int a[] = {1,2,3,4,5};
print_array(a, sizeof(a)/sizeof(a[0])); // вычисляем размер ВНЕ функции
}
Передача 2D массива
Нужно знать все размерности кроме первой:
void print_matrix(int rows, int cols, int mat[][cols]) { // VLA параметр (C99)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) printf("%d ", mat[i][j]);
printf("\n");
}
}
// Или через указатель на массив:
void print_matrix2(int rows, int cols, int (*mat)[cols]) { ... }
// Для фиксированного размера:
void print_fixed(int mat[][4]) { ... } // только 4 столбца!
Возврат массива из функции
Нельзя вернуть локальный массив (указатель на стек — висячий). Варианты:
- Статический массив (не потокобезопасно):
static int arr[10]; return arr; - Динамическое выделение:
int *arr = malloc(n * sizeof(int)); return arr;(вызывающий долженfree). - Структура-обёртка:
struct Vec { int *data; size_t len; }; - Вызывающий передаёт буфер:
void fill(int *out, size_t n).
Строки как массивы char
char str[] = "Hello"; // изменяемый массив на стеке, размер 6
char *ptr = "Hello"; // указатель на строковый литерал (read-only память!)
// str[0] = 'h'; // OK
// ptr[0] = 'h'; // UB! (сегфолт на современных ОС)
Частые ошибки
- Выход за границы:
for (i=0; i<=n; i++)вместоi. - Использование неинициализированного массива.
- Возврат указателя на локальный массив.
sizeof(arr)внутри функции — даст размер указателя, не массива!- Присваивание массивов:
arr1 = arr2— ошибка компиляции. Используйтеmemcpyили цикл.
Пример: работа с массивом
#include <stdio.h>
#include <stdlib.h>
void read_array(int *arr, size_t n) {
for (size_t i = 0; i < n; i++) {
if (scanf("%d", &arr[i]) != 1) arr[i] = 0;
}
}
double average(const int *arr, size_t n) {
long long sum = 0;
for (size_t i = 0; i < n; i++) sum += arr[i];
return (double)sum / n;
}
int main() {
size_t n = 5;
int *data = malloc(n * sizeof(int));
if (!data) return 1;
printf("Введите %zu чисел: ", n);
read_array(data, n);
printf("Среднее: %.2f\n", average(data, n));
free(data);
return 0;
}
Язык C
- Массивы в C#: объявление, инициализация и операции
- Зубчатые массивы в C#: объявление, инициализация и доступ
- Как передавать массивы в функции в C++: практическое руководство
- Как передавать одномерные и двумерные массивы в функции в C — практическое руководство
- Понимание взаимосвязи между массивами и указателями в C
- Техники копирования массивов в Java: подробное руководство
- Массивы в C++: объявление, инициализация и работа с указателями
- Динамическое выделение массивов в C++: практическое руководство с примерами кода
- Учебник по массивам в Java: объявление, создание и инициализация с примерами кода
- MATLAB: Мастерство работы с многомерными и специальными массивами