Valgrind error: Invalid write of size 8 while using lists made of struct and malloc

Valgrind error: Invalid write of size 8 while using lists made of struct and malloc

我对 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

注意,如果你需要在函数中访问列表的最后一个节点,那么最好将singly-linked列表定义为two-sidedsingly-linked列表。

例如

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