ошибка сегментации перераспределения 2d массива Ошибка

#arrays #c #multidimensional-array #realloc

Вопрос:

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

void Append(int **arr, int *count, int *len, int x, int y){
    if (*count >= *len){
        *len *= 2;
        arr = realloc(arr, *len * sizeof(int*));
        for (int i=*count; i< *len;   i){
            arr[i] = (int *) malloc(sizeof(int) * 2);
        }
    }
    arr[*count][0] = x;
    arr[*count][1] = y;
      *count;
}


int main(){
    int **arr, count, len;

    arr = (int **) malloc(sizeof(int *) * 1);
    arr[0] = (int *) malloc(sizeof(int) * 2);

    count = 0;
    len = 1;

    for (int i=0; i<100;   i){
        Append(arr, amp;count, amp;len, i, i);
        printf("%d - %d %dn", i, arr[i][0], arr[i][1]);
    }

    return 0;
}

 

из

 0 - 0 0
1 - 1 1
Segmentation Fault
 

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

Когда я изменяю код, как показано ниже, я не получаю никаких ошибок, и код работает правильно. в чем разница между этими двумя?

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

typedef struct{
    int **arr;
    int len;
    int count;
}ARRAY;

void Append(ARRAY *array, int x, int y){
    if (array->count >= array->len){
        array->len *= 2;
        array->arr = realloc(array->arr, array->len * sizeof(int *));
        for (int i=array->count; i< array->len;   i){
            array->arr[i] = (int *) malloc(sizeof(int) * 2);
        }
    }
    array->arr[array->count][0] = x;
    array->arr[array->count][1] = y;
      array->count;
}

int main(){
    ARRAY coordinate;

    coordinate.arr = (int **) malloc(sizeof(int *) * 1);
    coordinate.arr[0] = (int *) malloc(sizeof(int) * 2);

    coordinate.len = 1;
    coordinate.count = 0;

    for (int i=0; i<100;   i){
        Append(amp;coordinate, i, i);
        printf("%d - %d %dn", i, coordinate.arr[i][0], coordinate.arr[i][1]);
    }



    return 0;
}
 

…………………………………………………………………………….

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

1. Когда вы arr = realloc(arr,...) находитесь в функции, она не изменяет arr указатель main . Таким образом, указатель всегда указывает на malloc возвращенную исходную память.

2. в исходном коде параметр arr представляет собой двойной указатель, но ваши ссылки на то, куда указывает этот параметр, не позволяют разыменовать эти обращения, в результате чего код ссылается на параметр в стеке вызовов, а не на данные в main()

3. OT: вызов malloc() возвращает void* значение, которое может быть присвоено любому указателю, поэтому не следует приводить возвращаемое значение, так как оно просто загромождает код и подвержено ошибкам.

4. ОТ: вызов realloc() может завершиться неудачно. Поэтому всегда следует присваивать возвращаемое значение временному указателю, проверять временный указатель на ненулевое значение, а если не равно НУЛЮ, то присваивать временный указатель целевому указателю. В противном случае при сбое вызова целевому указателю будет присвоено значение NULL, что приведет к потере выделенной памяти, что приведет к утечке памяти

5. ОТ: при возврате из main() , для надежного кода, все эти выделения памяти должны быть переданы free()

Ответ №1:

realloc() функция довольно сложна в деталях. Время от времени это работает по-разному. Например, предположим , вы выделили массив, содержащий два int s, с помощью malloc() и вызываете realloc() , чтобы увеличить его размер.

 int *arr;

arr = malloc(sizeof(int) * 2);
arr = realloc(arr, sizeof(int) * 5);
 

Иногда, realloc() будет только расти в размерах. Но иногда это будет работать так, как вы написали вот так:

 int *arr;

arr = malloc(sizeof(int) * 2);

int *new;

new = malloc(sizeof(int) * 5);
memcpy(new, arr, 2); //copy first 2 elements of arr to new
free(arr);
arr = new;
 

Это означает, что realloc() иногда значение будет меняться arr .

 int *arr1;
int *arr2;

arr1 = malloc(sizeof(int) * 2);
arr2 = realloc(arr1, sizeof(int) * 5);

if (arr1 == arr2)
    printf("not changedn");
else
    printf("changedn");
 

Таким образом, приведенный выше код иногда будет печатать «не изменен», а иногда «изменен» (я не уверен, что один и тот же код будет воспроизводить разные результаты. Что я могу с уверенностью сказать, так это то, что результат будет отличаться в зависимости от нового размера, который вы отправили realloc() ).

Таким образом, причина, по которой ваш первый код генерирует два элемента, заключается в том, что realloc() значение не меняется arr при первых нескольких вызовах, а после этого изменилось значение. Но, как отметил пользователь 3629249 в комментарии , realloc() изменилась локальная переменная arr , а не arr от main() . Затем ваша следующая попытка доступа к пространству памяти, на которое arr указывает , например arr[i][0] , должна привести к ошибке сегментации, поскольку пространство памяти, на которое arr указывает, уже освобождено.

Извините за любые грамматические ошибки. Я не являюсь носителем английского языка.

Ответ №2:

Проблема связана не realloc с чем-то, а с чем-то очень важным для программирования на языке Си.

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

Простой пример:

 void foo(int x)
{
    printf("%dn", x);  // Will print 42
    x = 5;
    printf("%dn", x);  // Will print 5
}

void bar()
{
    int x = 42;
    printf("%dn", x);  // Will print 42
    foo(x);
    printf("%dn", x);  // Will still print 42 because foo can't change this x
}
 

x in bar и x in foo -это две разные переменные, поэтому изменение x if foo не меняет x in bar .

Именно в этом и заключается проблема с вашим первым примером кода. Переменная arr in main не изменится в результате присвоения arr = realloc(... .

Чтобы изменить значение arr in main внутри функции, вы должны передать указатель на arr , т. е. Append(amp;arr, ...

Теперь вы программист с тремя звездами.

Снова используя простой пример:

 void foo(int* x)
{
    printf("%dn", *x);  // Will print 42
    *x = 5;
    printf("%dn", *x);  // Will print 5
}

void bar()
{
    int x = 42;
    printf("%dn", x);  // Will print 42
    foo(amp;x);            // Pass the address of x
    printf("%dn", x);  // Will print 5 because foo changed this x
}
 

Теперь foo можно изменить значение x in bar , потому foo что передается адрес x .

И это именно то, что вы делаете во втором примере кода, т. Е. вы вносите изменения только в значения, на которые указывает, array что находится coordinate внутри main .