Освободить указатель void **

#c #pointers #void-pointers

#c #указатели #указатели void

Вопрос:

Я пишу код для освобождения общего двойного указателя в C ( void **ptr ). Как мне проверить, был ли указатель выделен через malloc / calloc ? Как мне освободить внутренние указатели? (Я научился делать это, используя for цикл и получая количество элементов в указателе, но здесь мне это не дано.). Объявление функции является

 void freeArray(void **ptr);
  

Как бы вы освободили этот указатель и проверили ошибки, чтобы убедиться, что вы не получите ошибку seg, утечку памяти или другую ошибку памяти?

Комментарии:

1. (a) Вы не проверяете, было ли что-то выделено malloc . Вы должны спроектировать свою программу так, чтобы она знала, какие объекты выделены, а какие нет. Стандарт C не предоставляет никаких средств для этого. (б) Вы не освобождаете указатели. Вы выделяете и освобождаете память. Если ptr of void **ptr указывает на указатели, которые указывают на выделенную память, вы освобождаете эту память, передавая каждый из этих указателей на free . Ваша программа должна быть спроектирована так, чтобы знать, сколько таких указателей существует.

Ответ №1:

Я пишу код для освобождения общего двойного указателя в C (void ** ptr).

Я считаю, что не существует такой вещи, как общий двойной указатель. Указатель — это просто указатель.

Как мне проверить, был ли указатель выделен через malloc / calloc?

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

Как мне освободить внутренние указатели? (Я научился делать это, используя цикл for и получая количество элементов в указателе, но здесь мне это не дано.). Объявление функции является

 void freeArray(void **ptr);
  

«Внутренний указатель», я полагаю, не является подходящим описанием для этого использования.

В любом случае, сравните прототип вашей функции с обычным прототипом main()

 int main(int argc, char** argv)
  

и вы увидите, что здесь чего-то не хватает.

Может быть, это будет более понятно, если вы напишете

 void**        ptr = NULL;
  

Вы объявляете ptr . В качестве указателя. Но ptr уже выделен. Он статичен. Может быть 4, может быть 8 байт. И он указывает на указатель на void . Только это.
ptr есть void** , *ptr есть void* , **ptr есть void . При использовании ptr в качестве блока указателей вы должны сделать то же самое, что система делает с main() : создайте блок и сохраните свой собственный argc , количество значений. Это ваша проблема, чтобы сделать это, или лучше, это ваша проблема, чтобы НЕ делать этого.
Попробуйте это:

 void freeArray(int ptrc, void** ptr);
  

И держите эту пару всегда вместе и обновленной, поэтому часть «указания количества элементов в указателе» не представляет сложности.

Ответ №2:

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

freeArray присваивается указатель на массив void указателей. Поскольку нет информации о количестве указателей на free, необходимо сделать предположение:

  • либо количество указателей фиксировано. Допустим, ваша программа имеет дело с массивами из 3 указателей повсюду, freeArray можно предположить, что она освобождает 3 указателя…

  • или массив может завершаться нулем, например, argv массив, переданный main . Обратите внимание, однако, что этот конкретный массив строк не предназначен для освобождения таким образом.

Посмотрите на код, который распределяет массивы, и определите, какое соглашение используется.

Вот наивная реализация freeArray() для выделенного массива указателей на выделенные блоки с нулевым завершением:

 void freeArray(void **ptr) {
    if (ptr) {
        for (size_t i = 0; ptr[i]; i  ) {
            free(ptr[i]);
        }
        free(ptr);
    }
}
  

Ответ №3:

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