我试图在C中创建队列(作为一个类项目)。他们提供的演示代码是Borland Turbo C.我试图通过gcc
重建程序。虽然代码在Turbo C中运行完美,但在gcc
运行时期间Segmentation Fault (core dumped)
会抛出错误struct node {
int data;
struct node *link;
};
struct queue {
struct node *front;
struct node *rear;
};
void initQ(struct queue *q) {
q->front = q->rear = NULL; // Error : Segmentation Fault! (core dumped)
}
void main() {
struct queue *Q;
initQ(Q);
}
。
我没有包含不必要的代码部分。逐行试验并测试它。
Segmentation Fault
我确信问题与编译器中的C版本有关。由于Turbo C非常古老,它不支持最新修复。我在代码的其他各个部分出现类似的void displayQ(struct queue *q) {
struct node *temp;
temp->link = q->front; // Error : Segmentation Fault! (core dumped)
}
错误,如:
{{1}}
问题1:为什么gcc会出现这样的运行时错误? (在此代码中)
问题2:为什么代码在Turbo C中运行良好而不是gcc?
问题3:是否有替代这种编程风格的方法?
答案 0 :(得分:4)
您需要预留空间(使用malloc
):
struct queue *Q = malloc(sizeof(*Q));
initQ(Q);
或更好calloc
:
struct queue *Q = calloc(1, sizeof(*Q));
/* initQ(Q); you don't need this, calloc set all members to NULL */
不要忘记最后致电free(Q);
。
答案 1 :(得分:3)
void initQ(struct queue *q) { q->front = q->rear = NULL; }
在您的代码q
中,当您使用它并指向某个随机地址时,它不会被初始化。尝试:
struct queue Q;
initQ(&Q);
答案 2 :(得分:2)
Q
尚未初始化为特定的任何位置,这意味着它是无效的指针值。当您将其传递给initQ
时,您会尝试使用->
运算符取消引用它。尝试取消引用无效指针会导致未定义的行为,这可能意味着从代码按预期工作到崩溃到损坏数据。
无论Turbo C下有什么初始值,不确定值Q
,它都位于可访问的内存区域(尽管如此,它仍然是无效的指针),所以你不会得到你在gcc中所做的段错误。
当您致电initQ
时,q
必须指向有效对象。你可以打电话给initQ
现有的队列实例,如下所示:
struct queue Q; // Q is an instance of struct queue, not a pointer to it
initQ( &Q );
或者,您可以创建一个为新的动态分配内存的伪构造函数
队列对象并在其上调用initQ
:
struct queue *create_queue_element( )
{
struct queue *q = malloc( sizeof *q );
if ( q )
initQ( q );
return q;
}
...
struct queue *Q = create_queue_element();
答案 3 :(得分:1)
问题1:为什么gcc会出现这样的运行时错误? (在此代码中)
因为代码不正确 - 您取消引用初始化指针。
问题2:为什么代码在Turbo C中运行良好而不是gcc?
它不能正常工作,它根本不检测错误。 16位DOS代码没有内存保护和虚拟内存的好处。它无法区分属于您的进程的实际内存(在DOS中没有进程内存,因为它不是多任务处理。整个DOS子系统在其自己的受保护VM中运行。您可能拥有的唯一保护是取消引用NULL指针。指针的值将解析为某些非确定性内存位置,并且可能看起来正常工作,直到其他东西使用相同的内存位置用于某些其他目的 - 可能永远不会发生,有时可能发生,或者可能在执行时发生在另一台机器上 - 你无法分辨。
大多数情况下,当您的应用程序变大并使用越来越多的内存时,问题将在代码编写后很久就会出现。如果引用的位置恰好位于您的代码空间中,那么您将有可能在执行时随机更改代码。这些错误很难找到,因为时间因素和代码接近度通常将因果关系分开。
你写的是:
struct queue *Q = NULL ;
您可能遇到过运行时错误报告。这不会使代码更正确,但它确实使得更有可能检测到这样的错误。
问题3:是否有替代这种编程风格的方法?
这不是风格问题,而是正确性问题。但是,如果您始终将声明上的指针初始化为指向有效实例或NULL,则可以避免该问题,或者至少可以及早发现错误。
即使在这种情况下,在32位代码中,错误是在运行时检测到的,这无法保证 - 您的初始化指针驻留在堆栈上,在其他情况下可能包含您的进程中有效地址的值,你只是在踩你自己的数据。