strtok指针异常致5x5矩阵解密程序输出异常的问题排查
问题分析
原代码的核心问题在于错误使用strtok,以及输入验证逻辑不严谨:
- 连续两次调用
strtok(str, "-")会重置tokenizer的内部状态,导致变量h只能获取到第一个坐标;且第一个循环已经遍历完所有token,第二个循环调用strtok(NULL, "-")直接返回NULL,因此仅输出第一个字符。 - 输入检查仅验证坐标的第一个字符是否为字母,无法检测类似
1a这样的混合非法输入;同时未确保坐标是严格的两位数格式。 - 使用
gets读取输入存在缓冲区溢出风险,属于不安全函数。
修复后的代码
#include <stdio.h> #include <string.h> #include <ctype.h> int main(void) { // 构建5×5字母数组(排除j) char arr[5][5]; char k = 'a'; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { arr[i][j] = k; k++; if (k == 'j') { k++; // 跳过j } } } char str[74]; // 使用fgets替代gets,避免缓冲区溢出 if (fgets(str, sizeof(str), stdin) == NULL) { printf("Input error"); return 1; } // 移除fgets读取的换行符 str[strcspn(str, "\n")] = '\0'; // 拆分坐标字符串 char *token = strtok(str, "-"); while (token != NULL) { // 验证坐标是否为两位数 if (strlen(token) != 2) { printf("Unable to decode"); return 0; } // 验证坐标每个字符都是数字 for (int i = 0; token[i] != '\0'; i++) { if (!isdigit(token[i])) { printf("Unable to decode"); return 0; } } // 解析行号和列号 int row = token[0] - '0'; // 直接转数字,避免atoi的额外开销 int col = token[1] - '0'; // 检查坐标是否越界 if (row < 0 || row > 4 || col < 0 || col > 4) { printf("Out of bounds"); return 0; } // 输出对应字母 printf("%c", arr[row][col]); // 获取下一个token token = strtok(NULL, "-"); } return 0; }
关键修复说明
- 合并Token遍历逻辑:将输入验证与字符输出合并到同一个循环中,避免重复调用
strtok导致的状态异常,确保每个坐标都被正确处理。 - 替换不安全的
gets:使用fgets读取输入,并通过strcspn移除末尾的换行符,彻底避免缓冲区溢出风险。 - 完善输入验证:
- 检查每个坐标字符串长度必须为2,确保符合两位数格式要求;
- 遍历坐标的每个字符,确保全部为数字,彻底拦截含字母或其他非法字符的输入。
- 优化坐标解析:直接通过字符转数字的方式获取行号和列号,比
atoi更高效,逻辑更清晰;同时明确判断行号和列号是否在0-4的合法范围内。
内容的提问来源于stack exchange,提问作者Christos Polimatidis
相关产品推荐
相关产品推荐

