为什么使用指向指针的指针

时间:2018-10-03 05:28:52

标签: c pointers memory-management

我用C语言创建了一个小程序来测试指向指针的指针。

当我使用函数(第39行)打印列表数据时,在将项目添加到列表后出现错误3221225477。但是,当我注释第39行并从第59行中删除注释时,程序可以工作通常。

如果程序从上到下运行,为什么从操作系统(在这种情况下为Windows)中收到ACCESS_VIOLATION错误?

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


struct employee{
    int id;
    char name[50];
    char street[50];
};

typedef struct element* List;

struct element{
    struct employee data;
    struct element *next;
};

typedef struct element Elem;

List* list_create();
void  list_print(List* list);
int   list_is_empty(List* list);
int   list_add(List* list, struct employee a);

int main(){

    List* list = list_create();

    struct employee a1;
    a1.id = 10;
    strcpy(a1.name, "John");
    strcpy(a1.street, "Address XYZ");

    list_add(list, a1);
   //Sleep(5000); small pause, I thinking it might have something to do with thread.    

  //list_print(list);   // *****line--39 *****;

    return 0;

}

List* list_create(){
    List* li = (List *) malloc(sizeof(List));
    if(li != NULL)
        *li = NULL;
    return li;
}

int  list_add(List* list, struct employee emp){

        Elem* n1 = (Elem *) malloc(sizeof(Elem));
        n1->data = emp;
        n1->next = *list;
        list = &n1;

        list_print(list);    // *****line--59 *****;

}

void list_print(List* list){

    Elem aux = **list;
    printf(" **lista Id:   %d\n", aux.data.id);
    printf(" **lista name: %s\n", aux.data.name);
    printf(" **lista name: %s\n", aux.data.street);

}

1 个答案:

答案 0 :(得分:0)

C是按值传递,而不是按引用传递。因此,函数List* list中的list_addList* list中的main指的是内存中的两个不同位置(它们最初指向{{ 1}},但是它们是两个不同的指针,其初始值相同,而不是相同的指针):在list_add中修改变量list的值不会影响list_add的值在list中。

具体来说:

main中的

list = &n1仅修改该函数内list_add的值,即在调用时复制到list的堆栈帧中的参数值。但是,当您尝试在仍位于list_add内的第59行上进行打印时,将看到更改后的值,并因此从中打印出指针,并按预期工作。

另一方面,当您尝试在第list_add行的第39行上打印list时,main函数中的变量list的值没有更改通过main中第39行之前的赋值list = &n1进行分配,因为这仅更改了该函数范围内的副本的值。因此,在这种情况下,list_add尝试打印一个空列表的元素(因为list_print的值仍然只是list返回的值,而list_create是指向{{1}的指针}指针,因此NULL会解除对Elem aux = **list的引用,然后尝试解除对list指针的引用),从而导致无效的内存访问。

那么,此代码中的问题不是列表的打印位置,而是NULL没有正确更新列表。在解决此问题时,您需要考虑到它只能通过修改作为参数接收的引用值所指向的列表结构来修改列表结构,而不是尝试修改指针本身。如果您确实想修改函数list_addlist中指向main的地址(例如,行list_add试图做的事情),将需要将指向list = &n1的指针传递到list中,而不仅仅是传递指针list_add本身。

代码本身似乎还有其他问题(例如,list仅打印一个元素,并且不处理打印空列表,这就是导致错误在这里实际上是无效的内存访问的原因只是在调用list_print之后错误地使列表为空),但是上述问题是导致此特定问题的原因。