如何定位并解决C语言项目中的SIGSEGV(段错误)问题
排查并解决Cygwin下project1.exe的SIGSEGV错误(仅修改printandcombine.c/h)
在Cygwin环境编译运行project1.exe时触发SIGSEGV错误,调试器指向combine_like_terms函数,仅允许修改printandcombine.c和printandcombine.h文件,以下是项目相关代码及问题排查、解决方案:
原项目代码
printandcombine.h
#ifndef PRINTANDCOMBINE_H #define PRINTANDCOMBINE_H //Returns a string representation of the struct term_t char * term_to_string(term_t * term); //Prints a linkedlist in no specific order void print_linklist(node_t * curr); //Returns a new linkedlist that is the result of combining the like terms node_t * combine_like_terms(const node_t * current_node_ptr); #endif
printandcombine.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "common.h" /* Functions */ char * term_to_string(term_t * term) { char *str = malloc((sizeof(char)) * 1024); sprintf(str, "%d%c%d ", term->coefficient, term->var, term->exponent); printf("%s", str); free(str); return str; } void print_linklist(node_t * curr) { printf("%s", term_to_string(curr->term)); if (!(curr->next_node == NULL)) { print_linklist(curr->next_node); } } node_t * combine_like_terms(const node_t * current_node_ptr) { node_t *temp = (node_t *)current_node_ptr; node_t *out = malloc(sizeof(node_t)); node_t *move = out; int x = 0; while (!(temp == NULL)) { if (temp->term->exponent > x) { x = temp->term->exponent; } temp = temp->next_node; } for (; x >= 0; x--) { temp = (node_t *)current_node_ptr; int a = 0; while (!(temp == NULL)) { if (temp->term->exponent == x) { a = a + temp->term->coefficient; } move->term->coefficient = a; move->term->var = 'x'; move->term->exponent = x; if (x > 0) { move->next_node = (node_t *)malloc(sizeof(node_t)); move = move->next_node; } } } return out; }
其他关联代码(仅作参考,不可修改)
project1.c
/* This is your main file */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include"common.h" #include"buildlinklist.h" #include"printandcombine.h" int main() { node_t * node_ptr = NULL; node_t * new_node_ptr=NULL; printf("NAME: Name\n"); /* Build linklist */ read_objects(&node_ptr); /* Print the link list */ printf("Original: "); print_linklist(node_ptr); /* Combine like terms in the link list and create a new link list */ new_node_ptr=combine_like_terms(node_ptr); printf("\nCombined: : "); /* Print new combine linklist */ print_linklist(new_node_ptr); printf("\nNAME: Name\n"); free(node_ptr); free(new_node_ptr); return 0; }
buildlinklist.h
#ifndef BUILDLINKLIST_H #define BUILDLINKLIST_H #include"common.h" /* Read from a text file and put in to a term_t structure. Then create a linklist of node (each node has an element of term struct.)*/ void read_objects(node_t ** pointer_to_node_ptr); /* Assign value to term_t object elements */ term_t * term_from_string(char * buff); /* Add a node to the link list */ int add_node(node_t ** pointer_to_node_ptr, void * term); #endif
buildlinklist.c
#include<stdio.h> #include<stdlib.h> #include<string.h> #include"common.h" /* Assign value to term_t object elements */ term_t * term_from_string(char * buff) { term_t * ret = malloc(sizeof(term_t)); ret->coefficient=atoi(strtok(buff, " ")); ret->var=*strtok(NULL, " "); ret->exponent=atoi(strtok(NULL, " ")); return ret; } /* Add a node to the link list */ int add_node(node_t ** pointer_to_node_ptr, void * term) { int ret = 0; node_t * newnode_ptr = (node_t *) malloc(sizeof(node_t)); if (newnode_ptr == NULL) { ret = -1; } else { newnode_ptr->term = term; newnode_ptr->next_node = *pointer_to_node_ptr; *pointer_to_node_ptr = newnode_ptr; } return ret; } /* Read from a text file and put in to a term_t structure. Then create a linklist of node (each node has an element of term struct.)*/ void read_objects(node_t ** pointer_to_node_ptr) { FILE *fp; char buffer[BUFFERLEN]; fp = fopen("terms.txt", "r"); while (fgets(buffer, BUFFERLEN,fp)) { term_t * this_term; this_term = term_from_string(buffer); add_node(pointer_to_node_ptr, this_term); } fclose(fp); }
common.h
#ifndef COMMON_H #define COMMON_H #define BUFFERLEN 40 typedef struct{ /* struct to hold elements like: 3x4 */ int coefficient; char var; int exponent; } term_t; typedef struct node { term_t * term; /* pointer to term */ struct node * next_node; /* pointer to next node */ } node_t; #endif
makefile
CC=gcc CFLAGS=-std=c11 -Wall -pedantic all: project1.c buildlinklist.o printandcombine.o $(CC) $(CFLAGS) project1.c buildlinklist.o printandcombine.o -o project1 buildlinklist.o: buildlinklist.c $(CC) $(CFLAGS) -c buildlinklist.c printandcombine.o: printandcombine.c $(CC) $(CFLAGS) -c printandcombine.c clean: rm -f *.o *~ project1.exe
错误排查分析
1. term_to_string函数的野指针问题
该函数中,先malloc分配字符串内存,打印后立即free,随后返回已释放的指针。print_linklist函数中使用这个野指针调用printf,会触发未定义行为,可能直接导致崩溃。
2. combine_like_terms函数的核心错误
- 空指针解引用:创建
node_t节点时,仅分配了节点本身的内存,但未为term指针分配term_t结构的内存,直接访问move->term->coefficient会触发SIGSEGV。 - 死循环:内层
while循环中未移动temp指针(缺少temp = temp->next_node;),导致循环永远无法退出,耗尽资源或触发错误。 - 链表尾节点未置空:最后一个节点的
next_node未设置为NULL,后续print_linklist遍历链表时会访问非法内存。 - 不必要的const强制转换:将
const node_t*强制转为node_t*,虽然语法合法,但违背了const语义,应改为const node_t* temp = current_node_ptr;避免修改原链表。
修正后的printandcombine.c代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "common.h" char * term_to_string(term_t * term) { // 分配足够内存,无需提前free char *str = malloc(1024 * sizeof(char)); if (!str) { perror("malloc failed"); exit(EXIT_FAILURE); } sprintf(str, "%d%c%d ", term->coefficient, term->var, term->exponent); // 这里不再打印,由print_linklist负责,避免重复输出 return str; } void print_linklist(node_t * curr) { if (!curr) return; // 空链表直接返回,避免空指针访问 char *term_str = term_to_string(curr->term); printf("%s", term_str); free(term_str); // 用完再释放内存 if (curr->next_node != NULL) { print_linklist(curr->next_node); } } node_t * combine_like_terms(const node_t * current_node_ptr) { if (!current_node_ptr) return NULL; // 处理空输入链表 const node_t *temp = current_node_ptr; int max_exp = 0; // 找出最大指数 while (temp != NULL) { if (temp->term->exponent > max_exp) { max_exp = temp->term->exponent; } temp = temp->next_node; } node_t *out = NULL; node_t **move = &out; // 使用二级指针简化链表构建 for (int x = max_exp; x >= 0; x--) { temp = current_node_ptr; int total_coeff = 0; // 累加当前指数的所有系数 while (temp != NULL) { if (temp->term->exponent == x) { total_coeff += temp->term->coefficient; } temp = temp->next_node; // 移动指针,避免死循环 } // 系数为0的项可以跳过,减少无用节点 if (total_coeff == 0) continue; // 分配节点和term内存 *move = malloc(sizeof(node_t)); if (!*move) { perror("malloc failed"); exit(EXIT_FAILURE); } (*move)->term = malloc(sizeof(term_t)); if (!(*move)->term) { perror("malloc failed"); exit(EXIT_FAILURE); } // 设置term属性 (*move)->term->coefficient = total_coeff; (*move)->term->var = 'x'; (*move)->term->exponent = x; (*move)->next_node = NULL; // 每个节点默认next为NULL move = &((*move)->next_node); // 移动到下一个节点的指针 } return out; }
额外说明
- 修正后的
term_to_string不再提前释放内存,而是由调用者print_linklist使用后释放,避免野指针。 combine_like_terms改用二级指针构建链表,逻辑更清晰,同时跳过系数为0的项,优化链表结构。- 新增了空指针检查,避免极端情况(如空输入链表)导致的崩溃。
内容的提问来源于stack exchange,提问作者dismembered
相关产品推荐
相关产品推荐

