Почему malloc возвращает нулевой указатель при вызове внутри функции?

avatar
Gameatro
8 апреля 2018 в 00:23
1425
2
1

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

Вот мой код. У меня проблемы с malloc() в функции reverse(). malloc() в других функциях работают нормально. Итак, почему есть проблема с той, что в этой функции. У меня достаточно памяти на компьютере, так что проблема точно не в этом.

#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
    int data;
    struct node *next;
} SNode;


typedef struct
{
    int count;
    SNode *top;
} Stack;

int isSEmpty(Stack *s)
{
    return (s->count==0);
}

void push(Stack *s, int x)
{
    SNode *temp = (SNode *)malloc(sizeof(SNode));
    temp->data = x;
    temp->next = s->top;
    s->top = temp;
    s->count++;
}

int pop(Stack *s)
{
    if (isSEmpty(s))
    {
        printf("Underflow");
        return -1;
    }
    SNode *temp = s->top;
    s->top = s->top->next;
    int t = temp->data;
    free(temp);
    s->count--;
    return t;
}
typedef struct qnode
{
    int data;
    struct qnode *next, *prev;
} QNode;

typedef struct
{
    QNode *front, *rear;
    int count;
} Queue;

int isQEmpty(Queue *q)
{
    return (q->count==0);
}

void enQueue(Queue *q, int x)
{
    QNode *temp = (QNode *)malloc(sizeof(QNode));
    temp->data = x;
    temp->prev=q->rear;
    temp->next = NULL;
    q->rear->next = temp;
    q->rear = temp;
    q->count++;
    if (q->count==1)
    {
        q->front = q->rear;
    }
}

int deQueue(Queue *q)
{
    if (isQEmpty(q))
    {
        printf("Underflow");
        return -1;
    }
    QNode *temp = q->front;
    q->front = q->front->next;
    int t = temp->data;
    free(temp);
    q->count--;
    return t;
}
void reverse(Queue *q)
{
    Stack *s = (Stack *)malloc(sizeof(Stack));
    s->count = 0;

    while (!isQEmpty(q))
    {
        push(s, deQueue(q));
    }
    while (!isSEmpty(s))
    {
        enQueue(q, pop(s));
    }
}

int main()
{
    char p = 'y';
    Queue *q = (Queue *)malloc(sizeof(Queue));

    q->count = 0;
    while (p =='y')
    {
        printf("Enter data to be Enqueued: ");
        int d;
        scanf("%d", &d);
        enQueue(q, d);
        printf("Do you want to enter more data? y/n:");
        scanf(" %c", &p);
    }
    printf("Original queue Front: %d Rear: %d\n", q->front->data, q->rear->data);
    reverse(q);
    printf("Reversed queue Front: %d Rear: %d", q->front->data, q->rear->data);
    return 0;
}
Источник
HolyBlackCat
8 апреля 2018 в 00:26
1

У вас есть несколько malloc здесь. Какой из них возвращает 0?

Pablo
8 апреля 2018 в 00:28
0

Откуда вы знаете, что malloc возвращает NULL, если вы его не проверяете? Обычно malloc возвращает NULL, если памяти больше нет.

Iharob Al Asimi
8 апреля 2018 в 00:31
0

Отформатируйте свой код правильно, особенно если вы хотите/нужно, чтобы другие его прочитали.

Gameatro
8 апреля 2018 в 00:37
0

@HolyBlackCat я указал в вопросе, тот, что в обратной функции

Gameatro
8 апреля 2018 в 00:38
0

@Pablo Я удалил оператор проверки при публикации кода, поскольку он не является частью основного кода.

HolyBlackCat
8 апреля 2018 в 00:40
0

Пардон, я этого не заметил. Тогда можете ли вы добавить чек обратно, чтобы мы могли быть уверены, что он не сломан? Кроме того, вы уверены, что не вызываете malloc абсурдное количество раз, прежде чем он вернет 0?

Gameatro
8 апреля 2018 в 00:40
0

@IharobAlAsimi Я отформатировал его. он не отформатировался после того, как я скопировал и вставил его.

HolyBlackCat
8 апреля 2018 в 00:42
0

Есть format.krzaq.cc (но я не уверен, как форматирование могло сломаться при копировании из IDE).

Gameatro
8 апреля 2018 в 00:43
0

@HolyBlackCat этот чек не имеет большого значения. только что использованный if(s==NULL) напечатать что-то после malloc. ну, я не думаю, что malloc называют абсурдным количеством раз, всего 2-3. и даже тогда у меня 8 гб оперативной памяти. он не будет заполнен путем выделения байтов

Iharob Al Asimi
8 апреля 2018 в 00:45
0

@Gameatro Это потому, что у вас были вкладки вместо пробелов для отступов. И некоторые другие вещи просто так, как вы форматируете это неправильно (ИМХО). Стиль — это то, что вы можете выбрать, но, конечно, есть и плохие варианты.

ad absurdum
8 апреля 2018 в 00:58
0

@Gameatro «эта проверка не имеет большого значения» - если ваша проверка на ноль показывает вам, что ваш код дает сбой, вам нужно включить ее в минимальный воспроизводимый пример. Достаточно часто здесь случается, что кто-то уверен, что он правильно проверил нулевое значение, но случайная запятая или отсутствующий знак равенства или что-то еще приводит к тому, что проверка идет наперекосяк. Не говоря, что это была ваша проблема, просто говоря, что это важная часть вашей программы, если так было, когда она потерпела неудачу.

Ответы (2)

avatar
Michael Burr
8 апреля 2018 в 00:44
3

Вы не инициализируете все поля структуры *q, которые изначально размещаете в `main():

Queue *q = (Queue *)malloc(sizeof(Queue));

q->count = 0;

Затем вы передаете этот указатель q на enQueue() и делаете что-то вроде:

q->rear->next = temp;

Я думаю, вы также можете использовать q->front без его инициализации.

Эти вещи имеют неопределенное поведение и в вашем случае, вероятно, повреждают кучу, из-за чего malloc() не работает так, как вы ожидаете. Если вы работаете с Linux, вам может пригодиться valgrind.

Gameatro
8 апреля 2018 в 00:45
0

нет, проблема в обратной функции. я использовал некоторые отпечатки, и это точки останова, malloc возвращает нулевой указатель.

Michael Burr
8 апреля 2018 в 00:47
1

И что? Вы просто не замечаете проблему, пока не будет вызван reverse(). Проблема в enQueue() (и/или другие подобные проблемы) заключается в настройке проблемы в reverse().

Iharob Al Asimi
8 апреля 2018 в 00:49
1

@Gameatro Послушайте более опытных программистов, я на 100% уверен, что проблема именно в q->rear->next = temp. Я знаю это, потому что я использовал инструмент под названием valgrind в вашей программе и обнаружил это.

Gameatro
8 апреля 2018 в 01:04
0

но почему это вызывает проблемы в обратной функции?

Iharob Al Asimi
8 апреля 2018 в 01:23
0

@Gameatro Это называется Неопределенное поведение. Поищи в Гугле.

Gameatro
8 апреля 2018 в 03:24
0

к спасибо за помощь

avatar
Iharob Al Asimi
8 апреля 2018 в 00:44
5

Вашей программе почти не хватает памяти, поэтому malloc() вернет NULL. Вместо этого сочетание плохого стиля программирования и беспорядочного кода вызывает проблемы, связанные с доступом к неинициализированной памяти, что является неопределенным поведением, после запуска UB вы больше не можете предсказать поведение программы.

Первое, что вам нужно исправить, это избегать такой конструкции

q->rear->next = temp;

потому что q->rear может быть NULL и, таким образом, вы вызовете UB, если разыменуете его.

Затем вам нужно явно инициализировать члены структуры, malloc() только выделяет память для использования, она вообще не инициализируется, хороший способ сделать это - создать функцию который выделяет и инициализирует пустые экземпляры, например следующие

Queue *queue_new(int count) 
{
    Queue *queue;
    queue = malloc(sizeof(*queue));
    if (queue == NULL)
        return NULL;
    queue->count = count;
    queue->front = NULL;
    queue->rear = NULL;
    return queue;
}

Кроме того, не смешивайте объявления с кодом. Мне пришлось искать определение Queue, чтобы написать вышеуказанную функцию, и я сделал это с помощью функции поиска/замены моего редактора кода.

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

Pablo
8 апреля 2018 в 00:51
0

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

Iharob Al Asimi
8 апреля 2018 в 00:53
0

@Pablo Если вы хотите установить для каждого члена значение 0, да, но в моем примере вы можете создать что-то вроде queue_new(4);, и в этом случае использование calloc() позволит избежать необходимости явно устанавливать front и rear для NULL. Обычно я избегаю calloc(), если только я ДЕЙСТВИТЕЛЬНО не хочу инициализировать 0.