Segmentation Fault(core dumped) 使用链表进行队列操作

Posted

技术标签:

【中文标题】Segmentation Fault(core dumped) 使用链表进行队列操作【英文标题】:Segmentation Fault(core dumped) Queue Operation using linked list 【发布时间】:2022-01-11 02:25:06 【问题描述】:

我用c写了一段代码,用链表实现队列操作,比如插入、删除、查看和显示。

我使用 vscode 来运行和编译我的代码。

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
    int data;
    struct Node *next;
node;
typedef struct Queue
    node *front;
    node *rear;
queue;
queue *q=NULL;
int isempty(queue *);
void create(queue *);
queue *insert(queue *);
queue *delete(queue *);
void display(queue *);
void peek(queue *);
int main()
    int o;
    create(q);
    do
        printf("\nQueue Operations");
        printf("\n1. Insert");
        printf("\n2. Delete");
        printf("\n3. Peek");
        printf("\n4. Display");
        printf("\n5. Exit");
        printf("\nEnter Option : ");
        scanf("%d",&o);
        switch(o)
            case 1:
                insert(q);
                break;
            case 2:
                delete(q);
                break;
            case 3:
                peek(q);
                break;
            case 4:
                display(q);
                break;
            case 5:
                break;
            default:
                printf("Invalid Option");
                break;
        
    
    while(o!=5);

void create(queue *q)

    q->front=NULL;
    q->rear=NULL;

queue *insert(queue *q)
    node *p;
    p=(node *)malloc(sizeof(node));
    printf("Enter Data : ");
    scanf("%d",&p->data);
    if(q->front=NULL)
        q->front=p;
        q->rear=p;
        q->front->next=q->rear->next=NULL;
    
    else
        q->rear->next=p;
        q->rear=p;
        q->rear->next=NULL;
    
    return q;

int isempty(queue *q)
    if(q->front==NULL)
        return 1;
    else 
        return 0;

queue *delete(queue *q)
    node *p;
    int t;
    p=q->front;
    t=isempty(q);
    if(t==1)
        printf("Queue Empty");
    else
        q->front=q->front->next;
        printf("Value Deleted : %d",p->data);
        free(p);
    
    return q;

void peek(queue *q)
    int t;
    t=isempty(q);
    if(t==1)
        printf("Queue Empty");
    else
        printf("Peek:%d",q->front->data);

void display(queue *q)
    node *p;
    p=q->front;
    if(p==NULL)
        printf("Queue is Empty");
    else
        while(p!=q->rear)
            printf("%d\t",p->data);
            p=p->next;
        
        printf("%d\t",p->data);
    

我不明白为什么我在这个问题中遇到分段错误。

这段代码在我的书中,我只是盲目地复制了这个,但我仍然收到错误。 我还在在线编译器中测试了代码,以确保我的机器没有任何故障但仍然遇到同样的问题。

如果有人可以帮助我。

【问题讨论】:

如果您已经编写了一大段代码并且没有经过测试,结果发现它崩溃了,那么您应该首先使用 debugger 来捕获崩溃并定位在您的代码中何时何地发生。一旦知道这一点,您应该尝试将代码最小化为复制崩溃的最小示例,实质上是创建一个minimal reproducible example。一旦你有了这个最少的代码,就更容易调试,看看会发生什么以及如何解决它。然后逐段添加代码的其他部分,在中间进行测试(你应该从一开始就这样做)。 @Someprogrammerdude 我试过这样做。当我调试代码时,我在 create() 中遇到错误,我正在初始化 q-&gt;next=NULL. 当你在main函数中调用create(q)时,q指向哪里?请记住,指针实际上就是它听起来的样子,它指向其他地方,并且要使其有效,您的代码必须实际上使其指向有效的地方。 【参考方案1】:

对于初学者来说,当函数依赖于文件范围变量时,就像在你的程序中函数依赖于变量 q 一样,这是一个坏主意。

queue *q=NULL;

其次,您在函数 create 中使用了空指针。

void create(queue *q)

    q->front=NULL;
    q->rear=NULL;

调用未定义的行为。

声明queue 类型的指针而不是声明queue 类型的对象没有什么意义。

所以不是文件范围内的这个定义

queue *q=NULL;

最好写在main里

queue q =  .front = NULL, .rear = NULL ;

queue q;
create( &q );

函数isempty可以写得更简单

int isempty( const queue *q )

    return q->front == NULL;

函数insert 不应该问任何问题。添加到队列中的值应作为参数传递给函数。

应该提供值的是函数的调用者。

注意内存分配可能会失败,你应该在函数中处理这种情况。否则,该函数会再次调用未定义的行为。

在这个 if 语句中使用赋值而不是使用比较

if(q->front=NULL)

函数可以这样写

int insert( queue *q, int data )

    node *p = malloc( sizeof( *p ) );
    int success = p != NULL;

    if ( success )
    
        p->data = data;

        if ( q->front == NULL )
        
            q->front = p;
        
        else
        
            q->rear->next = p;
        

        q->rear = p; 
    

    return success;

在函数delete 中,如果队列仅包含一个节点,则不会更新q-&gt;rear

同样,该函数不应发出任何消息。决定是否输出消息的是函数的调用者。

函数的定义方式如下

int delete( queue *q )

    int success = !isempty( q );

    if ( success )
    
        node *p = q->front;
        q->front = q->front->next;

        if ( q->front == NULL ) q->rear = NULL;

        free( p );
    

    return success;
      

函数peek 应该通过引用其数据成员data 返回,因为函数的用户可能想要处理该值。

函数可以如下所示

int peek( queue *q, int *data )

    int success = !isempty( q );

    if ( success )
    
        *data = q->front->data;
    

    return success;

函数display的参数应该有限定符const,因为队列在函数内没有改变。

函数可以通过以下方式定义

void display( const queue *q )

    if ( isempty( q ) )
    
        puts( "Queue is Empty" );
    
    else
    
        for ( const node *p = q->front; p != NULL; p = p->next )
         
            printf( "%d", p->data );
            if ( p != q->rear ) putchar( '\t' );
        
        putchar( '\n' );
    

这是您的更新程序。

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

typedef struct Node

    int data;
    struct Node *next;
 node;

typedef struct Queue

    node *front;
    node *rear;
 queue;

int isempty( const queue *q )

    return q->front == NULL;


void create(queue *q)

    q->front=NULL;
    q->rear=NULL;


int insert( queue *q, int data )

    node *p = malloc( sizeof( *p ) );
    int success = p != NULL;

    if ( success )
    
        p->data = data;
        
        if ( q->front == NULL )
        
            q->front = p;
        
        else
        
            q->rear->next = p;
        

        q->rear = p; 
    

    return success;


int delete( queue *q )

    int success = !isempty( q );

    if ( success )
    
        node *p = q->front;
        q->front = q->front->next;

        if ( q->front == NULL ) q->rear = NULL;

        free( p );
    

    return success;
 

int peek( queue *q, int *data )

    int success = !isempty( q );

    if ( success )
    
        *data = q->front->data;
    

    return success;


void display( const queue *q )

    if ( isempty( q ) )
    
        puts( "Queue is Empty" );
    
    else
    
        for ( const node *p = q->front; p != NULL; p = p->next )
         
            printf( "%d", p->data );
            if ( p != q->rear ) putchar( '\t' );
        
        putchar( '\n' );
    


int main( void ) 

    queue q;
    create( &q );
 
    int option = 5;
    
    do
    
        printf("\nQueue Operations");
        printf("\n1. Insert");
        printf("\n2. Delete");
        printf("\n3. Peek");
        printf("\n4. Display");
        printf("\n5. Exit");
        
        printf("\nEnter Option : ");
        
        scanf( "%d", &option );
        
        switch( option )
        
        case 1:
        
            printf( "Enter Data : " );
            int data;
            
            if ( scanf( "%d", &data ) == 1 )
            
                if ( !insert( &q, data ) )
                
                    puts( "Error: not enough memory." );
                
            
            else
            
                puts( "Invalid input." );
            
            
            break;
        

        case 2:
        
            if ( !delete( &q ) )
            
                puts( " Error: queue is empty." );              
            
            else
            
                puts( "First element of the queue has been deleted." );
            
            
            break;
        
        
        case 3:
        
            int data;
            if ( peek( &q, &data ) )
            
                printf( "The value is %d\n", data );
            
            else
            
                puts( "Error: the queue is empty." );
            
            
            break;
        
        
        case 4:
        
            display( &q );
            break;
                   
        
        case 5:
        
            break;
        
        
        default:
        
            puts( "Invalid Option" );
            break;
        
               
     while( option != 5 );

    return 0;

程序输出可能看起来像

Queue Operations
1. Insert
2. Delete
3. Peek
4. Display
5. Exit
Enter Option : 1
Enter Data : 1

Queue Operations
1. Insert
2. Delete
3. Peek
4. Display
5. Exit
Enter Option : 1
Enter Data : 2

Queue Operations
1. Insert
2. Delete
3. Peek
4. Display
5. Exit
Enter Option : q
Enter Data : 3

Queue Operations
1. Insert
2. Delete
3. Peek
4. Display
5. Exit
Enter Option : 4
1   2   3

Queue Operations
1. Insert
2. Delete
3. Peek
4. Display
5. Exit
Enter Option : 5

请注意,您还需要编写一个清除队列的函数,该函数将释放队列中所有已分配的内存。

【讨论】:

【参考方案2】:

创建函数需要分配内存你不能在没有分配内存的情况下创建每当你在创建函数中分配它或传递已经分配内存的队列时(你不能同时做这两种解决方案只是其中一个),第一个更适合制作有组织的代码。

queue* create(queue *q)

q= (queue*)malloc(sizeof(queue));
q->front=NULL;
q->rear=NULL;
return q;

插入函数 if(q-&gt;front=NULL) 中的另一个错误会使 q->front 实际上分配为 null 并导致分段错误,您应该检查 == 未分配 =

if(q-&gt;front==NULL) 会让它更正确地工作

【讨论】:

【参考方案3】:

当指针被取消引用时,应该在使用前通过 malloc 分配相当数量的内存。因此,在创建链表时,应该分配队列指针并将其传递回 main 以使用它。下面附上修改后的队列创建代码供参考,

queue* create(queue *q)

q= (queue*)malloc(sizeof(queue));
q->front=NULL;
q->rear=NULL;
return q;

同时更新 main 中的调用函数。

【讨论】:

它现在正在分配队列,但是当我调用 q-&gt;front==NULL 时在 insert() 中显示分段错误。我是否必须在每个部分中分配 q 才能运行? 在你写的代码中 q->front=NULL 不是 q->front==NULL 确保你写的正确,正如我在我的回答中指出的那样......我发现你在这里写的正确但是在写错的问题中......确保它是==不是=【参考方案4】:

q 没有指向任何东西。当你在 create() 中尊重它时,你正在写入随机内存,这通常会导致崩溃。

您应该将 q 定义为类型队列(无指针)并将其地址传递给子函数。

【讨论】:

不是随机内存。因为q原本是一个全局变量,所以会被初始化为空指针。

以上是关于Segmentation Fault(core dumped) 使用链表进行队列操作的主要内容,如果未能解决你的问题,请参考以下文章

Linux程序Segmentation fault (core dumped)

Segmentation Fault(core dumped) 使用链表进行队列操作

Core Dump (Segmentation fault) in C/C++

运行C++程序报错:Segmentation fault(coredump) 请问为啥?

conda pip 安装 dgl 并运行demo 出现:Segmentation fault (core dumped) 错误

C++文档阅读笔记-Core Dump (Segmentation fault) in C/C++