【问题标题】:Using a function call to implement a stack使用函数调用来实现堆栈
【发布时间】:2014-02-21 18:42:00
【问题描述】:

我对 C 的这一部分有点不清楚,因为它有点不像我用过的其他语言,但这可能只是一个愚蠢的问题。我正在尝试实现一个堆栈。我有节点结构,它有我想要传递的信息:

struct position{
   int square[2];
   int counter;
   struct position *prev;
};

所以在main中,我声明并初始化栈底节点,将*prev设置为NULL,然后声明其余部分。我的问题是,当我尝试将它传递给函数pop 时会发生什么?我可以创建一个指向该对象的position 对象并返回它,但是当函数关闭时它会被推离堆栈吗?或者我应该返回 position 并将其设置为等于 main 中的新 position 对象?如果我决定在一个函数中创建几个这样的节点怎么办?功能关闭后它们会保留吗?

编辑:mah 让我想起了我的后续问题,如果它们不存在于函数之外,我应该使用 malloc 为它们在内存中创建空间吗?

【问题讨论】:

  • 首先,您没有设置*prev(读取为prev 地址处的值),但您确实将prev 设置为NULL,即struct position* 类型。这是语法细节,但将* 运算符和foo* 类型混合使用是一个非常常见的初学者错误。
  • @JFA 回复:您的后续行动...大括号 ({}) 不仅定义代码块,正如您在结构定义中看到的那样。您的元素(squarecounterprev)不需要进行 malloc,只需要对 struct position 进行 malloc。它将包含所有元素。
  • @mah 我可能打错了一些东西,使问题产生了误导。我编辑了编辑。
  • @JFA 你要去的地方真的是“可以让它工作”和“这是一个好主意”之间的一条细线。在适当的情况下(这意味着它取决于您编写的确切代码),您可以使其在使用和不使用 malloc 的情况下工作,但是如果您采用 no-malloc 方法,您将很容易破坏某些东西。这种捷径最初可能会奏效,但后来细微的代码更改可能会产生令人惊讶的不良影响。使用指向 malloc 对象的指针可以最大限度地减少这种风险。
  • 这不是一个真正的范围问题,而更多的是你想如何使用你的堆栈的问题。要使其动态化,您需要使用malloc()s。

标签: c struct


【解决方案1】:

对象的生命周期取决于它们的创建位置;例如,如果您在代码块中声明一个结构(其中一个块是{ 及其匹配的} 中的所有内容),则一旦执行离开该块,该结构将不再有效。指向该结构的指针仅在结构有效时才有效。

对于您所描述的内容,您希望使用malloc() 或类似函数动态分配您的结构。动态分配的数据将保持有效(假设您没有覆盖它),直到您 free() 内存,或直到您的程序终止。指向这些内存区域的指针将在同一时间段内保持有效。

考虑:

static struct position *topOfStack = NULL;

void push(struct position *node)
{
    node->prev = topOfStack;
    topOfStack = node;
}

struct position *pop()
{
    struct position *popped = topOfStack;
    if (topOfStack) topOfStack = topOfStack->pref;
    return popped;
}

要使用它,您可以:

f() {
    struct position *node = malloc(sizeof(*node));
    /* ... fill in node details ... */
    push(node);
}

请注意,我是动态分配节点的。如果我刚刚声明了一个struct position node;,我可以合法地调用push(&node);,但是一旦我的函数离开作用域,堆栈中就会有一个无效项(这可能会造成严重破坏)。

【讨论】:

  • 如果我打算只在一个函数中使用堆栈(即main 或我的函数generatestack)但仍然使用函数push,我可以返回顶部position 结构因为它是按值传递的?
  • 如果你通过值而不是引用传递,你仍然需要 malloc 空间来复制它以便将它链接到你的堆栈中......如果你不这样做,你'很可能会遇到问题。在 C 中使用指针不一定是一件容易学习的事情,但是对于构建任何类型的动态列表(例如堆栈)来说,它应该被认为是绝对必要的。
  • 谢谢。我学会了如何使用指针,但我并不总是清楚何时应该使用。
【解决方案2】:

当我尝试将它传递给函数 pop 时会发生什么?

这取决于您的 pop() 函数原型。如果pop的函数原型应该是:

struct position* pop(struct position* stack);

我可以创建一个指向这个的位置对象并返回那个,但是当函数关闭时它会被推离堆栈吗?

你的问题很不清楚,它看起来像是对 C 中实例范围的一个很大的误解。基本上,你有两种分配变量的方法,要么在堆栈上,要么在堆上。您所说的范围是堆栈实例范围。

如果我决定在一个函数中创建多个这样的节点怎么办?功能关闭后它们会保留吗?

基本上,如果您使用堆栈,它们将与声明它们的范围一样长。在 C 中,范围由 {} 定义。例如:

int main() {
    struct position pos1;
    struct position pos2;
    struct position pos3;
    pos3.prev = pos2;
    pos2.prev = pos1;
    pos1.prev = NULL;
    pop(&pos3);
}

在那里你声明了 3 个变量,并将它们关联起来,pop 函数只是重置.prev 链接。但是对于堆栈而言,这种架构并不是很有用,因为它非常有限。

您肯定需要将您的实例推送到堆中,因此使用malloc()free()

// push() pseudocode:
// take stack, iterate over each prev until prev is NULL
// allocate prev with malloc() the same way as for "stack" in main()
// insert values in prev
void push(struct position* stack, int* value);

// pop() pseudocode:
// take stack, iterate over each prev until prev->prev is NULL, 
// then keep prev->prev in a temporary variable
// set prev to NULL
// return temporary variable (former prev->prev)
struct position* pop(struct position* stack);

int main() {
    int value[2];
    struct position* stack = malloc(sizeof(struct position));
    // value is what you want to push to the stack
    value[0] = 42;
    value[1] = 42;
    push(stack, value);
    value[0] = 2;
    value[1] = 20;
    push(stack, value);
    struct position* pos;
    pos = pop(stack);
    // do something with pos->value
    free(pos);
}

在那里你创建一个指向你在堆中分配一些内存的节点的指针。 push() 函数正在分配一些新内存,将新空间的.prev 分配给stack 的地址并用该值填充该内存。 pop() 应该到达最后一个值之前的值,将其指针重置为该值,然后返回该值。

当然,我在这里只是给出概念和想法,我让你去了解真正的实现。不过有一个建议,不要使用包含数组的square,而是在结构中使用两个单独的值,这将使第一次实现更简单。

【讨论】:

    猜你喜欢
    • 2021-10-11
    • 2020-08-13
    • 2019-11-22
    • 2015-08-17
    • 2021-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多