Очень часто возникают задачи обработки массивов данных, размерность которых заранее неизвестна. В этом случае возможно использование одного из двух подходов:

  • выделение памяти под статический массив, содержащий максимально возможное число элементов, однако в этом случае память расходуется не рационально;
  • динамическое выделение памяти для хранение массива данных.

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

Начальный адрес статического массива определяется компилятором в момент его объявления и не может быть изменен.

Для динамического массива начальный адрес присваивается объявленному указателю на массив в процессе выполнения программы.

Стандартные функции динамического выделения памяти

Функции динамического выделения памяти находят в оперативной памяти непрерывный участок требуемой длины и возвращают начальный адрес этого участка.

Функции динамического распределения памяти:

Для использования функций динамического распределения памяти необходимо подключение библиотеки :

Поскольку обе представленные функции в качестве возвращаемого значения имеют указатель на пустой тип void , требуется явное приведение типа возвращаемого значения.

Для определения размера массива в байтах, используемого в качестве аргумента функции malloc() требуется количество элементов умножить на размер одного элемента. Поскольку элементами массива могут быть как данные простых типов, так и составных типов (например, структуры), для точного определения размера элемента в общем случае рекомендуется использование функции

Память, динамически выделенная с использованием функций calloc(), malloc() , может быть освобождена с использованием функции

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

Динамическое выделение памяти для одномерных массивов

Форма обращения к элементам массива с помощью указателей имеет следующий вид:

Пример на Си : Организация динамического одномерного массива и ввод его элементов.

Результат выполнения программы:

Динамическое выделение памяти для двумерных массивов

Пусть требуется разместить в динамической памяти матрицу, содержащую n строк и m столбцов. Двумерная матрица будет располагаться в оперативной памяти в форме ленты, состоящей из элементов строк. При этом индекс любого элемента двумерной матрицы можно получить по формуле

index = i*m+j;

где i — номер текущей строки; j — номер текущего столбца.

Рассмотрим матрицу 3×4 (см. рис.)

Индекс выделенного элемента определится как

index = 1*4+2=6

Объем памяти, требуемый для размещения двумерного массива, определится как

n·m·(размер элемента)

Однако поскольку при таком объявлении компилятору явно не указывается количество элементов в строке и столбце двумерного массива, традиционное обращение к элементу путем указания индекса строки и индекса столбца является некорректным:

Правильное обращение к элементу с использованием указателя будет выглядеть как

  • p — указатель на массив,
  • m — количество столбцов,
  • i — индекс строки,
  • j — индекс столбца.

Пример на Си Ввод и вывод значений динамического двумерного массива

Результат выполнения

Возможен также другой способ динамического выделения памяти под двумерный массив — с использованием массива указателей. Для этого необходимо:

  • выделить блок оперативной памяти под массив указателей;
  • выделить блоки оперативной памяти под одномерные массивы, представляющие собой строки искомой матрицы;
  • записать адреса строк в массив указателей.

Графически такой способ выделения памяти можно представить следующим образом.

При таком способе выделения памяти компилятору явно указано количество строк и количество столбцов в массиве.
Пример на Си

Результат выполнения программы аналогичен предыдущему случаю.

С помощью динамического выделения памяти под указатели строк можно размещать свободные массивы. Свободным называется двухмерный массив (матрица), размер строк которого может быть различным. Преимущество использования свободного массива заключается в том, что не требуется отводить память компьютера с запасом для размещения строки максимально возможной длины. Фактически свободный массив представляет собой одномерный массив указателей на одномерные массивы данных.

Для размещения в оперативной памяти матрицы со строками разной длины необходимо ввести дополнительный массив m , в котором будут храниться размеры строк.

Пример на Си : Свободный массив

Результат выполнения

Перераспределение памяти

Если размер выделяемой памяти нельзя задать заранее, например при вводе последовательности значений до определенной команды, то для увеличения размера массива при вводе следующего значения необходимо выполнить следующие действия:

  • Выделить блок памяти размерности n+1 (на 1 больше текущего размера массива)
  • Скопировать все значения, хранящиеся в массиве во вновь выделенную область памяти
  • Освободить память, выделенную ранее для хранения массива
  • Переместить указатель начала массива на начало вновь выделенной области памяти
  • Дополнить массив последним введенным значением

Все перечисленные выше действия (кроме последнего) выполняет функция

  • ptr — указатель на блок ранее выделенной памяти функциями malloc() , calloc() или realloc() для перемещения в новое место. Если этот параметр равен NULL , то выделяется новый блок, и функция возвращает на него указатель.
  • size — новый размер, в байтах, выделяемого блока памяти. Если size = 0 , ранее выделенная память освобождается и функция возвращает нулевой указатель, ptr устанавливается в NULL .

Размер блока памяти, на который ссылается параметр ptr изменяется на size байтов. Блок памяти может уменьшаться или увеличиваться в размере. Содержимое блока памяти сохраняется даже если новый блок имеет меньший размер, чем старый. Но отбрасываются те данные, которые выходят за рамки нового блока. Если новый блок памяти больше старого, то содержимое вновь выделенной памяти будет неопределенным.

Пример на Си Выделить память для ввода массива целых чисел. После ввода каждого значения задавать вопрос о вводе следующего значения.

Результат выполнения

Я пытаюсь объявить массив char динамически, мне нужно добавить новый символ в строку, которая отлично работает, проблема в том, что когда я пытаюсь ее распечатать, в начале строки есть некоторые неизвестные символы.

3 ответа

Чтобы получить некоторую память в вашей строке, вы должны указать malloc сколько байтов памяти вы хотите. sizeof(char) возвращает 1 , поэтому у вас будет только 1 байт. В C строки заканчиваются байт NULL ( ), а printf и другие будут печататься до тех пор, пока они не найдут этот NULL терминатор.

Если вы сделаете что-то вроде этого:

Вероятно, вы получите очень странный вывод, так как у вас нет терминатора NULL .

Когда вы используете unsigned x; str = malloc(x); unsigned x; str = malloc(x); , это фактически не определено, сколько байтов у вас есть, поскольку эта переменная x не инициализирована.

Поскольку ваш вопрос очень неясен, то, что я могу вам сказать (из того, что, как я думаю, вы спрашиваете), состоит в том, как на самом деле получить место для строки из 63 символов плюс завершающий байт NULL .

Это сделает это.

Также обратите внимание, что блок памяти, возвращаемый malloc , не будет обнулен, поэтому вы не можете знать, что в нем (это может быть причиной, по которой вы получаете мусор при печати).

Я рекомендую вам прочитать о распределении памяти в хорошей книге C или в Википедии.

После редактирования и "MCVE"

Я внес некоторые изменения в то, что, как я думаю, вы хотите. Изменения поясняются в комментариях источника. Дайте мне знать, если у вас есть какие-то сомнения.

0 dbush [2018-06-13 16:48:00]

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

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

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

Обратите внимание, что первое выделение, это

точно эквивалентно 1

поэтому у вас нет места, кроме одного символа, который является проблемой, поскольку он представляет только пустую строку.

Если вы выделите это пространство, вы, скорее всего, получите доступ к памяти из выделенного пространства, что приведет к неопределенному и непредсказуемому поведению. Вам нужно понять, что такое строка в c,

  • Строка в c представляет собой последовательность ненулевых символов, за которой следует нулевой символ, поэтому для строки с N символами вам нужны элементы массива N + 1 (для ascii это равно байтам)

Согласно этому определению строки, если вы хотите сохранить строку "Hello" вам понадобится хотя бы следующий код

как вы можете видеть, последний символ ‘’ или 0 — то же самое — очень важен.

Все функции в стандартной библиотеке c, которые ожидают строковый параметр, ожидают, что существует нулевой ограничитель. Например, strlen() будет считать символы до тех пор, пока не достигнет ‘’ , если он не существует, вы не можете предсказать, где он остановит подсчет, что приведет к неопределенному поведению.

1 sizeof(char) соответствует стандарту c, всегда равному единице.

Пишу словарик и совсем запуталась в коде. Тестирую функцию, которая читает текстовый файл с матрицей размера counter*3 и читает его в соответствующий массив.

Пожалуйста, не могли бы вы объяснить, почему сам массив не хочет задаваться? Компилятор (MSVS) пишет, что для char * нельзя (char(counter)[3]) .

1 ответ 1

Давайте разбираться. Вы объявили словарь как

это лишено смысла, подобные указатели ссылаются на одну строку, а насколько я понял вам нужен двумерный массив целых чисел, тем более что у вас в fscanf считывание именно целых десятичных чисел ( %d ) тогда вам надо сделать объявление таким

далее, operator new может выделять память под один объект либо под одномерный массив, а для двумерного память нужно выделять в 2 этапа

и ещё одно уточнение — использование new это всё-таки С++ , а не С . Для чистого С память выделяется функцией malloc

И последнее — рассмотрим ваше считывание

у вас опечатка для ограничения j , и подобная запись для доступа к требуемому элементу массива неудобна для восприятия, плюс в параметры scanf необходимо передавать адрес требуемой переменной, это нужно заменить на