проблемы с вводом данных и malloc в C

Я новичок в C, и у меня небольшая проблема с моим кодом:

int i, n;
int *arr;
while(n != 0) {
    scanf("%d", &n);
    if(n == 0)
        exit(1);
    else {
        arr = (int*) malloc(sizeof(int) * n);
        for(i = 0; i < n; i++)
            scanf("%d", &arr[i]);
    } //end if
} //end while

То, что я пытаюсь сделать, это создать массив размером n, и я хочу прекратить чтение, когда я получаю «0», например, если я ввожу:

3
2
2
5
2
6
7
0

Мне нужен массив размера 3 со значениями 2, 2, 5, массив 2 со значениями 6 и 7 и выход из-за 0 * Извините, я пропустил важную часть, я думаю... В моем коде вызов calc(), где я отправляю arr сразу после scanf("%d",&arr[i]), а затем я возвращаю значение, а затем, если следующие значения, например. 2 не 0, я прочитаю, создам новый массив, отправлю arr, выведу результат на консоль и снова, если следующее значение равно 0, он выйдет. * Не могли бы вы, ребята, сказать мне, где я ошибаюсь?


person Ignacio Pochart    schedule 15.09.2011    source источник
comment
Объясните, почему код, который вы представили, не удался. Разве он не выходит при вводе последовательности в примере?   -  person K-ballo    schedule 15.09.2011
comment
Используйте оператор break вместо exit. Это гарантирует выполнение любого другого кода после цикла while.   -  person Mahesh    schedule 15.09.2011


Ответы (4)


Проблемы, которые видны в вашем коде:
1. Проверка неинициализированного целого числа n в while. Чтобы исправить это, либо инициализируйте n ненулевым значением, либо используйте do{ ... } while() вместо while().
2. Вам необходимо проверить значение n, которое считывается через scanf. malloc принимает тип size_t в качестве параметра, который равен unsigned int. Но n, будучи целым числом, может принимать отрицательные значения, поэтому, если введено отрицательное значение, оно будет передано как unsigned int в malloc, это может привести к нежелательным результатам (также цикл for будет выполняться неправильное количество раз). Вы также можете изменить тип n с integer на unsigned int или изменить условие выхода на if( n < 1 ).
3. В вашей программе есть утечка памяти. Память, выделенная с помощью malloc, не освобождается с помощью free.
4. Не думайте, что malloc всегда будет успешным. Пожалуйста, проверьте успешность malloc с помощью проверки NULL, т.е.

if (NULL == arr)
{
   //error handling
}

5. exit с ненулевым значением обычно указывает на аварийное завершение. Вы можете использовать break или return. break может быть лучшей идеей, так как, как правило, становится трудно тестировать функцию по мере увеличения количества точек выхода в функции (хотя это может быть не так в вашем случае, но это к вашему сведению)
6. При желании вы можете проверить вернуть значение scanf, чтобы убедиться, что введено правильное значение.

Помогите это помогает!

person another.anon.coward    schedule 15.09.2011
comment
Спасибо, это очень помогло мне понять программирование! :) - person Ignacio Pochart; 15.09.2011

Вы почти там!

Вы создаете новые массивы в arr, но это единственный указатель, поэтому он может ссылаться только на один блок памяти. Когда вы вызываете malloc, новая память сохраняется в arr, но старая память теряется. У вас «утечка памяти», потому что на машине зарезервирована старая память, но у вас нет переменной, в которой хранится ее адрес, поэтому у вас нет возможности найти ее снова.

Если вам нужно сохранить только последний список, вы должны освободить старую память (в arr) перед выделением нового пространства. Если вам нужно хранить все массивы, вам понадобится массив указателей в arr.

edit:
Вам нужно вызвать free, чтобы «освободить» ранее выделенную память, прежде чем выделять новую память. В первом наборе данных у вас нет существующего «malloc», но всегда безопасно освободить указатель NULL, поэтому просто установите указатель на NULL в начале.

Подсказка: всегда полезно установить для всех переменных какое-то безопасное начальное значение при их определении.

 int *arr=NULL;  // Mark this as pointing to no memory

  ....  

 free(arr);  // first time it does nothing, afterwards it deletes the previous reserved memory
 arr = (int*) malloc(sizeof(int) * n); // as before this reserves some memory
person Martin Beckett    schedule 15.09.2011
comment
Я думаю, что это нормально, потому что, когда я получаю массив, я могу вызвать один метод, который я туда не добавил, а затем вывести значение на консоль. Если значение next не равно 0, я снова вызову метод. Поэтому я не думаю, что мне нужно запоминать ВСЕ массивы сразу... в этом случае... мне нужно освободить (обр)??? - person Ignacio Pochart; 15.09.2011

Вы не инициализируете n, поэтому вы можете или не можете войти в свой цикл while. Было бы разумно начать n с -1:

int i, n = -1;

И вы должны указать возвращаемое значение malloc, которое может скрыть проблемы.

У вас также происходит утечка памяти, потому что вы не вызываете free, когда возвращаетесь из malloc, и вы теряете след того, что читаете, каждый раз, когда присваиваете новое значение arr. Однако Брайан Роуч и Мартин Бекет упоминали об этом.

person mu is too short    schedule 15.09.2011

Предположительно, вы захотите получить доступ к этим массивам позже.

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

Вам нужно выделить часть int * (набор указателей int), а затем сохранить там каждый указатель int.

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

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

Вот некоторая помощь, если вы хотите пойти по последнему пути:

int i;
int n = 1; 
int **myArrayOfArrays = malloc(sizeof(int*) * 5); /* max of 5 arrays */

int *arr;
int arrayCount = 0;
while(n != 0) {
    scanf("%d", &n);
    if(n == 0)
        break;
    else {
        if (arrayCount == 4) {
            printf("Woah there partner! That's enough!\n");
            break;
        }
        else
        {
            arr = malloc(sizeof(int) * n);
            for(i = 0; i < n; i++)
                scanf("%d", &arr[i]);
            myArrayOfArrays[arrayCount] = arr;
            arrayCount++;
         }
    } //end if
} //end while

ОДНАКО... теперь вы не знаете, какова длина каждого массива. Что является проблемой. Вам нужно будет отслеживать это или использовать динамическую структуру, такую ​​как связанный список. В приведенном ниже примере мы добавляем длину в качестве первого элемента каждого массива:

int main()
{

    int i;
    int n = 1;
    int **myArrayOfArrays = malloc(sizeof(int*) * 5);

    int *arr;
    int arrayCount = 0;
    while(n != 0) {
        scanf("%d", &n);
        if(n == 0)
            break;
        else {
            if (arrayCount == 4) {
                printf("Woah there partner! That's enough!\n");
                break;
            }
            else
            {
                arr = malloc(sizeof(int) * (n + 1)); /* one more than we need */
                arr[0] = n; /* store the array length in the first element */
                for(i = 1; i <= n; i++)
                    scanf("%d", &arr[i]);
                myArrayOfArrays[arrayCount] = arr;
                arrayCount++;
             }

        } //end if
    } //end while
    int j;
    for (i = 0; i < arrayCount; i++)
    {
        int length = myArrayOfArrays[i][0]; /* retrieve the length */
        for (j = 1; j <= length; j++)
            printf("%d ", myArrayOfArrays[i][j]);
        printf("\n");
    }
}

Динамическое выделение с использованием массивов/необработанной памяти означает, что вам нужно следить за вещами. На самом деле лучший подход — использовать связанный список для ваших данных. В этом случае у вас может быть связанный список узлов, каждый из которых содержит связанный список целых чисел.

person Brian Roach    schedule 15.09.2011