C程序参数解析中变量莫名被修改的问题排查求助
变量莫名被修改的C程序问题排查与解决
问题现象:传入参数对应的变量在未被代码显式修改时发生了变更。
初始执行命令:
anonymous DELAI_ANONYME=O sanspm=O
主函数代码:
int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; SQL100 qstrRefIndiv = {0}; int qiCheckIndiv = 0; SQL20 qstrTrigStatus = {0}; EXEC SQL END DECLARE SECTION; char strLogName[100] = {0}; char strRef[100] = {0}; int iNoCommit = 0; int i = 0; int iDelCase=0; char strDelai[1]; char strSanspm[1]; strcpy(strDelai, "N"); strcpy(strSanspm, "N"); char selectcof[1000]= {0}; initscr(); bkg_init(); for(i=1; i<argc; i++) { refresh(); db_log_message (LL_INFO, "Iter Nbr= [%d]\n", i); if(strcmp(argv[i],"NOCOMMIT")==0) { iNoCommit = 1; } if(strncmp(argv[i], "ref=", 4)==0) { strcpy(strRef,&argv[i][4]); strcpy (qstrRefIndiv, strRef); db_log_message (LL_INFO, "ref param= [%s]\n", qstrRefIndiv); } if(strncmp(argv[i], "DELAI_ANONYME=", 14)==0) { strcpy(strDelai,&argv[i][14]); db_log_message (LL_INFO, "ref Delai= [%s]\n", strDelai); } if(strncmp(argv[i], "delcase=", 8)==0) { iDelCase=atoi(&argv[i][8]); db_log_message (LL_INFO, "ref iDelCase= [%s]\n", iDelCase); } if(strncmp(argv[i], "sanspm=", 7)==0) { db_log_message (LL_INFO, "[sanspm] before ref Delai= [%s]\n", strDelai); strcpy(strSanspm,&argv[i][7]); db_log_message (LL_INFO, "ref strSanspm= [%s]\n", strSanspm); db_log_message (LL_INFO, "[sanspm] after ref Delai= [%s]\n", strDelai); } } db_log_message (LL_INFO, "ref Delai 1= [%s]\n", strDelai); db_log_message (LL_INFO, "ref strSanspm 1= [%s]\n", strSanspm); ... endwin(); return(0); }
第一次执行日志:
[INFO ] [ 0.017] Iter Nbr= [1] [INFO ] [ 0.000] ref Delai= [O] [INFO ] [ 0.000] Iter Nbr= [2] [INFO ] [ 0.000] [sanspm] before ref Delai= [O] [INFO ] [ 0.000] ref strSanspm= [O] [INFO ] [ 0.000] [sanspm] after ref Delai= [] [INFO ] [ 0.000] ref Delai 1= [] [INFO ] [ 0.000] ref strSanspm 1= [O]
调整参数顺序后的执行命令:
anonymous sanspm=O DELAI_ANONYME=O
调整参数后的执行日志:
[INFO ] [ 0.001] Iter Nbr= [1] [INFO ] [ 0.000] [sanspm] before ref Delai= [] [INFO ] [ 0.000] ref strSanspm= [O] [INFO ] [ 0.000] [sanspm] after ref Delai= [] [INFO ] [ 0.000] Iter Nbr= [2] [INFO ] [ 0.000] ref Delai= [O] [INFO ] [ 0.000] ref Delai 1= [O] [INFO ] [ 0.000] ref strSanspm 1= [OO]
原因分析
- 数组越界与字符串结束符问题:C语言中
strcpy会自动复制字符串的结束符'\0',而代码中strDelai和strSanspm仅被定义为char [1],只能容纳1个字符。执行strcpy(strDelai, "N");时,"N"实际是包含'\0'的两个字符,strcpy会把这两个字符都写入,直接导致内存越界;复制"O"时同理。 - 栈内存覆盖:这两个变量在栈上连续存储,越界写入的内容会直接覆盖相邻变量的内存区域。第一次执行时,
strSanspm的越界写入覆盖了strDelai的内存;调整参数顺序后,strDelai的越界写入又覆盖了strSanspm的内存,从而出现变量值异常变更的现象。
解决方案
方案1:调整数组大小
将strDelai和strSanspm的数组大小改为至少2,以容纳单个字符加上字符串结束符:
char strDelai[2] = {0}; char strSanspm[2] = {0};
方案2:使用安全的字符串复制函数
用strncpy代替strcpy,明确指定复制长度,并手动添加结束符,彻底避免越界:
// 初始化时 strncpy(strDelai, "N", sizeof(strDelai)-1); strDelai[sizeof(strDelai)-1] = '\0'; strncpy(strSanspm, "N", sizeof(strSanspm)-1); strSanspm[sizeof(strSanspm)-1] = '\0'; // 处理参数时 strncpy(strDelai, &argv[i][14], sizeof(strDelai)-1); strDelai[sizeof(strDelai)-1] = '\0'; strncpy(strSanspm, &argv[i][7], sizeof(strSanspm)-1); strSanspm[sizeof(strSanspm)-1] = '\0';
方案3:直接使用字符变量
如果仅需要存储单个字符,无需用字符串数组,直接定义为char类型:
char strDelai = 'N'; char strSanspm = 'N'; // 处理参数时直接赋值 strDelai = argv[i][14]; strSanspm = argv[i][7];
内容的提问来源于stack exchange,提问作者DrChi
相关产品推荐
相关产品推荐

