段错误(核心已转储)——那些易被忽略的细节

段错误(核心已转储)——那些易被忽略的细节

  段错误,就是一旦一个程序发生了越界访问,cpu 就会产生相应的保护,于是 segmentation fault 就出现了,通过上面的解释,段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或者文件损坏。更多关于段错误的介绍可参照C语言再学习 – 段错误(核心已转储)。
  对于段错误,常伴随着“free():invaild pointer”或者"double free or corruption"等,目前我遇到过的错误便是上面这些,欢迎大家在评论区进行指教。一般就是对于内存的重复释放或者越界访问,对于这种简单易见的错误解决,可参照我的上篇博客Linux C 内存管理-实例分析。接下来要说的是那些容易被忽略的细节:

1、函数中隐秘的地址赋值:
  即,在将指针变量作为参数调用某函数时,要注意该函数是否改变了我们传递进去的指针变量所指向的地址,相信看到这大家也会有种恍然大悟的感觉,是的,如果在函数中改变了指针变量所指向的地址,那么我们主动释放的内存便不再是自己所申请的了,此时如果改变后的地址指向了一个局部变量或者其它生命周期非全文件的变量,其地址会有系统自行进行回收,而我们后续的主动释放,自然就引起了“free():invaild pointer”或者"double free or corruption",如我下面这段代码,利用asn1c所解析产生的编码接口进行V2X消息填充并编码:

	BasicSafetyMessage_t *bsm;
    MessageFrame_t *msgFrame = (MessageFrame_t *)malloc(sizeof(MessageFrame_t));    
    memset(msgFrame, 0, sizeof(MessageFrame_t));
    #ifdef DEBUG
    printf("memset MessageFrame_t Success\n");
    #endif
    
    msgFrame->present = MessageFrame_PR_bsmFrame;
    bsm = &msgFrame->choice.bsmFrame;
//===========  对填充数据进行UPER编码并返回编码后的字符串  ==================
    unsigned char *chbuf = (unsigned char *)malloc(260);    
    memset(chbuf, 0, 260);
    //unsigned char *chbuf;
    
    int encodeSize = uper_encode_to_new_buffer(&asn_DEF_MessageFrame, 0,msgFrame, (void**)&chbuf);
    if (encodeSize == -1)
    {
        printf("Could not encode bsmFrame \n");
    }
    if (msgFrame != NULL)
    {
         free(msgFrame);
         #ifdef DEBUG
         printf("free(msgFrame) Success\n");
         #endif
         msgFrame = NULL;
    }
    if (chbuf != NULL)
    {
        free(chbuf);
        #ifdef DEBUG
        printf("free(chbuf) Success\n");
        #endif
        chbuf = NULL;
    }

  乍一看,确实没什么毛病,主动申请的内存在使用过后均进行了主动释放,而且刚运行时也很正常,但是在运行一段时间后,就会报"free() invalid pointer"的错误(偶发,也有可能是跑的时间不够长,系统还没发现错误的内存操作),一开始百思不得解,后来在所有进行内存操作的代码后添加DEBUG输出,终于命中了问题所在——产生错误的时候均是在要调用UPER编码的时候。从uper_encode_to_new_buffer()函数接口中能够看出,传入了两个我们自己申请了地址的指针变量,于是大胆猜测,此函数不仅仅是把msgFrame的值编码后赋给了chbuf,而是首先将msgFrame的数据进行编码而后将地址赋给了chbuf,于是将后面的主动释放改为仅释放其中一个的地址,果然,跑起来没问题了,所以对于chbuf,其实没必要去为其申请地址空间,因为申请了却没有被释放,所以最终代码变成了这样:

	BasicSafetyMessage_t *bsm;
    MessageFrame_t *msgFrame = (MessageFrame_t *)malloc(sizeof(MessageFrame_t));    
    memset(msgFrame, 0, sizeof(MessageFrame_t));
    #ifdef DEBUG
    printf("memset MessageFrame_t Success\n");
    #endif
    
    msgFrame->present = MessageFrame_PR_bsmFrame;
    bsm = &msgFrame->choice.bsmFrame;
//===========  对填充数据进行UPER编码并返回编码后的字符串  ==================
    unsigned char *chbuf;
    
    int encodeSize = uper_encode_to_new_buffer(&asn_DEF_MessageFrame, 0,msgFrame, (void**)&chbuf);
    if (encodeSize == -1)
    {
        printf("Could not encode bsmFrame \n");
    }
    if (msgFrame != NULL)
    {
         free(msgFrame);
         #ifdef DEBUG
         printf("free(msgFrame) Success\n");
         #endif
         msgFrame = NULL;
    }

  这仅仅是我目前所发现的会对指针变量参数作隐式地址改变的一个函数,即uper_encode_to_new_buffer(),欢迎大家在评论区进行评论丰富,感激不尽!
(该博客后续将继续丰富有关那些隐秘的错误内存管理)

你可能感兴趣的:(C/C++,嵌入式Linux,内存管理,c语言)