【问题标题】:Segfault with memory reallocation structure and linked list具有内存重新分配结构和链表的 Segfault
【发布时间】:2013-09-13 10:27:45
【问题描述】:

我是 C 的新手,我正在从事一个项目,我一直在努力努力工作好几天,但我无法弄清楚问题出在哪里。

基本上,应用程序应该做的是从数据库中读取数据并将值插入到结构中。有些行是相互关联的,因此在结构中是一个链接列表,它将包含数据库中的其他值。

我不知道要从数据库中取出多少条记录,所以我最初将结构分配为 100 个项目,并且我跟踪一个索引,如果它达到 100,它会将结构重新分配到添加另外 100 个项目。重新分配位似乎有效,但这就是为什么我尝试将一些东西插入到结构中的链表中,我得到一个段错误。

以下是主要结构的定义。

typedef struct CallLogSearchDataStruct
{
    char * date;
    char * time;
    char * bParty;
    char * aParty;
    float duration;
    char * cleardownCause;
    struct CallLogSearchOutboundStruct * outboundLegs;
} callLogSearchDataStruct;

下面是链表(CallLogSearchOutb​​oundStruct)的代码

typedef struct CallLogSearchOutboundStruct
{
    char * target;
    float duration;
    char * cleardownCause;
    struct CallLogSearchOutboundStruct * nextLeg;
} callLogSearchOutboundStruct;

我使用以下代码初始化结构

callLogSearchData = calloc(INITIAL_CALL_STRUCT_SIZE,sizeof(callLogSearchDataStruct));
callLogSearch = calloc(INITIAL_CALL_STRUCT_SIZE,sizeof(callLogSearchResultStruct));
switches = calloc(INITIAL_CALL_STRUCT_SIZE, sizeof(switchIDStructure));

每次循环数据时,我使用以下代码在结构中分配 outboundLeg 链表

if (dataRow > -1 && callLogSearchData[dataRow].outboundLegs == NULL)
                {
                    //Initialise the outbound struct
                    callLogSearchData[dataRow].outboundLegs = malloc(sizeof(callLogSearchOutboundStruct));
                    callLogSearchData[dataRow].outboundLegs->cleardownCause = NULL;
                    callLogSearchData[dataRow].outboundLegs->duration = 0;
                    callLogSearchData[dataRow].outboundLegs->target = NULL;
                    callLogSearchData[dataRow].outboundLegs->nextLeg = NULL;
                }
            outboundCallLegStartPtr = callLogSearchData[dataRow].outboundLegs;
            outboundCallLegStartPtr->nextLeg = NULL;

下面是调用函数将数据插入到结构中的链表中的代码

insertOutboundLegToList(outboundCallLegStartPtr, targetBuffer, durationBuffer, atoi(rowReport[cleardownColIndex]), debugFile);

下面是实际插入函数的代码

void insertOutboundLegToList(callLogSearchOutboundStruct * outboundLeg, char * target, float duration, int cleardown, FILE * debugFile)
{
    //fprintf(debugFile, "INSIDE INSERT OUTBOUND LEG FUNCTION\n");
    if (outboundLeg->target == NULL)
    {
        outboundLeg->target = strdup(target);
        outboundLeg->duration = duration;
        outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
        //fprintf(debugFile, "Outbound target is: %s\n", outboundLeg->target);
    }
    else
    {
        while (outboundLeg->nextLeg != NULL)
        {
            outboundLeg = outboundLeg->nextLeg;
        }
        outboundLeg->nextLeg = (callLogSearchOutboundStruct*)malloc(sizeof(callLogSearchOutboundStruct));
        outboundLeg = outboundLeg->nextLeg;
        outboundLeg->target = strdup(target);
        outboundLeg->duration = duration;
        outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
        //fprintf(debugFile, "Outbound target is: %s\n", outboundLeg->target);
        outboundLeg->nextLeg = NULL;
    }


}

下面是我如何调用函数来检查结构是否需要重新分配

if (reallocateStructures(&callLogSearch, &callLogSearchData, &switches, &timesStructHasBeenReallocated, currentStructIndexValue, dataRow) == 0)
                    {
                        //Structures have been reallocated so reset the index
                        currentStructIndexValue = -1;
                    }

下面是结构重新分配的实际功能

int reallocateStructures(callLogSearchResultStruct **callLogSearch, callLogSearchDataStruct ** callLogSearchData, 
        switchIDStructure ** switches, int *timesStructHasBeenReallocated, int currentStructIndexValue,
        int dataRow)
{
    int INITIAL_CALL_STRUCT_SIZE = 100;
    int currentSize = 0;
    int newSize = 0;
    int initFromIndex = 0;
    callLogSearchResultStruct * callLogSearchTemp;
    callLogSearchDataStruct * callLogSearchDataTemp;
    switchIDStructure * switchesTemp;


    printf("Current Struct Index Value: %i\n", currentStructIndexValue);

    if (currentStructIndexValue >= INITIAL_CALL_STRUCT_SIZE) {
        printf("REALLOCATING STRUCTURES");
        currentSize = currentStructIndexValue * *timesStructHasBeenReallocated;

        newSize = currentSize + INITIAL_CALL_STRUCT_SIZE;
        *timesStructHasBeenReallocated = *timesStructHasBeenReallocated + 1;


        callLogSearchTemp = (callLogSearchResultStruct*)realloc(*callLogSearch, (newSize * sizeof(callLogSearchResultStruct)));
        callLogSearchDataTemp = (callLogSearchDataStruct*)realloc(*callLogSearchData, (newSize * sizeof(callLogSearchDataStruct)));
        switchesTemp = (switchIDStructure*)realloc(*switches, (newSize * sizeof(switchIDStructure)));


        /**callLogSearchData = realloc(*callLogSearchData, newSize * sizeof (callLogSearchDataStruct));
        *callLogSearch = realloc(*callLogSearch, newSize * sizeof (callLogSearchResultStruct));
        *switches = realloc(*switches, newSize * sizeof (switchIDStructure));
        */
        for (initFromIndex = currentSize; initFromIndex < newSize; initFromIndex++) {
            callLogSearchDataTemp[initFromIndex].aParty = NULL;
            callLogSearchDataTemp[initFromIndex].bParty = NULL;
            callLogSearchDataTemp[initFromIndex].cleardownCause = NULL;
            callLogSearchDataTemp[initFromIndex].date = NULL;
            callLogSearchDataTemp[initFromIndex].duration = 0;
            callLogSearchDataTemp[initFromIndex].outboundLegs = NULL;
            callLogSearchDataTemp[initFromIndex].time = NULL;

            callLogSearchTemp[initFromIndex].date = NULL;
            callLogSearchTemp[initFromIndex].dRowIndex = dataRow;

            switchesTemp[initFromIndex].switchID = NULL;
        }

        *callLogSearch = callLogSearchTemp;
        *callLogSearchData = callLogSearchDataTemp;
        *switches = switchesTemp;
        return 0;
    }
    else
    {
        return 1;
    }
}

我尝试通过 Valgrind 运行我的程序,但我不完全确定所有消息的含义,并且使用 Google 搜索,虽然我找到了一些东西,但并没有帮助我理解问题。

来自 Valgrind 的消息是

==9152== Invalid read of size 4
==9152==    at 0x80544EB: GenerateCallLog (performreport.c:3112)
==9152==    by 0x804AA49: ProcessReport (performreport.c:344)
==9152==    by 0x8056C7E: PerformReportCheck (reportcheck.c:72)
==9152==    by 0x80494D0: main (main.c:82)
==9152==  Address 0x47b22b0 is not stack'd, malloc'd or (recently) free'd
==9152==
==9152== Invalid read of size 4
==9152==    at 0x80545CF: GenerateCallLog (performreport.c:3123)
==9152==    by 0x804AA49: ProcessReport (performreport.c:344)
==9152==    by 0x8056C7E: PerformReportCheck (reportcheck.c:72)
==9152==    by 0x80494D0: main (main.c:82)
==9152==  Address 0x47b22b0 is not stack'd, malloc'd or (recently) free'd
==9152==
==9152== Invalid write of size 4
==9152==    at 0x80545DF: GenerateCallLog (performreport.c:3124)
==9152==    by 0x804AA49: ProcessReport (performreport.c:344)
==9152==    by 0x8056C7E: PerformReportCheck (reportcheck.c:72)
==9152==    by 0x80494D0: main (main.c:82)
==9152==  Address 0x35c is not stack'd, malloc'd or (recently) free'd

行 performreport.c:3112 是 if (dataRow &gt; -1 &amp;&amp; callLogSearchData[dataRow].outboundLegs == NULL)

performreport.c:3123 的行是 outboundCallLegStartPtr = callLogSearchData[dataRow].outboundLegs;

performreport.c:3124 的行是outboundCallLegStartPtr-&gt;nextLeg = NULL;,它的这一行也是在重新分配发生后导致核心转储

请求的setCallResult函数如下:

char * setCallResult(int callResult)
{
    if (callResult == 1)
    {
        return "Answered";
    }
    else if (callResult != 3)
    {
        return "Unanswered";
    }
    else if (callResult == 3)
    {
        return "Engaged";
    }
    else
    {
        return "N/A";
    }
}

对不起,对于所有代码,但我不知道问题可能出在哪里。在核心转储中看起来好像有什么东西在破坏内存,因为当我检查结构中的一些值时,它只是乱码。

感谢您提供的任何帮助。

【问题讨论】:

  • 这意味着您正在读取 performreport.c:3480 处的未初始化变量。如果您正在读取一个指针,然后通过该指针访问数据,那么您就输了。根据 sn-p,我不知道那条线在哪里。只有你可以告诉我们。但是尝试阅读 valgrind 的输出,真的没那么难。
  • 抱歉,由于我所做的所有更改,我错误地取出了一些东西,所以它没有初始化它。问题似乎是在结构上完成了 realloc 之后,当它在重新分配后执行 outboundCallLegStartPtr-&gt;nextLeg = NULL 时,它会崩溃,如果我尝试打印它,它会说它越界,所以 realloc 中的某些东西搞砸了记忆,但我看不到什么
  • 你能在insertOutboundLegToList函数中清楚地标出performreport.c:3480行吗?
  • @us2012 由于以前的 cmets,该消息现在已更改。我已经用来自 Valgrind 的新消息更新了问题,但是发生了同样的错误
  • @Boardy 我没有看到任何其他明显的问题,但我觉得要解决剩余的问题,您需要有人检查整个代码。例如,if 行中的无效读取可能是由您的问题中未显示的某些内容引起的。我还建议彻底学习一些valgrind/gdb 教程,如果您继续使用 C,花在这上面的时间将在以后获得十倍的回报。

标签: c linked-list malloc structure valgrind


【解决方案1】:

我可以看到一个潜在的问题:

insertOutboundLegToList 中,您似乎将第一个条件视为“有一个已分配的结构,但它是空的,尚未填充”。然后你填写它,但你忘记了NULLing nextLeg。所以假设nextLeg0x75732d42,因为那是仍然存在的数据;下次调用该函数时,您会触发else 条件:

while (outboundLeg->nextLeg != NULL)
        {
            outboundLeg = outboundLeg->nextLeg;
        }

现在nextLeg 不是NULL,即使它应该是。一次迭代后,outboundLeg 指向0x75732d42,即使那里没有分配结构。在下一次迭代中,您会因为尝试从无效的内存地址读取而崩溃。


我的建议是(insertOutboundLegToList):

if (outboundLeg->target == NULL)
{
    outboundLeg->target = strdup(target);
    outboundLeg->duration = duration;
    outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
    // HERE v HERE v HERE v HERE
    outboundLeg->nextLeg = NULL;
    ...
}

还有,realloc 函数中的这一行

*callLogSearchData = callLogSearchData;

可能缺少Temp

【讨论】:

  • 抱歉,我不完全确定您的意思。我应该在哪里NULLing outboundLeg->nextLeg
  • 嗯是的,这是有道理的。我已经这样做了,但仍然遇到了原来的问题
  • @Boardy valgrind/gdb 中的行仍然相同?请在您的源代码中标记调试工具给您的行。
  • @Boardy 更新了另一个提示。
  • 谢谢,不知道我看了多少次这个函数却从来没有注意到。 Valgrind 仍然有原始错误,但之前还有另一个错误,所以我更新了我的问题。尽管它的核心转储位置似乎没有受到影响
猜你喜欢
  • 1970-01-01
  • 2013-05-06
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多