Valgrind错误:使用由struct和malloc构成的列表时,大小为8的无效写入

问题描述

我对Valgrind有问题,今天是我开始使用它的第一天,我真的不习惯。 这是我的代码

//list' typedef
typedef struct nodo_s {
    int n;
    struct nodo_s * next;
} nodo_t;


//function with error
void cambiaDirezionesinistra(nodo_t * head)
{
    nodo_t * tmp,*temp;
    for(tmp = head; tmp != NULL; tmp = tmp->next)
        ;
    printf("1s: %d\n",tmp->n);
    temp = head;
    printf("2s: %d\n",temp->n);
    head = temp->next;
    printf("3s: %d\n",head->n);
    tmp->next = temp; ----> error
    printf("4s: %d\n",tmp->n);
    temp->next = NULL;
}


//main
int main()
{
    int i;
    int dir;
    nodo_t * head = NULL;

    for(i = 0; i < N; i ++)
        head = nuovoNodo(head);

    stampaLista(head);

    printf("Inserisci la direzione di scambio,0 per sinistra e 1 per destra.\n");
    scanf("%d",&dir);

    if(dir == 0)
        cambiaDirezionesinistra(head);
    else if(dir == 1)
        cambiaDirezioneDestra(head);

    stampaLista(head);

    return 0;
}

这是该错误的Valgrind报告:

 ==511== Invalid write of size 8
 ==511==    at 0x10930F: cambiaDirezionesinistra (20200120_6.c:55)
 ==511==    by 0x1094EE: main (20200120_6.c:103)
 ==511==  Address 0x8 is not stack'd,malloc'd or (recently) free'd
 ==511== 
 ==511== 
 ==511== Process terminating with default action of signal 11 (SIGSEGV)
 ==511==  Access not within mapped region at address 0x8
 ==511==    at 0x10930F: cambiaDirezionesinistra (20200120_6.c:55)
 ==511==    by 0x1094EE: main (20200120_6.c:103)

我尝试了很多在互联网上找到的“解决方案”,但是我无法以任何方式解决它。谢谢。

解决方法

函数cambiaDirezioneSinistra具有未定义的行为。

对于初学者,指向头节点的指针按值传递。因此,该函数处理指向头节点的指针的值的副本。更改副本不会影响原始指针。

void cambiaDirezioneSinistra(nodo_t * head)

指向头节点的指针可以等于NULL。但是函数内没有检查head是否等于NULL。

此循环后

for(tmp = head; tmp != NULL; tmp = tmp->next)
    ;

指针tmp将等于NULL。因此,使用此指针访问结构的数据成员没有任何意义。

可以通过以下方式声明和定义函数

//function with error
void cambiaDirezioneSinistra( nodo_t **head )
{
    if ( *head != NULL && ( *head )->next != NULL )
    {
        nodo_t *last = *head;

        while ( last->next ) last = last->next;

        last->next = *head;
        *head = ( *head )->next;

        last->next->next = NULL;
    }
}

该函数的调用方式类似于

cambiaDirezioneSinistra( &head );

这是一个演示程序。

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

//list' typedef
typedef struct nodo_s 
{
    int n;
    struct nodo_s * next;
} nodo_t;


//function with error
void cambiaDirezioneSinistra( nodo_t **head )
{
    if ( *head != NULL && ( *head )->next != NULL )
    {
        nodo_t *last = *head;

        while ( last->next ) last = last->next;

        last->next = *head;
        *head = ( *head )->next;

        last->next->next = NULL;
    }
}

int append( nodo_t **head,int n )
{
    while ( *head ) head = &( *head )->next;
    
    *head = malloc( sizeof( nodo_t ) );
    int success = *head != NULL;
    
    if ( success )
    {
        ( *head )->n = n;
        ( *head )->next = NULL;
    }
    
    return success;
}

void display( const nodo_t *head )
{
    for ( ; head != NULL; head = head->next )
    {
        printf( "%d -> ",head->n );
    }
    
    puts( "null" );
}

void clear( nodo_t **head )
{
    while ( *head )
    {
        nodo_t *tmp = *head;
        *head = ( *head )->next;
        free( tmp );
    }
}

int main(void) 
{
    nodo_t *head = NULL;
    
    const int N = 10;
    
    for ( int i = 0; i < N; i++ ) append( &head,i );
    
    display( head );
    
    for ( int i = 0; i < N; i++ )
    {
        cambiaDirezioneSinistra( &head );
    
        display( head );
    }
    
    clear( &head );
    
    return 0;
}

其输出为

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 0 -> null
2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 0 -> 1 -> null
3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 0 -> 1 -> 2 -> null
4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 0 -> 1 -> 2 -> 3 -> null
5 -> 6 -> 7 -> 8 -> 9 -> 0 -> 1 -> 2 -> 3 -> 4 -> null
6 -> 7 -> 8 -> 9 -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> null
7 -> 8 -> 9 -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> null
8 -> 9 -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> null
9 -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null

请注意,如果您需要访问函数中列表的最后一个节点,那么最好将单链接列表定义为双面单链接列表。

例如

typedef struct lista_t
{
    nodo_t *head;
    nodo_t *tail;
} lista_t;