присвоение ‘int’ из ‘void *’ создает целое число из указателя без приведения

#c #pointers #memory #struct

#c #указатели #память #структура

Вопрос:

Я пытаюсь создать стек связанных списков, и я следовал онлайн-руководству, однако я получаю это предупреждение, а автор — нет.

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

typedef struct stack {
    int data;  
    struct stack *next;

} stack;

stack *top;


void Initialize(stack *s){
    s=malloc(sizeof(stack));
    s->data=NULL;
    s->next=NULL;
    top=s;

    
    return;
}
  

Во время отладки я получаю ошибку сегментации, если я не использую malloc функцию инициализации.
Компилятор MINGW-W64.

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

1. Кажется, вы пропустили немного кода. У этого даже нет main, поэтому мы не можем запустить этот код, чтобы увидеть вашу проблему.

2. NULL часто (но не всегда) определяется как #define NULL ((void *)0) , что приведет к предупреждению в присваивании s->data = NULL; , поскольку s->data имеет тип int . Возможно, вам следует использовать s->data = 0; вместо этого. Кроме того, вы не показываете, как Initialize вызывается функция, но аргумент, соответствующий параметру s , игнорируется функцией.

3. Либо в функции что-то не так Initialize , либо аргумент функции stack *s бесполезен и должен быть заменен локальной переменной. Значение s , переданное функции, немедленно перезаписывается результатом malloc . Вызывающий объект Initialize не получит измененное значение, поскольку указатель s передается по значению. 2-я проблема: вы должны проверить возвращаемое значение malloc . Если он возвращает NULL указатель, следующие обращения к s->data и s->next являются недопустимыми.

4. Аргумент функции бесполезен, поскольку вы присваиваете указатель глобальному stack *top;

5. @Sumsar Очевидно, что автор руководства — программист низкой квалификации. 🙂

Ответ №1:

Предупреждение связано с тем NULL , что макрос определяется (большинством современных компиляторов) как ((void *)0) , поскольку он предназначен для использования только для указателей. Присвоение этого значения data члену вашей структуры вызывает предупреждение.

Чтобы удалить это предупреждение, используйте s->data=0; вместо s->data=NULL; . Автор руководства либо использует более старый компилятор, либо отключил это конкретное предупреждение.

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

 stack* Initialize(void) { // Don't need an argument - just return the 'answer'
    stack* made = malloc(sizeof(stack));
    if (made) { // Don't attempt the initialization if "malloc" failed!
        made->data = 0;
        made->next = NULL;
    }
    return made;
}
  

И затем, когда вы вызываете эту функцию, вы можете присвоить ее возвращаемое значение вашему «глобальному» top указателю:

 //...
top = Initialize();
// You should check that 'top' is not NULL before continuing!
  

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

1. Вы могли бы использовать что-то вроде: stack *test = Initialize (); а затем добавить assert(test != NULL) перед другими проверками.

Ответ №2:

В C макрос NULL определяется как

 ( void * )0
  

то есть тип выражения является нулевым указателем типа void * .

Таким образом, в этом утверждении

 s->data=NULL;
  

указатель присваивается объекту типа int , и компилятор выдает сообщение о том, что вы делаете что-то неправильно.

Инициализация функции по мере ее написания не имеет смысла. Помимо всего, например, это утверждение

 s=malloc(sizeof(stack));
  

не изменяет исходный указатель top , используемый в качестве аргумента функции.

На самом деле функция избыточна.

Тем не менее, если написать такую функцию, то она может выглядеть либо как

 stack * Initialize( void )
{
    return NULL;
}
  

и вызывается как

 stack *top = Initialize();
  

или как

 void Initialize( stack **top )
{
    *top = NULL;
}
  

Или для нового созданного узла он может быть объявлен и определен как

 void Initialize( stack *s, int data, stack *next )
{
    s->data = data;
    s->next = next;
}