【问题标题】:C: Segmentation fault: GDB: <error reading variable>C:分段错误:GDB:<错误读取变量>
【发布时间】:2016-01-12 12:18:53
【问题描述】:

我有一个函数 shortestPath(),它是 Dijkstra 算法的修改实现,用于我正在为我的 comp2 类工作的棋盘游戏 AI。我浏览了该网站并使用 gdb 和 valgrind 我确切地知道段错误发生的位置(实际上在几个小时前就知道了),但无法弄清楚是什么未定义的行为或逻辑错误导致了问题。

出现问题的函数被调用了大约 10 倍,并按预期工作,直到它与 GDB 发生段错误: “错误读取变量:无法访问内存” 和 valgrind: "大小为 8 的无效读取"

通常这就足够了,但我无法解决这个问题。任何一般性的建议和提示也很感激......谢谢!

GDB:https://gist.github.com/mckayryan/b8d1e9cdcc58dd1627ea
瓦尔格林:https://gist.github.com/mckayryan/8495963f6e62a51a734f

这里是发生段错误的函数:

static void processBuffer (GameView currentView, Link pQ, int *pQLen, 
                           LocationID *buffer, int bufferLen, Link prev,
                           LocationID cur)
{
    //printLinkIndex("prev", prev, NUM_MAP_LOCATIONS);
    // adds newly retrieved buffer Locations to queue adding link types 
    appendLocationsToQueue(currentView, pQ, pQLen, buffer, bufferLen, cur);
    // calculates distance of new locations and updates prev when needed
    updatePrev(currentView, pQ, pQLen, prev, cur);  <--- this line here 

    qsort((void *) pQ, *pQLen, sizeof(link), (compfn)cmpDist);
    // qsort sanity check
    int i, qsortErr = 0;
    for (i = 0; i < *pQLen-1; i++) 
        if (pQ[i].dist > pQ[i+1].dist) qsortErr = 1;
    if (qsortErr) {
        fprintf(stderr, "loadToPQ: qsort did not sort succesfully");
        abort();
    }  
}

以及调用它后一切都崩溃的函数:

static void appendLocationsToQueue (GameView currentView, Link pQ, 
                                   int *pQLen, LocationID *buffer, 
                                   int bufferLen, LocationID cur)
{
    int i, c, conns;
    TransportID type[MAX_TRANSPORT] = { NONE };     

    for (i = 0; i < bufferLen; i++) { 
        // get connection information (up to 3 possible)  
        conns = connections(currentView->gameMap, cur, buffer[i], type);
        for (c = 0; c < conns; c++) {
            pQ[*pQLen].loc = buffer[i];
            pQ[(*pQLen)++].type = type[c];            
        }            
    }
}

所以我认为一个指针已被覆盖到错误的地址,但在 GDB 中进行了大量打印后,情况似乎并非如此。我还轮流对相关变量进行读/写,以查看哪些触发了故障,它们都在 appendLocationsToQueue() 之后执行,但不是在之前(或在该函数结束时)。

以下是其余相关代码: 最短路径():

Link shortestPath (GameView currentView, LocationID from, LocationID to, PlayerID player, int road, int rail, int boat)
{
    if (!RAIL_MOVE) rail = 0;

    // index of locations that have been visited    
    int visited[NUM_MAP_LOCATIONS] = { 0 };

    // current shortest distance from the source
    // the previous node for current known shortest path
    Link prev;
    if(!(prev = malloc(NUM_MAP_LOCATIONS*sizeof(link))))
        fprintf(stderr, "GameView.c: shortestPath: malloc failure (prev)");

    int i;
    // intialise link data structure
    for (i = 0; i < NUM_MAP_LOCATIONS; i++) {
        prev[i].loc = NOWHERE;
        prev[i].type = NONE;
        if (i != from) prev[i].dist = INF; 
        else prev[i].dist = LAST; 
    }
    LocationID *buffer, cur;
    // a priority queue that dictates the order LocationID's are checked
    Link pQ;
    int bufferLen, pQLen = 0;
    if (!(pQ = malloc(MAX_QUEUE*sizeof(link))))
        fprintf(stderr, "GameView.c: shortestPath: malloc failure (pQ)");
    // load initial location into queue
    pQ[pQLen++].loc = from;

    while (!visited[to]) {
        // remove first item from queue into cur  
        shift(pQ, &pQLen, &cur);
        if (visited[cur]) continue;
        // freeing malloc from connectedLocations()
        if (cur != from) free(buffer); 
        // find all locations connected to   
        buffer = connectedLocations(currentView, &bufferLen, cur, 
                                    player, currentView->roundNum, road, 
                                    rail, boat); 
        // mark current node as visited
        visited[cur] = VISITED;
        // locations from buffer are used to update priority queue (pQ) 
        // and distance information in prev       
        processBuffer(currentView, pQ, &pQLen, buffer, bufferLen, prev,
                      cur);
    }
    free(buffer);
    free(pQ);
    return prev;
}

【问题讨论】:

  • 你给buffer分配内存了吗?
  • 不应该 sizeof(link)sizeof(Link) 吗? (link 到底是什么?)。
  • typedef struct _link { LocationID loc;运输ID类型;浮动距离; } 关联; typedef struct _link *Link;它是图 ADT 上两个节点之间的链接
  • 好的 - 所以Link 本质上是link * - 这是有道理的,但是当你进入现实世界时,我建议不要做这样的事情。
  • 你/或任何人能详细说明我为什么不应该取悦吗?

标签: c debugging segmentation-fault gdb valgrind


【解决方案1】:

您的所有参数在此行之前看起来都不错:

appendLocationsToQueue(currentView, pQ, pQLen, buffer, bufferLen, cur);

并在它告诉我您已踩到(将0x7fff00000000 写入)$rbp 寄存器后变得不可用(在没有优化的情况下构建时,所有局部变量和参数都相对于$rbp)。

您可以在调用 appendLocationsToQueue 之前和之后在 GDB 中使用 print $rbp 确认这一点($rbp 应该在给定函数中始终具有相同的值,但会发生变化)。

假设这是真的,只有几种可能发生这种情况,最有可能的方法是appendLocationsToQueue(或它调用的东西)中的堆栈缓冲区溢出。

您应该能够使用 Address Sanitizer (g++ -fsanitize=address ...) 相当容易地找到此错误。

在 GDB 中查找溢出也相当容易:进入appendLocationsToQueue,然后执行watch -l *(char**)$rbpcontinue。当您的代码覆盖 $rbp 保存位置时,应触发观察点。

【讨论】:

  • 这很奏效。我的操作假设是 valgrind 会提醒我导致问题的溢出类型,因此将其归因于看不见的东西。
  • @RyanMckay Valgrind 很少检查堆栈和全局变量。 Address Sanitizer 在这方面好多了
猜你喜欢
  • 2018-09-14
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多