Массивы в языке Си
Массивы служат для хранения множества данных одного типа.
Массив характеризуется типом хранимых данных и размером массива — количество хранимых данных.
Массивы делятся на статические — создаваемые при запуске программы и динамические — создаваемые в процессе выполнения программы.
Массивы бывают одномерные и многомерные. Легко визуализировать массивы с размерностью до трех. Одномерные массивы это хранение элементов в линию, двумерные — прямоугольником, трехмерные — параллелепипедом. Четырехмерный массив — это одномерный массив трехмерных массивов и т.д.
Статические массивы
Статические массивы создаются при запуске программы в статической памяти и существуют всё время выполнения программы.
Статические массивы создаются в области памяти называемой стеком. Размер стека ограничен сильнее чем размер кучи (heap) (динамическая память), поэтому нельзя создать большой статический массив (например в 100 миллионов записей). И в общем случае размер динамического массива может быть больше чем статического.
Размер статического массива должен быть известен на момент запуска программы, поэтому он должен либо быть явно прописан в коде цифрами, либо вставляться через макроподстановки (некоторые компиляторы позволяют использовать переменную для задания размера статического массива). В любом случае вы НЕ МОЖЕТЕ задать размер статического массива во время выполнения программы.
Инициализация статического массива
int a[5]; // Статический массив a состоящий из 5 элементов int. int a[5] = <>; // Статический массив инициализированный нулями static int a[5]; // Статический массив инициализированный нулями int a[3] = ; //Создание и явная инициализация статического массива со значениями 1, 3, 8. int a[] = ; //Создание и явная инициализация статического массива со значениями 1, 3, 8. int a[2][3];//Двумерный массив из двух строк (row) и трех столбцов (column) int a[2][3]=< //Двумерный массив с явной инициализацией , , >; int a[][3]=< //Двумерный массив с явной инициализацией . Первую размерность можно не указывать , , >;
Ниже приведен пример программы с прямой и косвенной адресацией элементов массива.
#include // Требуется для prinf() #define AR_SIZE 10 // Размер массива int main() < int a[10]; // Статический массив размером 10 #define AR_SIZE 10 // Размер массива можно задать через макроопределение int b[AR_SIZE]; // Статический массив размером AR_SIZE for(int n = 0; n < AR_SIZE; n++)< // Перебираем массив printf("a[%d] = %d\n", n, a[n]); // Выводим значения printf("a + %d = %d\n", n, *(a + n)); // Аналогично предыдущей строке (косвенная адресация) >return 0; >
Динамические массивы
Динамические массивы создаются во время работы программы в динамической памяти функцией malloc() и выделенная под них память освобождается либо вручную, вызовом функции free(), либо во время завершения программы.
Динамические массивы создаются в динамической памяти называемой кучей. Размер кучи ограничен операционной системой и для 32-х битный систем не может превышать 4 гигабайта.
Размер динамического массива может задаваться во время выполнения программы, в том числе размер может задаваться переменными, которые инициализируются во время работы программы — например пользовательский ввод, это одно из важнейших отличий от статических массивов.
Необходимость ручного выделения памяти под динамические массивы функцией malloc() и освобождения памяти функцией free() усложняет программу и может вызывать утечки памяти. Поэтому, если есть возможность — всегда используйте статические массивы.
Утечкой памяти называют ситуацию когда динамическая память, используемая программой, постоянно растет. Такая ситуация возникает когда выделение памяти (функцией malloc()) происходит чаще чем освобождение (функцией free()). В результате чего динамическая память кончается и программа завершается с ошибкой и теряет работоспособность. Выделение памяти может происходить в цикле и программисту приходится вручную следить чтобы она своевременно освобождалась. Существует множество языков где процессом освобождения памяти занимается так называемый сборщик мусора (garbage collector), но на данный момент данные языки сильно проигрывают Си по производительности. Для маленьких и короткоживущих программ проблема утечки памяти может быть не актуальна, но для программ и сервисов которые работают постоянно это бывает серьезной проблемой, решением которой бывают кривые подходы вроде периодических ручных или автоматических перезапусков программы — не делайте так.
Ниже приведен пример программы с созданием, обработкой и уничтожение одномерного динамического массива.
#include //printf() #include // malloc(), free() int main() < const int AR_SIZE = 5; // Размер массива int* ar = malloc(5 * sizeof(int)); // Создание динамического массива на 5 элементов. Необходимо далее их инициализировать. printf("&ar = %p, ar = %p \n", &ar, ar); for(int i = 0; i < AR_SIZE; i++)< ar[i] = i * i; // Инициализация массива printf("&ar[%d] = %p, ar[%d] = %d \n", i, &ar[i], i, ar[i]); // Использование массива >free(ar); // Освобождение памяти >
Двумерный динамический массив
Работа с двумерным динамическим массивом заметно сложнее чем с одномерным, а на практике размерность массива может быть и больше.
#include //printf() #include // malloc(), free() int main() < const int ROW = 2, COL = 4; // Размер массива рядов, столбцов char **ar = malloc(ROW * sizeof (char *)); // Выделение памяти под указатели на строки (ряды) printf("&ar = %p, ar = %p \n", &ar, ar); // Адрес массива for (int i = 0; i < ROW; i++)< // Перебираем строки (ряды) ar[i] = malloc(COL * sizeof(char)); // Выделение памяти под хранение столбцов printf("&ar[%d] = %p, ar[%d] = %p \n", i, &ar[i], i, ar[i]); // Адреса массивов for(int n = 0; n < COL; n++)< // Перебираем элементы столбца(ов) ar[i][n] = i * COL + n; // Заполняем значения массива возростающими значениями >> for (int i = 0; i < ROW; i++)< // Перебираем строки (ряды) for(int n = 0; n < COL; n++)< // Перебираем элементы столбца(ов) printf("&ar[%d][%d] = %p, ar[%d][%d] = %d \n", i, n, &ar[i][n], i, n, ar[i][n]); // Выводим значения массива >> for (int i = 0; i < ROW; i++)< // Перебираем строки (ряды) free(ar[i]); // Освобождение памяти >free(ar); // Освобождение памяти >
Понравилась страница?
Добавить в закладки
Или поделиться!
Как из статического массива сделать динамический с
Кроме отдельных динамических объектов в языке C++ мы можем использовать динамические массивы. Для выделения памяти под динамический массив также используется оператор new , после которого в квадратных скобках указывается, сколько массив будет содержать объектов:
int *numbers ; // динамический массив из 4 чисел // или так // int *numbers = new int[4];
Причем в этом случае оператор new также возвращает указатель на объект типа int — первый элемент в созданном массиве.
В данном случае определяется массив из четырех элементов типа int, но каждый из них имеет неопределенное значение. Однако мы также можем инициализировать массив значениями:
int *numbers1 >; // массив состоит из чисел 0, 0, 0, 0 int *numbers2 >; // массив состоит из чисел 1, 2, 3, 4 int *numbers3 >; // массив состоит из чисел 1, 2, 0, 0 // аналогичные определения массивов // int *numbers1 = new int[4]<>; // массив состоит из чисел 0, 0, 0, 0 // int *numbers1 = new int[4](); // массив состоит из чисел 0, 0, 0, 0 // int *numbers2 = new int[4]< 1, 2, 3, 4 >; // массив состоит из чисел 1, 2, 3, 4 // int *numbers3 = new int[4]< 1, 2 >; // массив состоит из чисел 1, 2, 0, 0
При инициализации массива конкретными значениями следует учитывать, что если значений в фигурных скобках больше чем длина массива, то оператор new потерпит неудачу и не сможет создать массив. Если переданных значений, наоборот, меньше, то элементы, для которых не предоставлены значения, инициализируются значением по умолчанию.
Стоит отметить, что в стандарт С++20 добавлена возможность выведения размера массива, поэтому, если применяется стандарт С++20, то можно не указывать длину массива:
int *numbers >; // массив состоит из чисел 1, 2, 3, 4
После создания динамического массива мы сможем с ним работать по полученному указателю, получать и изменять его элементы:
int *numbers >; // получение элементов через синтаксис массивов std::coutПричем для доступа к элементам динамического массива можно использовать как синтаксис массивов ( numbers[0] ), так и операцию разыменования ( *numbers )
Соответственно для перебора такого массива можно использовать различные способы:
unsigned n< 5 >; // размер массива int* p < new int[n] < 1, 2, 3, 4, 5 >>; // используем индексы for (unsigned i<>; i < n; i++) < std::cout std::cout ; i < n; i++) < std::cout std::cout ; q != p + n; q++) < std::cout std::coutОбратите внимание, что для задания размера динамического массива мы можем применять обычную переменную, а не константу, как в случае со стандартными массивами.
Для удаления динамического массива и освобождения его памяти применяется специальная форма оператора delete :
delete [] указатель_на_динамический_массив;#include int main() < unsigned n< 5 >; // размер массива int* p < new int[n] < 1, 2, 3, 4, 5 >>; // используем индексы for (unsigned i<>; i < n; i++) < std::cout std::cout
Чтобы после освобождения памяти указатель не хранил старый адрес, также рекомендуется обнулить его:
delete [] p; p = nullptr; // обнуляем указательМногомерные массивы
Также мы можем создавать многомерные динамические массивы. Рассмотрим на примере двухмерных массивов. Что такое по сути двухмерный массив? Это набор массив массивов. Соответственно, чтобы создать динамический двухмерный массив, нам надо создать общий динамический массив указателей, а затем его элементы - вложенные динамические массивы. В общем случае это выглядит так:
#include int main() < unsigned rows = 3; // количество строк unsigned columns = 2; // количество столбцов int** numbers>; // выделяем память под двухмерный массив // выделяем память для вложенных массивов for (unsigned i<>; i < rows; i++) < numbers[i] = new int[columns]<>; > // удаление массивов for (unsigned i<>; i < rows; i++) < delete[] numbers[i]; >delete[] numbers; >Вначале выделяем память для массива указателей (условно таблицы):
int** numbers>;Затем в цикле выделяем память для каждого отдельного массива (условно строки таблицы):
numbers[i] = new int[columns]<>;Освобождение памяти идет в обратном порядке - сначала освобождаем память для каждого отдельного вложенного массива, а затем для всего массива указателей.
Пример с вводом и выводом данных двухмерного динамического массива:
#include int main() < unsigned rows = 3; // количество строк unsigned columns = 2; // количество столбцов int** numbers>; // выделяем память под двухмерный массив for (unsigned i<>; i < rows; i++) < numbers[i] = new int[columns]<>; > // вводим данные для таблицы rows x columns for (unsigned i<>; i < rows; i++) < std::cout ; j < columns; j++) < std::cout > numbers[i][j]; > > // вывод данных for (unsigned i<>; i < rows; i++) < // выводим данные столбцов i-й строки for (unsigned j<>; j < columns; j++) < std::cout std::cout for (unsigned i<>; i < rows; i++) < delete[] numbers[i]; >delete[] numbers; >Пример работы программы:
Enter data for 1 row 1 column: 2 2 column: 3 Enter data for 2 row 1 column: 4 2 column: 5 Enter data for 3 row 1 column: 6 2 column: 7 2 3 4 5 6 7Указатель на массив
От типа int** , который представляет указатель на указатель (pointer-to-pointer) следует отличать ситуацию "указатель на массив" (pointer to array). Например:
#include int main() < unsigned n; // количество строк int (*a)[2] = new int[n][2]; int k<>; // устанавливаем значения for (unsigned i<>; i < n; i++) < // устанавливаем данные для столбцов i-й строки for (unsigned j<>; j < 2; j++) < a[i][j] = ++k; >> // вывод данных for (unsigned i<>; i < n; i++) < // выводим данные столбцов i-й строки for (unsigned j<>; j < 2; j++) < std::cout std::cout // удаляем данные delete[] a; a = nullptr; >Здесь запись int (*a)[2] представляет указатель на массив из двух элементов типа int. Фактически мы можем работать с этим объектом как с двухмерным массивом (таблицей), только количество столбцов в данном случае фиксировано - 2. И память для такого массива выделяется один раз:
int (*a)[2] = new int[n][2];То есть в данном случае мы имеем дело с таблице из n строк и 2 столцов. Используя два индекса (для строки и столца), можно обращаться к определенному элементу, установить или получить его значение. Консольный вывод данной программы:
1 2 3 4 5 6Как заменить статический массив на динамический c++
Прошу помочь. Пытаюсь статический массив в динамический. В результате выводится массив адресов, а не элементов. Чем глубже капаю, тем больше путаюсь, поэтому прошу проверить корректность кода и подкорректировать в случае необходимости. Еще ввод и вывод данных должен быть осуществлен в текстовый файл. На каком этапе кода это нужно вводить?
#include using namespace std; void printArray(int *arr, int arrSize) < for (int i = 0; i < arrSize; ++i)< //Вывод значений входящего массива cout cout int *createArrOfOddNum(int *arrIn, int sizeIn) < int *outputArray = new int[sizeIn/2]; int value; for (int i = 0; i < sizeIn; ++i) //Перебор всех элементов < if (arrIn[i] % 2 != 0) < value = arrIn[i]; //Если элемент нечетный, то добавляем его в новый массив >else < value = arrIn[i]; //Если элемент четный int j = 1; bool flag = true; //Условие выполнения цикла while, пока число не станет нечетным while (flag) < if (i + j < sizeIn) < value += arrIn[i + j]; //Если элемент не последний, то прибавляем к нему следующий в массиве if (value % 2 != 0) < flag = false; //Если после прибавления элемент стал нечетным - прекращаем While >> if ((i - j >= 0) and (flag == true)) //Если следующий элемент прибавить не удалось, то прибавляем предыдущий элемент < value += outputArray[i - j]; //Прибавляем предыдущий элемент if (value % 2 != 0) < flag = false; //Если число стало нечетным - выход из цикла >> j += 1; //Переход на следующий элемент массива в цикле While > > //Конец работы с четным элементом outputArray[i] = value; //Запись новых значений > return outputArray; > int main()< int array_in[] = ; const int arrSize = sizeof(array_in) / sizeof(array_in[0]); int N = 8; // Задается размер массива int *inArray = new int[N]; // Выделение памяти для исходного массива int *outArray = NULL; //Присваивание NULL обозначает, что память не выделена int outArray_size; inArray[N] = array_in[arrSize]; outArray = createArrOfOddNum(inArray, N); coutОтслеживать
задан 15 июн 2021 в 13:42
17 3 3 бронзовых знака
"Чем глубже капаю, тем больше путаюсь" - это не удивительно, ведь вы не составили минимальный воспроизводимый пример, который тут займет аж 2 строки.
В чем преимущество статического массива перед динамическим?
Я совсем новичок в с++ и вот наткнулся на тему указателей и динамический памяти и немного не понимаю одной вещи: в чем преимущество статически объявленного массива перед динамически объявленым в данном примере?
#include int main() < int array_length = 0; std::cin >> array_length; int numbers[array_length]; return 0; >#include int main() < int array_length = 0; std::cin >> array_length; int* numbers = new int[array_length]; delete[] numbers; return 0; >
- Вопрос задан более года назад
- 405 просмотров
Комментировать
Решения вопроса 2

Насколько мне известно.
Статический массив:
- Размер должен быть известен на момент компиляции. Твой пример - это нестандартная фишка ряда компиляторов, по-хорошему для статического массива размер не должен быть динамическим (определяться во время выполнения).
+ Компилятор сам позаботится об удалении массива.
Динамический массив:
+ Размер может определяться во время выполнения.
- Нужно не забыть освободить память массива самостоятельно.
А вообще, если нет причин делать иначе, используй std::vector. Если тебе понадобится именно массив в стиле C, вектор позволяет его легко получить методом .data().
+ Размер не просто динамический, вектор может переаллоцировать свою память по мере надобности. Так что для ситуаций, когда размер заведомо неизвестен, вектор весьма удобен.
+ Вектор сам управляет своей памятью. Убил вектор - убил управляемый им массив.