c ++参考书堆栈示例效率低下?

时间:2013-05-28 19:29:40

标签: c

我正在阅读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职位?

非常感谢。

2 个答案:

答案 0 :(得分:6)

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

要测试堆栈是否为空,您需要在修改指针之前进行指针比较。如果比较表明堆栈为空,不会递减指针

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

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

原始代码中的问题:tos最初指向stack的元素0,并且push()pop()未对其进行修改。为什么不将它声明为const,为什么它被称为tos(一个暗示“堆栈顶部”的名称,但这就是p用于的内容),以及为什么有这个变量毕竟,假设stack&stack[0]给你相同的价值?我怀疑这些问题是否有任何好的答案。

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

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

(这个答案的先前版本错误地将Schildt归咎于一些不是他的错误;我为此道歉。我仍然建议避免他的书。)

答案 1 :(得分:1)

看起来他正试图让p总是指向堆栈中的某个位置,而不是在像你这样的堆栈之前指向内存中的某个随机位置。只要无效的p在任何地方都没有被解除引用(即,如果它是错误的,那么你就会调用exit),那应该没什么区别。