【问题标题】:Initialize a stack in C by setting pointer to NULL通过将指针设置为 NULL 在 C 中初始化堆栈
【发布时间】:2012-02-22 22:56:22
【问题描述】:

我正在尝试根据以下标头(stack.h)在 C 中实现堆栈:

#ifndef STACK_H
#define STACK_H

/* An element from which stack is consisting */
typedef struct stack_node_ss {
  struct stack_node_ss *next;    /* pointer to next element in stack */
  void *value;                  /* value of this element */
} stack_node_s;

/* typedef so that stack user doesn't have to worry about the actual type of
 * parameter stack when using this stack implementation.
 */
typedef stack_node_s* stack_s;

/* Initializes a stack pointed by parameter stack. User calls this after he
 * has created a stack_t variable but before he uses the stack.
 */
void stack_init(stack_s *stack);

/* Pushes item to a stack pointed by parameter stack. Returns 0 if succesful,
 * -1 otherwise.
*/
int stack_push(void *p, stack_s *stack);

/* Pops item from a stack pointed by parameter stack. Returns pointer to
 * element removed from stack if succesful, null if there is an error or
 * the stack is empty.
 */
void *stack_pop(stack_s *stack);

#endif

但是,作为 C 的新手,我被困在 stack_init 函数中,我在 stack.c 中编写了:

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

void stack_init(stack_s *stack) {
    (*stack)->value = NULL;
    (*stack)->next = NULL;
}

主程序开头:

  int *tmp;
  stack_s stack;
  stack_init(&stack);

这会使我的程序崩溃:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000008
0x0000000100000abf in stack_init (stack=0x7fff5fbffb30) at stack.c:6
6       (*stack)->value = NULL;

你能提示我正确的轨道吗?非常感谢。

【问题讨论】:

  • 这就是为什么你不隐藏类型定义后面的指针类型,除非有一个真的很好的理由。
  • @Ed S.:完全正确。即使typedef struct{... } mystruct_t; 也是有问题的,恕我直言。为什么学校仍然教授这种做法?在我看来,老师们都患有帕斯卡主义的终极形式。
  • @wildplasser:嗯...当我写 C 时,我将 typedef 一个结构体以避免在任何地方写 struct foo f;。我不认为这是有问题的,但是对于指针类型......有龙。

标签: c null initialization linked-list


【解决方案1】:

您必须为**stack 本身分配内存:

*stack = malloc(sizeof(**stack));

但请不要 typedef 指针类型。这确实令人困惑且难以阅读。最好按值传递指针,并留给调用者存储指针,如下所示:

typedef struct stack_node_t
{
    struct stack_node_t * next;
    /* ... */
} stack_node;

stack_node * create_stack()
{
    stack_node * res = calloc(1, sizeof(stack_node));
    return res;
}

void destroy_stack(stack_node * s)
{
    if (!next) return;

    stack_node * next = s->next;
    free(s);
    destroy_stack(next);
}

// etc.

那么你可以说:

stack_node * s = create_stack();

// use s

destroy_stack(s);
s = NULL;  // some people like this

【讨论】:

  • +1 表示正确并且提到typdef 指针类型通常是个坏主意。
  • +1 以获得明确的解释!现在我的堆栈工作了,但是在为 *s 释放内存后,您能简要解释一下“s = NULL”吗?如果我不写“s = NULL”,在“free(s)”之后,s 的值是否会不确定/随机取决于系统?
  • 顺便说一句。 “s = NULL”在我的实现中确实非常有用,因为我必须释放堆栈,如果用户弹出堆栈直到它为空(以避免在用户退出程序的情况下泄漏),但我如果用户继续再次压入堆栈,则必须再次为堆栈分配内存。所以首先,没有“s = NULL”,当堆栈变空时,我遇到了崩溃,当尝试在推送函数中使用“if(stack)”检查堆栈是否已被清空并且必须分配内存时再次堆栈。
  • 您不需要将指针设置为 null,但您可以选择使用 null 作为标记值来指示当前没有动态内存,并且您可以同意一项策略,即一个指针只有在指向有效内存时才为非空——如果远处的代码需要知道指针的状态并能够清理它,这可能很有用。
【解决方案2】:

您正在取消引用未初始化的指针,导致未定义的行为。

因为这个函数正在创建一个新的堆栈,你需要为堆栈分配一些动态内存,然后设置指针指向新分配的内存:

void stack_init(stack_s *stack) {
    *stack = malloc(sizeof(**stack)); // create memory for the stack

    (*stack)->value = NULL;
    (*stack)->next = NULL;
}

stack_s stack;
stack_init(&stack);

那么你应该有一个名为stack_destroy的函数,它将free动态内存并将指针设置为NULL

void stack_destroy(stack_s *stack) {
    free(*stack);
    *stack = NULL;
}

【讨论】:

  • 只要它始终是stack_node_s。使用sizeof(**stack) 可能更安全
  • @EdS。你是对的。我的无效指针感觉很刺痛,但我忘记了 sizeof 是在编译时,所以没关系
【解决方案3】:

您应该将堆栈初始化为 NULL - 不要将 NULL 值推送给它:

void stack_init(stack_s *stack) {
    *stack=NULL;
}

【讨论】:

  • 这是我见过的最没用的init 函数……它并没有解决问题。 -1
  • @EdS。我知道它没用,但是 OP 想要一个函数......他试图将 NULL 推送到无效地址,而没有理由这样做。他所需要的只是将 NULL 分配给他得到的stack_s。 (推送东西需要分配)
  • 不,他有一个init 函数,并且希望您之后能够使用堆栈。您的实现只是保证您将无法这样做。 OP 的问题是他没有意识到函数的输入应该是指向堆栈指针的有效指针。他声明了一个指向堆栈的指针(通过 typdef 隐藏),但从未初始化它。如果有的话,你应该做相反的事情; malloc 就在此时此地。
  • @EdS。为什么不能用?推送一个值,为其分配空间,将值放入其中,使其下一个指向当前第一个成员(即**stack,而不是(*stack)-&gt;next),并使*stack 指向它。你能解释一下赛斯回答中(*stack)-&gt;value = NULL; 的那一行吗?
  • stack_s 已经是一个指针,所以stack_s* 是一个pointer-to-pointer。这就是为什么有两个取消引用。这允许您初始化参数。您的函数将指针设置为 NULL... 这意味着它现在无法使用。我看不出这有什么用,也没有初始化。如果我打电话给init,我希望得到一个有效的对象作为回报。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-24
  • 2017-09-06
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
相关资源
最近更新 更多