现象:在某进程启动后, 触发某个操作该进程出现段错误,当添加打印调试时,段错误就不出现,当去掉打印时,段错误又继续出现。
可能原因:检查是否存在函数调用中参数传递时,传址的误写成了传值的操作,导致被调用函数将该传递的值作为指针地址进行操作,而该参数的值因为没有初始化恰巧默认值是某个指针的地址,导致指针被踩而异常。
下面是本人遇到这个问题的一个简单的描述:
一个时间封装函数,将gettimeofday(tv, NULL);修改为gettimeofday(&tv, NULL);可直接使用。
static struct timeStampTLV *TimestampTLV(void)
{
struct tm *gt = NULL;
struct timeval tv;
uint8_t timeStr[40] = {0};
struct timeStampTLV *tlv = (struct timeStampTLV *)memalloc(sizeof(struct timeStampTLV));
printf("\33[31m\33[1m%s:%d---->>&tv = %x \033[0m \n",__FUNCTION__,__LINE__,&tv);
printf("\33[31m\33[1m%s:%d---->>tv = %x \033[0m \n",__FUNCTION__,__LINE__,tv);
gettimeofday(tv, NULL);
gt = localtime(&tv.tv_sec);
printf("\33[31m\33[1m%s:%d---->>&tv = %x \033[0m \n",__FUNCTION__,__LINE__,&tv);
printf("\33[31m\33[1m%s:%d---->>tv = %x \033[0m \n",__FUNCTION__,__LINE__,tv);
sprintf(timeStr,"%04d-%02d-%02dT%02d:%02d:%02d.%d+Z%c%02d:%02d",
gt->tm_year + 1900,gt->tm_mon + 1,gt->tm_mday,
gt->tm_hour,gt->tm_min,gt->tm_sec,tv.tv_usec,
(gt->tm_gmtoff < 0? '-' : '+'),
gt->tm_gmtoff/3600,gt->tm_gmtoff%60);/*tm_gmtoff指定了日期变更线东面时区中UTC东部时区正秒数或UTC西部时区的负秒数*/
tlv->tlv.type = TLV_TYPE_TIMESTAMP;
tlv->timeStamp = strdup(timeStr);
tlv->timeStampLen = strlen(timeStr);
return tlv;
}
以上面函数为例,在LocalTimestampTLV函数中,调用了gettimeofday函数,第一个参数应该为&tv传址的方式,但是误用直接传值,直接导致将值作为指针地址去修改,导致出现内存被踩的情况。
更奇怪的现象如下
1、在调用LocalTimestampTLV函数之前添加打印时,就不会出现段错误,去掉打印就会出现段错误。
2、timeStr空间从40改为60也不会重新错误。
3、在struct timeval tv;定义前面定义一个无符号字符型初始化为0,也不会出现段错误。
段错误的原因:我再主调用函数中申请了一个二维指针,该二维指针用来存储包含时间戳(嵌套调用了LocalTimestampTLV,并通过strdup克隆数据)和其他数据的一个指针数组,在存储了3个数据以后,再次调用LocalTimestampTLV添加一个时间错TLV,进行数据类型打印的时候,就出现了段错误,因为第三个的指针被破坏了。
TimestampTLV:2329------&tv = 7ff1bbd8
TimestampTLV:2330------tv = 4d6540
ChannelScanResultTLV:2406-----tlv = 4d6290(赋值给channelScanResult[0])
TimestampTLV:2324------&tv = 7ff1bbd8
TimestampTLV:2325------tv = 4d6544
TimestampTLV:2329------&tv = 7ff1bbd8
TimestampTLV:2330------tv = 4d6544
ChannelScanResultTLV:2406-----tlv = 4d8f38 (赋值给channelScanResult[1])
TimestampTLV:2324------&tv = 7ff1bbd8
TimestampTLV:2325------tv = 4d6548
TimestampTLV:2329------&tv = 7ff1bbd8
TimestampTLV:2330------tv = 4d6548
ChannelScanResultTLV:2406-----tlv = 4d8f80 (赋值给channelScanResult[2])
TimestampTLV:2324------&tv = 7ff1c428
TimestampTLV:2325------tv = 4d6548
TimestampTLV:2329------&tv = 7ff1c428
TimestampTLV:2330------tv = 4d6548 (他的初始化为channelScanResult[2]的地址)
ChannelScanReport:7800-----channelScanResult = 4d6540
ChannelScanReport:7801-----&channelScanResult[2] = 4d6548 (channelScanResult[2]的地址)
ChannelScanReport:7802-----timestamp = 4d4220
ChannelScanReport:7806-----channelScanResult[0] = 4d6290
ChannelScanReport:7806-----channelScanResult[1] = 4d8f38
ChannelScanReport:7806-----channelScanResult[2] = 5ede6985 (已经被踩了)
ChannelScanReport:7806-----channelScanResult[3] = d59a
如上面打印,根本原因就是struct timeval tv结构体 tv没有初始化,但是存在默认给的值4d6548 ,刚好这个值是channelScanResult[2]的地址,所以tv作为参数传入以后(没有使用&tv),就会被转换为指针,然后修改它指向的内容,因此channelScanResult[2]指向的内容也别修改了,他已经指向了gettimeofday给tv装值的空间,而不是之前所存储的指向存储channelScanResult的指针了。(很奇怪为什么tv初始化的值一直都是当前channelScanResult存储指针的值,暂不一探究竟了)
疑惑分析:
1、为什么在调用TimestampTLV函数之前添加打印,不会出现段错误,通过打印看到是因为TimestampTLV函数中的tv的值默认不再是channelScanResult[2] 的地址了,所以不会再这里导致异常,但是肯定还是会破坏别的指针。
2、为什么timeStr空间从40改为60也不会出现段错误,当从40变成60字节的空间后,tv被初始化的默认值也变成了0,所以相当于传入的是空指针,也就不会导致段错误了。
3、为什么在前面加个i初始化,也不会出现段错误,因为初始化以后,tv被初始化的默认值也变成了0。
如下是在struct timeval tv前面添加一个char i = 0;后的地址打印和tv的初始值
TimestampTLV:2324---->>&tv = 7fb5cab4
TimestampTLV:2325---->>tv = 44b198
TimestampTLV:2326------i = 7fb5cab0
TimestampTLV:2329---->>&tv = 7fb5cab4
TimestampTLV:2330---->>tv = 44b198
ChannelScanResultTLV:2406-----tlv = 4d73a8
TimestampTLV:2324---->>&tv = 7fb5cab4
TimestampTLV:2325---->>tv = 44b198
TimestampTLV:2326------i = 7fb5cab0
TimestampTLV:2329---->>&tv = 7fb5cab4
TimestampTLV:2330---->>tv = 44b198
ChannelScanResultTLV:2406-----tlv = 4d7260
TimestampTLV:2324---->>&tv = 7fb5cab4
TimestampTLV:2325---->>tv = 44b198
TimestampTLV:2326------i = 7fb5cab0
TimestampTLV:2329---->>&tv = 7fb5cab4
TimestampTLV:2330---->>tv = 44b198
ChannelScanResultTLV:2406-----tlv = 4d6f88
TimestampTLV:2324---->>&tv = 7fb5d304
TimestampTLV:2325---->>tv = 0
TimestampTLV:2326------i = 7fb5d300
TimestampTLV:2329---->>&tv = 7fb5d304
TimestampTLV:2330---->>tv = 0
ChannelScanReport:7800---->>channelScanResult = 4d6140
ChannelScanReport:7801---->>channelScanResult[2] = 4d6148
ChannelScanReport:7802---->>timestamp = 4d6eb8
ChannelScanReport:7806---->>channelScanResult[0] = 4d73a8
ChannelScanReport:7806---->>channelScanResult[1] = 4d7260
ChannelScanReport:7806---->>channelScanResult[2] = 0
ChannelScanReport:7806---->>channelScanResult[3] = 0
ChannelScanReport:7806---->>channelScanResult[4] = 0
ChannelScanReport:7806---->>channelScanResult[5] = 0
所以出现段错误的原因就是:
1、tv没有初始化。(养成变量初始化的良好习惯)
2、struct timeval结构体应该传地址(&tv)的结果传入了值(tv)。(明确参数类型,结构体作为参数一般都是传址,很奇怪编译器为什么不报错,在linux下直接编译是会报错的,我找了编译工具的时候他报了警告,但是警告类型看不出来我传址异常)