使用C结构体时strcat触发Segmentation Fault的问题排查
问题:无<stdio.h>依赖实现读取文件前10个单词时的段错误问题
本人是C语言和Unix新手,正尝试实现一个不依赖<stdio.h>库、仅通过系统调用打印文件前10个单词的函数。目前采用逐字符读取方式,希望将非空格字符拼接至结构体变量中,但调用strcat()时触发Segmentation Fault(段错误),询问是否需要以其他方式分配字符串/结构体。
相关代码如下:
struct word{ char *content; int length; }; void openRead(){ char curChar; //open file int infile = open("sample.txt", O_RDONLY); // set count int count = 10; //create array of words struct word words[count]; struct word firstWord; firstWord.length = 0; firstWord.content = "test\0"; words[0] = firstWord; int totalLen = lseek(infile, 0, SEEK_END); lseek(infile, 0, SEEK_SET); int i = 0; int curCount = 1; //-1 due to terminating character while (curCount < count && i < totalLen-1){ // read next character int p= lseek(infile, i, SEEK_SET); read(infile, &curChar, 1); int spaceCheck = isspace(curChar); // if regular character add to word if(spaceCheck == 0){ struct word curWord = words[curCount]; curWord.length = curWord.length + 1; // conver character to string for string concatenation char cToStr[2]; cToStr[1] = '\0'; cToStr[0] = curChar; strcat(curWord.content, cToStr); // segmentation fault words[curCount] = curWord; } else { // create new word struct word newWord; newWord.length = 1; newWord.content = &curChar; words[curCount] = newWord; } i++; } close(infile); } int main() { openRead(); }
问题分析与解决
段错误的核心原因是非法内存访问,你的代码存在以下几个关键问题:
- 字符串常量不可修改:
firstWord.content = "test\0";中的"test"是只读字符串常量,存储在程序的只读数据段,strcat尝试修改它会直接触发段错误。 - 结构体数组未初始化:
struct word words[count];中除words[0]外的元素都未初始化,words[curCount]的content是野指针,访问必然出错。 - 栈变量指针无效:
newWord.content = &curChar;中curChar是栈上的局部变量,循环迭代会覆盖它,函数结束后地址直接失效,无法存储单词内容。 - 冗余的
lseek调用:read会自动推进文件指针,每次循环用lseek定位到i的位置会导致重复读取字符。
修正思路
- 用
malloc/realloc给每个单词的content动态分配可修改的内存。 - 初始化结构体数组的所有元素,避免野指针。
- 去掉冗余的
lseek,直接通过read逐字符推进文件指针。 - 处理连续空格的情况,避免生成空单词。
修正后的代码示例
#include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <stdlib.h> #include <string.h> struct word{ char *content; int length; }; void openRead(){ char curChar; int infile = open("sample.txt", O_RDONLY); if (infile == -1) { return; } const int max_words = 10; struct word words[max_words]; // 初始化所有单词结构体 for (int i = 0; i < max_words; i++) { words[i].content = NULL; words[i].length = 0; } int cur_word_idx = 0; // 逐字符读取,读到文件结束即停止 while (cur_word_idx < max_words && read(infile, &curChar, 1) == 1) { if (!isspace(curChar)) { struct word *cur_word = &words[cur_word_idx]; // 重新分配内存:原长度+1(当前字符)+1(终止符) char *new_content = realloc(cur_word->content, cur_word->length + 2); if (!new_content) { // 内存分配失败,清理已分配的内存 for (int i = 0; i <= cur_word_idx; i++) { free(words[i].content); } close(infile); return; } cur_word->content = new_content; cur_word->content[cur_word->length] = curChar; cur_word->length++; cur_word->content[cur_word->length] = '\0'; // 确保字符串终止 } else { // 遇到空格且当前单词有内容时,切换到下一个单词 if (cur_word_idx < max_words - 1 && words[cur_word_idx].length > 0) { cur_word_idx++; } // 连续空格直接跳过,不生成空单词 } } // 用系统调用write打印结果,不依赖<stdio.h> for (int i = 0; i < max_words && words[i].content; i++) { write(STDOUT_FILENO, words[i].content, words[i].length); write(STDOUT_FILENO, "\n", 1); free(words[i].content); // 释放动态分配的内存 } close(infile); } int main() { openRead(); return 0; }
关键说明
- 用
realloc动态调整内存大小,确保每个单词有足够空间存储字符。 - 初始化结构体数组所有元素,彻底避免野指针问题。
- 去掉冗余的
lseek,通过read的返回值判断是否读到文件末尾。 - 处理连续空格,不会生成空单词。
- 使用
write系统调用完成打印,完全不依赖<stdio.h>。 - 添加了基础错误处理,避免程序因文件打开失败、内存分配失败崩溃。
内容的提问来源于stack exchange,提问作者IndexZero
相关产品推荐
相关产品推荐

