简体   繁体   English

双指针取消引用的分段错误

[英]Segmentation fault on double pointer dereference

The following code works fine without the statement d = *dummy; 以下代码工作正常,没有语句d = *dummy; which is a double pointer dereference. 这是一个双指针解引用。 However if this line is present, a segmentation fault occurs. 但是,如果存在此行,则会发生分段错误。 Why so? 为什么这样?

The code allocates and initializes memory for data structs dynamically. 代码动态地为数据结构分配和初始化内存。 I was trying to simplify access to the returned pointer-to-pointer. 我试图简化对返回的指针指向的访问。

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

typedef struct s_dummy {
    char dummy_number;  
} Dummy;

int mock_read_from_external_source() {
    return 4;
}

int load_dummies(Dummy** dummies, int* num_of_dummies) {
    *num_of_dummies = mock_read_from_external_source();
    *dummies = (Dummy*) calloc(*num_of_dummies, sizeof(Dummy));

    if (!dummies) {
        return 1; // allocation unsuccessful
    }

    // Iterate dummies and assign their values...

    for (int i = 0; i < *num_of_dummies; i++) {
        (*dummies + i)->dummy_number = i;
    }

    return 0;
}

void main() {
    Dummy** dummies;
    Dummy* d;
    int num_of_dummies = 0;
    int *p_num_of_dummies = &num_of_dummies;
    int err;

    err = load_dummies(dummies, p_num_of_dummies);

    // Segmentation fault occurs when dummies is dereferenced
    d = *dummies;

    if (err) {
        exit(err);
    }

    for (int i = 0; i < num_of_dummies; i++) {
        printf("Dummy number: %d\n", (*dummies + i)->dummy_number);
    }
}

Thanks in advance. 提前致谢。

You are getting the fault because of UB , in part caused by trying to use variable objects without memory. 你因为UB而得到了错误,部分原因是试图使用没有内存的变量对象。 dummies , although created as a Dummies ** , has never been provided memory. dummies虽然被创造为Dummies ** ,却从来没有提供记忆。 At the very least, your compiler should have warned you about dummies not being initialized in this call: 至少,您的编译器应该警告您在此调用中没有初始化的dummies

err = load_dummies(dummies, p_num_of_dummies);

This is easily addressed by simply initializing the variable when it is created: 只需在创建变量时初始化变量即可轻松解决此问题:

Dummy** dummies = {0}; //this initialization eliminates compile time warnings
                ^^^^^

Then come the run-time errors. 然后是运行时错误。 The first is called a fatal run-time on my system, which means the OS refused to continue because of a serious problem, in this case an attempt to dereference a null pointer in this line: 第一个在我的系统上称为致命运行时,这意味着操作系统由于严重问题而拒绝继续,在这种情况下尝试取消引用此行中的空指针:

dummies = (Dummy ) calloc(*num_of_dummies, sizeof(Dummy)); dummies =(Dummy )calloc(* num_of_dummies,sizeof(Dummy));

Because you created a Dummy ** called dummies , the first step is to create memory for the pointer to pointers dummies , then create memory for the several instances of dummies[i] that will result. 因为你创建了一个名为dummiesDummy ** ,第一步是为指针dummies指针创建内存,然后为将产生的几个dummies[i]实例dummies[i]创建内存。 Only then can the members of any of them be written to. 只有这样才能将其中任何一个成员写入。

Here is one method illustrating how memory can be created for a Dummies pointer to pointers, ( d ) and several Dummies instances ( d[i] ): 这是一个方法,说明如何为指针,( d )和几个Dummies实例( d[i] )的Dummies指针创建内存:

Dummy ** loadDummies(int numPointers, int numDummiesPerPointer)
 {
     int i;
     Dummy **d = {0};
     d = malloc(numPointers * sizeof(Dummy *));//Create Dummies **
     if(!d) return NULL;
     for(i=0;i<numPointers;i++)
     {   //Now create Dummies *
         d[i] = malloc(numDummiesPerPointer*sizeof(Dummy)); //random size for illustration
         if(!d[i]) return NULL;
     }
     return d;
 }

In your main function, which by the way should really be prototyped at a minimum as: int main(void){...} , this version of loadDummies could be called like this: 在你的main函数中, 顺便说一下它应该至少原型为: int main(void){...} ,这个版本的loadDummies可以这样调用:

...
Dummies **dummies = loadDummies(4, 80);
if(!dummies) return -1;//ensure allocation of memory worked before using `dummies`.
...

After using this collection of dummies , be sure to free all of them in the reverse order they were created. 使用这个dummies集合后,请务必按照创建它们的相反顺序释放所有这些dummies Free all instances of dummies[0]-dummies[numPointers-1] first, then free the pointer to pointers, dummies 首先释放所有dummies[0]-dummies[numPointers-1]实例,然后将指针释放到指针, dummies

void freeDummies(Dummy **d, int numPointers)
{
    int i;
    for(i=0;i<numPointers;i++)
    {
        if(d[i]) free(d[i]);
    }
    if(d) free(d);
}

Called like this: 这样称呼:

freeDummies(dummies, 4); 

dummies was never assigned a value, so de-referencing will attempt to reach some random memory which is almost certainly not going to be part of your program's allocated memory. dummies从未被赋予一个值,因此取消引用会尝试到达一些随机内存,这几乎肯定不会成为程序分配内存的一部分。 You should have assigned it to &d. 你应该把它分配给&d。

But you don't even need to do that. 但你甚至不需要这样做。 Just use &d once when you call the function. 只需在调用该函数时使用&d一次。

Also, if you return the number of dummies allocated instead of 1/0, you can simplify your code. 此外,如果返回分配的假人数而不是1/0,则可以简化代码。 Something like the below (not tested): 像下面的东西(未测试):

#include <stdio.h>

int mock_read_from_external_source() {
    return 10;
}

typedef struct Dummy {
    int dummy_number;
} Dummy;

int load_dummies(Dummy** dummies) {
    int want, i = 0;
    if((want = mock_read_from_external_source()) > 0) {
        *dummies = (Dummy*) calloc(want, sizeof(Dummy));
        if(*dummies) {
            // Iterate dummies and assign their values...

            for (i = 0; i < want; i++) {
                (*dummies)[i].dummy_number = i;
            }
        }
    }
    return i;
}

int main() {
    Dummy* d = NULL;
    int num_of_dummies = load_dummies(&d); // when &d is de-referenced, changes are reflected in d

    if(num_of_dummies > 0) {
        for (int i = 0; i < num_of_dummies; i++) {
            printf("Dummy number: %d\n", d[i].dummy_number);
        }
    }

    if(d) { // clean up
        free(d);
    }

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM