Боюсь, вы не понимаете, насколько вы далеки от правильного понимания. Сиди крепко, это будет долго. Добро пожаловать в C.
char** monthGroup
Все это на самом деле означает "указатель на указатель на char
". Однако у C есть много причин, по которым вы хотели бы указать на что-то. В вашем случае «внутреннее» указание таково, что вы действительно можете указать на последовательность из char
с в памяти (которую вы в разговорной речи воспринимаете как «строку», что C правильно делает не имеет ), а «внешнее» указание таково, что вы можете указывать на последовательность этих char*
s и рассматривать эту последовательность как «массив» (даже если не ; вы собираетесь его динамически распределять).
Вот проблема: когда вы передаете это char**
, которое пришло от main
, оно фактически ни на что не указывает. "Хорошо", говорите вы; «Функция будет указывать на некоторую память, которую я выделю с помощью malloc()
».
Нет.
C передает все по значению. char**
, получаемый months
, является копией блока char**
в main
локальных переменных. Вы перезаписываете указатель (с результатом вызова malloc
), записываете некоторые указатели в указанную память (больше malloc
результатов), копируете некоторые данные в эти порции указанной памяти ... и затем, в конце функции, параметр monthGroup
(который является локальной переменной в months
) больше не существует, и вы потеряли все эти данные, и переменная monthGroup
в main по-прежнему неизменна и не указывает ни на что. Когда ты пытаешься использовать его так, как будто он на что-то указывает, бум, ты мертв.
Так как же нам обойти это? С другим уровнем наведения, конечно, C правильно не имеет «передачи по ссылке», поэтому мы должны подделать его. Мы принимаем char***
и передаем &monthGroup
. Это все еще скопированное значение, но оно указывает непосредственно в хранилище локальной переменной для этого вызова main
(в стеке). Это позволяет нам написать значение, которое будет видно в main
. Мы присваиваем первый malloc
результат *monthGroup
и записываем указатели в это хранилище (*monthGroup[count]
) и т. Д.
За исключением того, что мы на самом деле не хотим этого делать, потому что это невероятно уродливо и сбивает с толку, и трудно понять, как правильно. Вместо этого давайте сделаем то, что должно быть невероятно очевидной вещью, которую вы должны делать, и эта базовая инструкция подчеркивает недостаточно: используйте возвращаемое значение функции, чтобы вернуть результат вычисления - поэтому она называется возвращаемое значение .
То есть мы устанавливаем char**
в months
( не принимает для него какой-либо параметр), возвращаем его и используем для инициализации значения в main
.
Мы закончили? нет Нет .
У вас все еще есть логические ошибки:
- Вы перераспределяете «внешний» слой внутри цикла while. Это явно не то, что вы хотите; Вы выделяете несколько «строк», но только один «массив», так что распределение выходит за пределы цикла. В противном случае вы каждый раз выбрасываете (без должного освобождения их!) Старые массивы.
На самом деле, вы do хотите сделать что-то подобное, но только потому, что заранее не знаете, сколько элементов вам нужно. Проблема в том, что новое распределение - это просто новое распределение, не содержащее ранее установленных указателей.
К счастью, у C есть решение для этого: realloc
. Это выделит новую память, скопирует старое содержимое (указатели на ваши выделенные «строки») и освободит старый блок. Ура! Более того, realloc
будет вести себя как malloc
, если мы дадим ему указатель NULL для «старой памяти». Это позволяет нам избежать использования специальной оболочки в нашем цикле.
- Вы неправильно используете значение
count
.В первый раз в цикле вы увеличите count
до 1, выделите некоторое пространство для monthGroup[1]
, чтобы указать, а затем попытаетесь записать в пространство, на которое указывает monthGroup[0]
, который никогда не был установлен.Вы хотите записать в то же пространство для "строки", которую вы только что выделили.(Кстати, sizeof(char)
бесполезен: это всегда 1. Даже если ваша система использует более 8 бит для представления символа ! char
является основной единицей храненияв вашей системе.)
За исключением случаев, когда есть более простой способ: используйте strdup
, чтобы получить указатель на выделенную копию вашего буфера.
char** months(FILE* monthfp) {
char buffer[50];
int count = 0;
char** monthGroup = NULL;
while (fgets(buffer, sizeof(buffer), monthfp) != NULL) {
// (re-)allocate the storage:
monthGroup = realloc(monthGroup, count * sizeof(char*));
// ask for a duplicate of the buffer contents, and put a pointer to the
// duplicate sequence into the last element of the storage:
monthGroup[count - 1] = strdup(buffer);
}
return monthGroup;
}
Настройка main
для соответствия оставлено (надеюсь, тривиальным) упражнение.Пожалуйста, прочтите также документацию для realloc
и strdup
.
Мы закончили? Нет.
Вы все равно должны проверять NULL
возвраты из realloc
и strdup
(так как они оба пытаются выделить память и, таким образом, могутв C) таким образом происходит сбой, и вам все еще нужен код для free
выделенной памяти.
И, как отмечали другие, не следует предполагать, что будет 12 месяцев.Если бы вы могли это предположить, вы бы не выделяли динамически monthGroup
во-первых;вы бы просто использовали массив .Таким образом, вам нужно как-то сообщить размер результирующего «массива» (добавление явного NULL-указателя в конец - это один из способов; другой - сделать ужасно некрасивую вещь, передать char***
и использовать возвращаемое значение для подсчетаразмер).