C语言向二维数组插入Token时截断及段错误问题求助
字符串分割Token插入二维数组时的截断与段错误问题
问题描述
能正确从一行字符串分割出单个Token,但插入二维数组时会截断Token内容,同时存在NULL相关问题触发段错误。
原代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // strtok #define MAX_FILE_LENGTH 30 #define MAX_COURSE_LENGTH 30 #define MAX_LINE_LENGTH 1000 void trim(char* str) { int l = strlen(str); if (str[l - 1] == '\n') { str[l - 1] = 0; } } int main() { char filename[MAX_FILE_LENGTH]; char arr[MAX_COURSE_LENGTH][MAX_COURSE_LENGTH]; const char delim[] = " "; char* token; int course = 0; char c; FILE* fp; int N = 0; // number of lines in file printf("This program will read, from a file, a list of courses and their prerequisites and will print the list in which to take courses.\n"); printf("Enter filename: "); scanf("%s%c", filename, &c); fp = fopen(filename, "r"); if (fp == NULL) { printf("Could not open file %s. Exit\n", filename); printf("\nFailed to read from file. Program will terminate.\n"); return -1; } while (!feof(fp) && !ferror(fp)) { int i = 0; if (fgets(arr[N], MAX_LINE_LENGTH, fp)) { trim(arr[N]); printf("Full line: |%s|\n", arr[N]); token = strtok(arr[N], delim); arr[N][i] = *token; printf("N = %d, i = %d, token = %s arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]); while (token != NULL) { i++; token = strtok(NULL, " \n"); printf("token at arr[%d][%i]: %s value at arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]); arr[N][i] = *token; printf("N = %d, i = %d, token = %s arr[%d][%d]: %s\n", N, i, token, N, i, &arr[N][i]); } N++; } } fclose(fp); return 0; }
运行输出
Full line: |c100 c200| N = 0, i = 0, token = c100 arr[0][0]: c100 token at arr[0][1]: c200 value at arr[0][1]: 100 N = 0, i = 1, token = c200 arr[0][1]: c00 token at arr[0][2]: (null) value at arr[0][2]: 00 zsh: segmentation fault ./a.out
输入文件内容
c100 c200 c300 c200 c100 c200 c100
错误原因分析
- 数组类型不匹配:原代码定义的
char arr[MAX_COURSE_LENGTH][MAX_COURSE_LENGTH]每个元素仅能存储单个字符,但需要存储的是c100这类字符串,结构无法容纳完整内容导致截断。 - 字符串赋值错误:
arr[N][i] = *token;仅将Token的第一个字符赋值给数组元素,而非复制整个字符串,这是截断问题的核心。 - NULL指针解引用:当
strtok返回NULL(无更多Token)时,仍执行arr[N][i] = *token;,直接访问NULL指针触发段错误。 - 循环逻辑混乱:初始获取Token后直接赋值,随后先递增索引再获取Token,处理顺序错误,且未判断Token是否为NULL就操作。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // strtok, strcpy #define MAX_FILE_LENGTH 30 #define MAX_COURSE_COUNT 30 // 最大课程行数 #define MAX_COURSE_CODE_LEN 30 // 单个课程代码最大长度 #define MAX_LINE_LENGTH 1000 void trim(char* str) { size_t l = strlen(str); if (l > 0 && str[l - 1] == '\n') { str[l - 1] = '\0'; } } int main() { char filename[MAX_FILE_LENGTH]; // 三维数组:行存储课程行,列存储课程代码,每个代码是独立字符数组 char arr[MAX_COURSE_COUNT][MAX_COURSE_COUNT][MAX_COURSE_CODE_LEN]; const char delim[] = " "; char* token; char c; FILE* fp; int N = 0; // number of lines in file printf("This program will read, from a file, a list of courses and their prerequisites and will print the list in which to take courses.\n"); printf("Enter filename: "); scanf("%s%c", filename, &c); fp = fopen(filename, "r"); if (fp == NULL) { printf("Could not open file %s. Exit\n", filename); printf("\nFailed to read from file. Program will terminate.\n"); return -1; } char line_buf[MAX_LINE_LENGTH]; // 单独的行缓冲区,避免strtok修改存储数组 while (fgets(line_buf, MAX_LINE_LENGTH, fp) != NULL) { trim(line_buf); printf("Full line: |%s|\n", line_buf); int i = 0; token = strtok(line_buf, delim); while (token != NULL && i < MAX_COURSE_COUNT) { // 复制完整字符串到数组,确保不越界 strncpy(arr[N][i], token, MAX_COURSE_CODE_LEN - 1); arr[N][i][MAX_COURSE_CODE_LEN - 1] = '\0'; // 手动添加字符串终止符 printf("N = %d, i = %d, token = %s arr[%d][%d]: %s\n", N, i, token, N, i, arr[N][i]); i++; token = strtok(NULL, delim); } N++; if (N >= MAX_COURSE_COUNT) { printf("Reached maximum course line limit, stopping.\n"); break; } } fclose(fp); // 验证存储结果 printf("\nStored course data:\n"); for (int row = 0; row < N; row++) { printf("Row %d: ", row); for (int col = 0; col < MAX_COURSE_COUNT; col++) { if (arr[row][col][0] != '\0') { printf("%s ", arr[row][col]); } else { break; } } printf("\n"); } return 0; }
关键修改说明
- 调整数组结构:改用三维数组
arr[MAX_COURSE_COUNT][MAX_COURSE_COUNT][MAX_COURSE_CODE_LEN],每行可存储多个完整的课程代码字符串。 - 正确复制字符串:使用
strncpy复制整个Token内容,同时手动添加终止符,避免截断和非法字符串问题。 - 优化循环逻辑:先获取Token并判断非NULL后再处理,避免NULL指针解引用;使用独立缓冲区存储行内容,防止
strtok修改存储数组。 - 添加边界检查:限制行和列的索引范围,防止数组越界访问。
内容的提问来源于stack exchange,提问作者Januar Soepangat
相关产品推荐
相关产品推荐

