• 记一次难忘的排错debug经历(找了5天左右)(涉及内存覆盖)


    strcpy和memcpy都没有处理内存覆盖问题。

    函数描述
    The memcpy function copies count bytes of src to dest. If the source and destination overlap, this function does not ensure that the original source bytes in the overlapping region are copied before being overwritten. Use memmove to handle overlapping regions.
    memcpy函数将src的字节数复制到dest。如果源和目标重叠,这个函数不能确保重叠区域的原始源字节在被覆盖之前被复制。
    这里已经提到了内存覆盖的问题,而在C语言却并没有对这种现象做相关的规定或检查,也就是说对于这种现象C语言是缺省。后边会详细分析如何处理在字符串拷贝函数中内存重叠的问题。
    ---------------------  

    memcpy和memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下:


    void *memcpy(void *dst, const void *src, size_t count);
    void *memmove(void *dst, const void *src, size_t count);
    他们的作用是一样的,唯一的区别是,当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确。
    一、memcpy函数

    Memcpy原型:     

    void *memcpy(void *dest, const void *src, size_t n); 

    假如考虑dst和src内存重叠的情况,strcpy该怎么实现

    char s[10]="hello";

    strcpy(s, s+1); //应返回ello,

    //strcpy(s+1, s); //应返回hhello,但实际会报错,因为dst与src重叠了,把''覆盖了

    所谓重叠,就是src未处理的部分已经被dst给覆盖了,只有一种情况:src<=dst<=src+strlen(src)

    char *my_memcpy(char *dst, const char* src, int cnt)
    {
    assert(dst != NULL && src != NULL);

    char *ret = dst;

    if (dst >= src && dst <= src+cnt-1) //内存重叠,从高地址开始复制
    {
    dst = dst+cnt-1;
    src = src+cnt-1;
    while (cnt--)
    *dst-- = *src--;
    }
    else //正常情况,从低地址开始复制
    {
    while (cnt--)
    *dst++ = *src++;
    }

    return ret;
    }

     

    https://blog.csdn.net/Teemo_king/article/details/77655550

    https://www.cnblogs.com/17bdw/p/6133315.html

    https://blog.csdn.net/wh_0727/article/details/80294107

    遇到的bug:

    打印日志:

    结算成绩时可以看到有时候玩家下注的值

    m_lPlayBet[i][j]

    这个值,有时候为负数???

    按照道理来讲,玩家下注数不可能为负数,到底什么时候导致为负数的呢?

    另外还有一点规律:当出现异常为负数时 总是前面的玩家为负数。

    也就是 m_lPlayBet 索引靠前的玩家为负数。

    玩家下注m_lPlayBet 这个东西搜索所有代码修改的只有两个地方:

    class A{

    ...

    //下注数

    protected:
    LONGLONG m_lAllBet[AREA_MAX]; //总下注
    LONGLONG m_lPlayBet[GAME_PLAYER][AREA_MAX]; //玩家下注

    ...

    }

    一个是赋值为0,一个是下注 加上下注数。

    m_lPlayBet[wChairID][cbBetArea] += lBetScore; (lBetScore 是正数,不可能是负数)

    怎么m_lPlayBet这个值怎么为负数呢?

    查了几天发现在这个地方可疑:

    //游戏结束
    bool CTableFrameSink::OnEventGameConclude(WORD wChairID, IServerUserItem * pIServerUserItem, BYTE cbReason)
    {

    ...

    case GER_USER_LEAVE:        //用户离开
        {
            if (wChairID == m_wCurSuperRobBankerUser)
            {
                m_wCurSuperRobBankerUser = INVALID_CHAIR;
    
                CMD_S_CurSuperRobLeave CurSuperRobLeave;
                ZeroMemory(&CurSuperRobLeave, sizeof(CurSuperRobLeave));
    
                //设置变量
                CurSuperRobLeave.wCurSuperRobBankerUser = m_wCurSuperRobBankerUser;
    
                //发送消息
                m_pITableFrame->SendTableData(INVALID_CHAIR, SUB_S_CURSUPERROB_LEAVE, &CurSuperRobLeave, sizeof(CurSuperRobLeave));
                m_pITableFrame->SendLookonData(INVALID_CHAIR, SUB_S_CURSUPERROB_LEAVE, &CurSuperRobLeave, sizeof(CurSuperRobLeave));
            }
    
            //闲家判断
            if (m_wCurrentBanker != wChairID)
            {
                //变量定义
                LONGLONG lRevenue = 0;
    
                Debug("oneventgameconclude: cbReason %d", cbReason);
                Debug("oneventgameconclude: gameid:%d", pIServerUserItem->GetGameID());
    
                //写入积分
                if (m_pITableFrame->GetGameStatus() != GAME_SCENE_END)
                {
    for (WORD wAreaIndex = AREA_XIAN; wAreaIndex <= AREA_ZHUANG_DUI; ++wAreaIndex)
                    {
                        Debug("oneventgameconclude wAreaIndex:%d", wAreaIndex);
    
                        if (m_lPlayBet[wChairID][wAreaIndex] != 0)
                        {
                            CMD_S_PlaceBetFail PlaceBetFail;
                            ZeroMemory(&PlaceBetFail, sizeof(PlaceBetFail));
                            PlaceBetFail.lBetArea = (BYTE)wAreaIndex;
                            PlaceBetFail.lPlaceScore = m_lPlayBet[wChairID][wAreaIndex];
                            PlaceBetFail.wPlaceUser = wChairID;
    
                            //游戏玩家
                            for (WORD i = 0; i < GAME_PLAYER; ++i)
                            {
                                IServerUserItem *pIServerUserItem = m_pITableFrame->GetTableUserItem(i);
                                if (pIServerUserItem == NULL)
                                    continue;
    
                                m_pITableFrame->SendTableData(i, SUB_S_PLACE_JETTON_FAIL, &PlaceBetFail, sizeof(PlaceBetFail));
                            }
    
                            m_lAllBet[wAreaIndex] -= m_lPlayBet[wChairID][wAreaIndex];
                            m_lPlayBet[wChairID][wAreaIndex] = 0;
    
                            Debug("oneventgameconclude: m_lPlayBet[wChairID][wAreaIndex]:%d", m_lPlayBet[wChairID][wAreaIndex]);
                            Debug("oneventgameconclude: m_lPlayBet[wChairID][wAreaIndex] %d", m_lPlayBet[wChairID][wAreaIndex]);
    
                        }
                    }
                }
       else

    {
    ....
       }
          return true

    看这行:

        for (WORD wAreaIndex = AREA_XIAN; wAreaIndex <= AREA_ZHUANG_DUI; ++wAreaIndex)
    

     实际上 玩家的下注区域只有3个:从AREA_XIAN到 

    AREA_ZHUANG(值为3)
    AREA_ZHUANG_DUI 

    #define AREA_MAX 3 //最大区域

    这里for循环截止到7,导致访问

    m_lAllBet 和m_lPlayBet
    越界。
       m_lAllBet[wAreaIndex] -= m_lPlayBet[wChairID][wAreaIndex]; 


    m_lPlayBet[wChairID][wAreaIndex] = 0;

    protected:
    LONGLONG m_lAllBet[AREA_MAX]; //总下注
    LONGLONG m_lPlayBet[GAME_PLAYER][AREA_MAX]; //玩家下注

    ...

    这2个成员变量是相连的。

    m_lAllBet减去玩家的下注数,这个wAreaIndex越界,导致实际上修改的是

    m_lPlayBet。
    所以:
    m_lPlayBet为负数就不足为齐了。

    另外,这个也解释了之前发现异常时出现的规律: 当出现异常为负数时 总是前面的玩家为负数。


    总结:问题产生的原因就是访问变量越界导致修改的实际上是下一个变量。






     


  • 相关阅读:
    CSS布局口诀
    JAVA集合框架
    java接口作用及其好处
    java 输入输出类
    Java中精确计算的一个类BigDecimal
    java 集合框架
    java JSTL标签总结
    meta标签
    Java字符串使用总结
    Java集合框架使用方法
  • 原文地址:https://www.cnblogs.com/youxin/p/10654631.html
Copyright © 2020-2023  润新知