#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;
}