回复 18楼 BlueGuy
其实C那么容易出错的很大一部分原因都是没有得到恰当的初始化造成的,因为我们可能使用到一个未知的内存块,造成段错误,并且稍加修改就能产生这样一个版本:
程序代码:
/**
* Stack2.h
* Code by Kenier Lee.
*/
#ifndef _KENIER_LEE_STACK_H
#define _KENIER_LEE_STACK_H
#define STACK_SIZE 1024 /* Size of the Stack */
typedef struct StackTag Stack; /* Prepare type definition */
struct StackTag {
int storage[STACK_SIZE]; /* Array of int */
int* tp; /* Top pointer */
void (*Push)(Stack*, int);
int (*Pop)(Stack*);
int (*Peek)(Stack*);
int (*GetSize)(Stack*);
int* (*GetTopPointer)(Stack*);
int* (*GetBottomPointer)(Stack*);
};
void InitStack(Stack*);
#endif /* _KENIER_LEE_STACK_H */
/**
* Stack2.c
* Code by Kenier Lee.
*/
#include "Stack2.h"
#include <stdio.h>
void Push(Stack*, int);
int Pop(Stack*);
int Peek(Stack*);
int GetSize(Stack*);
int* GetTopPointer(Stack*);
int* GetBottomPointer(Stack*);
void InitStack(Stack* stack) {
stack->tp = stack->storage;
stack->Push = Push;
stack->Pop = Pop;
stack->Peek = Peek;
stack->GetSize = GetSize;
stack->GetTopPointer = GetTopPointer;
stack->GetBottomPointer = GetBottomPointer;
}
void Push(Stack* stack, int value) {
int size = GetSize(stack);
if (size == STACK_SIZE) /* Full */
printf("Error: The stack is full.\n");
else
*stack->tp++ = value;
}
int Pop(Stack* stack) {
int value = 0;
if (stack->tp == stack->storage) /* Empty */
printf("Error: The stack is empty.\n");
else
value = *--stack->tp;
return value;
}
int Peek(Stack* stack) {
int value = 0;
if (stack->tp == stack->storage) /* Empty */
printf("Error: The stack is empty.\n");
else
value = *stack->tp;
return value;
}
int GetSize(Stack* stack) {
return stack->tp - stack->storage;
}
int* GetTopPointer(Stack* stack) {
return stack->tp;
}
int* GetBottomPointer(Stack* stack) {
return stack->storage;
}
/**
* Stack2Test.c
* Code by Kenier Lee.
*/
#include "Stack2.h"
#include <stdio.h>
int main(void) {
Stack stack;
int i, *tPointer, *bPointer;
InitStack(&stack); // (1)
for (i = 0; i < 10; ++i)
stack.Push(&stack, i);
for (i = 0; i < 10; ++i)
printf("%d ", stack.Pop(&stack));
printf("\n");
for (i = 0; i < 10; ++i)
stack.Push(&stack, i);
printf("Sizeof stack: %d\n", stack.GetSize(&stack));
tPointer = stack.GetTopPointer(&stack);
bPointer = stack.GetBottomPointer(&stack);
while (bPointer != tPointer)
printf("%d ", *bPointer++);
printf("\n");
bPointer = stack.GetBottomPointer(&stack);
while (tPointer != bPointer)
printf("%d ", *--tPointer);
printf("\n");
for (i = 0; i < 10; ++i)
printf("%d ", stack.Pop(&stack));
printf("\n");
printf("%d\n", stack.Pop(&stack));
printf("%d\n", stack.Peek(&stack));
return 0;
} /* Output:
9 8 7 6 5 4 3 2 1 0
Sizeof stack: 10
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
Error: The stack is empty.
0
Error: The stack is empty.
0
*/
这里把函数与自定义数据类型绑定在了一起,看起来非常成功,但这里有几个危险:
1、(1)这句话在任何时候都有可能被遗忘,我以前也经常这样,导致我几天都没找到问题所在。。。
2、tp的值可能被修改,特别是那些认为自己聪明的程序员,想利用它达到某些特殊的效果。
还有很多程序员不愿意使用stack.GetTopPointer和stack.GetBottomPointer来获得顶和底指针,而喜欢直接用stack.tp和stack.storage,看起来没有任何错误,因为这两个字段可以直接使用,而且节省函数调用开销。想一想,如果有一点我修改了实现,我认为用tp作为栈顶指针的名字是不好的,storage也如此,那么任何直接使用了tp和storage的代码都需要重新修改。并且我认为C并不能做到面向对象,只能做到不完全的基于对象。