Динамическое распределение памяти для двумерного массива символов - PullRequest
0 голосов
/ 18 января 2019

Я использую malloc для динамического выделения массива двумерных символов. Если я не установил для каждого индекса массива значение NULL до free(), я получаю ошибку сегментации при попытке free(). Почему я получаю ошибку сегментации?

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    int nrows = 20; int ncolumns = 10;
    char ** arr = malloc(nrows * sizeof(char *));
    for(int i = 0; i < nrows; i++)
        arr[i] = malloc(ncolumns * sizeof(char));

    arr[0] = "string1";
    arr[1] = "string2";

    // does not work without the following code:
    // for(int i = 0; i < 20; i++)
    //     arr[i] = NULL;

    for(int i = 0; i < 20; i++)
        free(arr[i]);
    free(arr);

    return 0;
}

Ответы [ 5 ]

0 голосов
/ 18 января 2019

Сбой при вызове free является признаком неправильного управления памятью где-то еще в вашем коде. Если вы установите указатель на NULL, а затем на free, вы не будете падать, потому что free(NULL) гарантированно будет доброкачественным согласно стандарту C & sect; 7.22.3.3:

7.22.3.3 Свободная функция

... Если ptr является нулевым указателем, никаких действий не происходит . В противном случае, если аргумент не соответствует указателю , ранее возвращенному управлением памятью функция , или, если пространство было освобождено с помощью вызова free или realloc, поведение не определено.

Акцент мой.

Как уже отмечалось в других ответах, вы пытаетесь вызвать free в памяти, которую вы явно не выделяли с помощью malloc -семейных функций (поскольку вы перезаписали arr[i] указатели с указателями на строковые литералы)

0 голосов
/ 18 января 2019

Ваш код в порядке, проблема в том, что вы присваиваете строке литералы вашему массиву здесь: arr[0] = "string1";.

Таким образом, вы заменяете указатель на arr[0], который указывает на выделенную память, указателем на строковый литерал.

Указатели на литералы защищены, вы не можете освободить (илипишите им) их, потому что вы их не выделяли.

Чтобы решить эту проблему, используйте strcpy, чтобы скопировать значение вашего литерала в выделенную память:

strcpy(arr[0], "string1");
strcpy(arr[1], "string2");
0 голосов
/ 18 января 2019

Когда вы делаете это:

arr[0] = "string1";
arr[1] = "string2";

Вы перезаписываете содержимое arr[0] и arr[1], которые содержат адреса памяти, возвращенные из malloc, адресом двух строковых констант.Это вызывает утечку памяти, так как эта память больше не доступна.Это также причина сбоя при вызове free, потому что эти переменные больше не содержат адреса выделенной памяти.

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

strcpy(arr[0], "string1");
strcpy(arr[1], "string2");

Теперь вы можете free память правильно.

0 голосов
/ 18 января 2019

Две вещи, которые нужно знать:

  • У вас есть две области памяти (чтобы было легче понять) куча и стек
  • malloc, realloc, calloc выделяют ресурсы из кучи. Я скажу только malloc (но это то же самое)
    • бесплатно можно освободить только ресурсы из кучи. Стек является резервным для компилятора (он хранит вызов функции и другие данные)

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

char * a = malloc(255);

бесплатно

free(a);/* this is mandatory */
a = NULL;/* we can add this to the first line */

На самом деле, если вы привыкли присваивать значение NULL, и когда вы получите доступ к его значению, у вас будет ошибка отсчета NULL: так вы будете знать, где найти ошибку

Что вы пытаетесь сделать: выделите массив char ** arr = malloc(nrows * sizeof(char *)); и вы освободите его free(arr);

но вы выделяете 20 массивов char arr[i] = malloc(ncolumns * sizeof(char)); вы игнорируете его значение arr[0] = "string1"; (вы теряете значение, возвращаемое malloc, поэтому вы не можете освободить теперь arr [0]), мы не на C ++. Таким образом, «string1» хранится в стеке (поэтому malloc не может его освободить) и вы звоните бесплатно.

что вы можете сделать

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    int nrows = 20; int ncolumns = 10;
    char ** arr = malloc(nrows * sizeof(char *));
    for(int i = 0; i < nrows; i++)
        arr[i] = malloc(ncolumns * sizeof(char));
    free(arr[0]);//know we can loose it value because it is freed
    arr[0] = NULL;// in fact we assign a value just after so this line is useless but is educationnal purpose
    free(arr[1]);//know we can loose it value because it is freed
    arr[1] = NULL;// in fact we assign a value just after so this line is useless but is educationnal purpose
    arr[0] = "string1";
    arr[1] = "string2";

    // does not work without the following code:
    // for(int i = 0; i < 20; i++)
    //     arr[i] = NULL;

    for(int i = 2; i < 20; i++)//we start at 2 because the first two value are on the stack
    {
        free(arr[i]);
        arr[i] = NULL;//this is useless because we will free arr just after the loop)
    }
    free(arr);
    arr = NULL;// this is useless because we exit the end of program

    return 0;
}
0 голосов
/ 18 января 2019

= оператор не копирует строку, он только назначает указатель. Таким образом, ваша неправильная память больше не доступна для этих элементов массива, и попытка освободить ее - это неопределенное поведение, которое может привести к segfault.

Вам необходимо скопировать его, используя strcpy.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...