1

我正在阅读 Herbert Schildt 的 C++ 参考书,本书的 c 部分有一个堆栈示例,类似于以下内容:

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

#define SIZE 50

void push(int i);
int pop(void);

int *tos, *p, stack[SIZE];

int main(void) {
    tos = stack;
    p = stack;
    // push, pop, etc
    return 0;
}
void push(int i) {
    p++;
    if (p==(tos+SIZE)) {
        printf("Stack overflow");
        exit(1);
    }
    *p = i; 
}
int pop(void) {
    if (p==tos) {
        printf("Stack Underflow");
        exit(1);
    }
    p--;
    return *(p+1);
}

上面的堆栈实现不使用 TOS 来存储值。第一个值存储在 TOS+1。我不明白您为什么要这样做,因为它似乎浪费了内存空间。

我已将其重写为下面的示例,这不会浪费空间(编辑:阅读所选答案 - 下面的代码是可疑的!):

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

#define SIZE 5

void push(int i);
int pop(void);

int *tos, *p, stack[SIZE];

int main(void) {
    tos = stack;
    p = stack;
    // push, pop, etc
    return 0;
}
void push(int i) {
    if (p==(tos+SIZE)) {
        printf("Stack overflow");
        exit(1);
    }
    *p = i; 
    p++;
}
int pop(void) {
    p--;
    if (p<tos) {
        printf("Stack Underflow");
        exit(1);
    }
    return *p;
}

Schildt 的实施是否有理由不使用 TOS 位置?

非常感谢。

4

2 回答 2

6

原始代码虽然风格不佳,但具有明确定义的行为。您修改后的代码的行为未定义。

测试栈是否为空,需要在修改指针前做指针比较。如果比较表明堆栈为空,则不要递减指针

如果p指向stack[0],则p--;具有未定义的行为,即使p在那之后您从未取消引用。您不能合法地形成指向数组开头之前的指针,或者超过数组结尾的多个元素。(您可以合法地在数组末尾形成一个指针,但不能取消引用它。)

它可能在大多数基于堆栈的实现中“起作用”——这只是意味着这是一个很难通过测试找到的错误。

原代码中的一个问题:tos最初指向 的元素 0 stack,并且它没有被push()or pop() 修改。为什么不声明它const,为什么要调用它tos(一个暗示“栈顶”的名称,但这就是它的p用途),以及为什么有这个变量,给定stack&stack[0]给你相同的值?我怀疑这些问题是否有任何好的答案。

同样在原始代码中,exit(1);它不是一种表示失败的可移植方式(在某些系统上它表示成功)。exit(EXIT_FAILURE);是这样做的正确方法,除非您故意编写不可移植的代码。(如果您正在编写一个仅在类 Unix 系统上使用的程序,这可能是有意义的,但如果您正在为语言教程编写示例,则不是这样)。

我的建议:谷歌“Schildt”以了解作者作为 C 和 C++ 书籍作者极差的声誉,然后找到一本由真正了解该语言的人写的书。

(此答案的先前版本错误地将一些不属于他的错误归咎于 Schildt;对此我深表歉意。我仍然建议避免阅读他的书。)

于 2013-05-28T20:03:57.537 回答
1

看起来他试图让 p 始终指向堆栈中的某个位置,而不是像您那样在堆栈之前指向内存中的某个随机位置。只要无效的 p 没有在任何地方取消引用(即,如果它是错误的,你就调用 exit )就不应该有太大的区别。

于 2013-05-28T19:40:49.293 回答