【问题标题】:Segmentation fault on double pointer dereference双指针取消引用的分段错误
【发布时间】:2017-05-01 19:26:32
【问题描述】:

以下代码在没有声明d = *dummy; 的情况下工作正常,这是一个双指针取消引用。但是,如果存在此行,则会发生分段错误。为什么会这样?

代码为数据结构动态分配和初始化内存。我试图简化对返回的指针的访问。

#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);
    }
}

提前致谢。

【问题讨论】:

  • 您的调试器会很乐意告诉您程序崩溃的位置。
  • main 应该返回 int,而不是 void
  • load_dummies(dummies, p_num_of_dummies) 具有未定义的行为:它从 dummies 读取,这是一个未初始化的变量。
  • Dummy** dummies; ... err = load_dummies(dummies, p_num_of_dummies);-> Dummy* dummies; ... err = load_dummies(&amp;dummies, &amp;num_of_dummies);。不需要p_num_of_dummies
  • if (!dummies) --> if (!*dummies)

标签: c pointers malloc dynamic-allocation


【解决方案1】:

由于UB,您遇到了错误,部分原因是尝试使用没有内存的变量对象。 dummies,虽然创建为 Dummies **,但从未提供过内存。至少,您的编译器应该警告您 dummies 未在此调用中初始化:

err = load_dummies(dummies, p_num_of_dummies);

这很容易解决,只需在创建变量时对其进行初始化:

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

然后是运行时错误。第一个在我的系统上被称为致命的运行时,这意味着操作系统由于一个严重的问题而拒绝继续,在这种情况下,尝试取消引用这一行中的空指针:

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

因为您创建了一个名为dummiesDummy **,所以第一步是为指向dummies 的指针创建内存,然后为将产生的dummies[i] 的几个实例创建内存。只有这样才能写入其中任何一个的成员。

这是一种说明如何为指向指针的Dummies 指针(d)和几个Dummies 实例(d[i])创建内存的方法:

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;
 }

在你的主函数中,顺便说一句,它实际上应该至少被原型化为:int main(void){...},这个版本的 loadDummies 可以这样调用:

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

使用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);
}

这样称呼:

freeDummies(dummies, 4); 

【讨论】:

  • @fhpriamo - 很抱歉,我发布的解决方案存在一些错误。我已经编辑并测试了函数Dummy ** loadDummies(int numPointers, int numDummiesPerPointer),它现在可以工作了。请尝试这个版本,折腾之前的版本。
【解决方案2】:

dummies 从未分配过值,因此取消引用将尝试访问一些随机内存,这些内存几乎肯定不会成为程序分配内存的一部分。你应该把它分配给 &d。

但您甚至不需要这样做。调用函数时只需使用一次&amp;d

此外,如果您返回分配的虚拟对象数而不是 1/0,则可以简化代码。类似于以下内容(未测试):

#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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多