C语言结构体成员值异常:调用无关函数后pin值遭破坏
问题:调用
parse_string后结构体中pin数组值被异常覆盖 本人刚接触C语言,自认对部分知识有所了解,但对细节掌握不足。以下是从复杂程序中提取的最小可复现示例(MWE),代码可正常编译运行,但测试发现:ev->pin初始值正确,调用parse_string函数后值却异常,疑似内存被覆盖,但无法定位原因。
复现代码
#include <string.h> #include <stdio.h> #include <stdlib.h> #define SCAN_EVENT_COUNT 5 #define PIN_ASSIGNMENTS {7, -1, 1, 13, 4} #define EVENT_NAMES "first-second-third-fourth-fifth" void init_scan_events(); int parse_string(char pInputString[], char *Delimiter, char *pToken[]); struct scan_events { char event_names[31]; char *pEvent[SCAN_EVENT_COUNT]; int pin[SCAN_EVENT_COUNT]; }; int main(void) { init_scan_events(); return (0); } /** * Note that `strtok` expects a `char * string` and `char * delimiter` as arguments. **/ int parse_string(char pInputString[], char *Delimiter, char *pToken[]) { int i = 0; pToken[i] = strtok(pInputString, Delimiter); // get first token i++; while ((pToken[i] = strtok(NULL, Delimiter)) != NULL) // get the rest of the tokens { i++; } return i; } void init_scan_events() { char en[] = EVENT_NAMES; int en_size = strlen(en); int size = SCAN_EVENT_COUNT; int pin[] = PIN_ASSIGNMENTS; int token_count = 0; char *Delim = "-"; int i; struct scan_events *ev = malloc(sizeof(struct scan_events)); if (ev == NULL) { printf("Allocation failed\n"); } for (int i = 0; i < en_size; i++) // copy EVENT_NAMES { ev->event_names[i] = en[i]; } for (int i = 0; i < size; i++) // copy PIN_ASSIGNMENTS { ev->pin[i] = pin[i]; } for (int i = 0; i < size; i++) { printf("% d ", ev->pin[i]); // inspect pin values: correct! } printf("\n"); token_count = parse_string(ev->event_names, Delim, ev->pEvent); // tokenize EVENT_NAMES for (int i = 0; i < size; i++) { printf("% d ", ev->pin[i]); // check pin values: corrupted! } printf("\n"); free(ev); }
问题原因
核心问题是**ev->event_names字符串未以空字符\0结尾**,导致strtok越界破坏内存:
- 循环复制
en内容时,只复制了strlen(en)个字符,漏掉了字符串必须的结束符\0 strtok会持续扫描直到遇到\0,由于event_names没有结束符,它会继续访问结构体中event_names之后的内存(即pEvent数组和pin数组的空间),在将分隔符替换为\0的过程中,直接改写了pin数组的内容,造成值异常
修复方案
方案1:补全字符串结束符
修改复制event_names的循环,把结束符也复制进去:
for (int i = 0; i <= en_size; i++) // 用<=包含最后的'\0' { ev->event_names[i] = en[i]; }
或者更简洁地用strcpy(event_names数组大小足够容纳目标字符串):
strcpy(ev->event_names, en);
方案2:用calloc初始化内存
用calloc代替malloc分配内存,它会自动将所有内存初始化为0,这样即使忘记复制结束符,event_names末尾也会有默认的\0:
struct scan_events *ev = calloc(1, sizeof(struct scan_events));
额外优化:限制token数量避免越界
当前parse_string没有限制最大token数,如果输入字符串分割出的token超过SCAN_EVENT_COUNT,会导致pEvent数组越界。可以给函数添加最大token数参数:
int parse_string(char pInputString[], char *Delimiter, char *pToken[], int max_tokens) { int i = 0; if (max_tokens == 0) return 0; pToken[i] = strtok(pInputString, Delimiter); if (pToken[i] == NULL) return 0; i++; while (i < max_tokens && (pToken[i] = strtok(NULL, Delimiter)) != NULL) { i++; } return i; }
调用时传入SCAN_EVENT_COUNT:
token_count = parse_string(ev->event_names, Delim, ev->pEvent, SCAN_EVENT_COUNT);
内容的提问来源于stack exchange,提问作者Bryan Hanson
相关产品推荐
相关产品推荐

