使用pthreads与sscanf的C程序出现Segmentation Fault问题
问题详情
C程序在transaction函数的第一条sscanf语句处触发段错误,修改语句和变量后问题仍未解决。调试时执行该sscanf后代码停止并高亮,后续无法运行;无调试运行时程序能执行但输出结果不正确。
输入文件内容
assignment_5_input.txt内容如下:
A1 balance 5000 A2 balance 5000 A3 balance 5000 A4 balance 5000 A5 balance 5000 C1 deposit A2 1000 C2 withdraw A1 300 deposit A4 200 C3 deposit A3 500 withdraw A4 400 withdraw A1 100 C4 withdraw A1 40000 withdraw A2 800 C5 withdraw A5 5000
程序代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #define max_lines 100 #define max_len 1000 struct ThreadArgs { char *data; int *account; }; // 控制线程执行的互斥锁 pthread_mutex_t lock; void *transaction(void *arg){ struct ThreadArgs *args = (struct ThreadArgs *)arg; char *data_array = args->data; int *account_array = args->account; int deposit = 0; int withdraw = 0; int acc_num = 0; char wordd[] = "deposit"; char wordw[] = "withdraw"; char ddata[strlen(data_array) + 1]; strcpy(ddata, data_array); printf("%s", ddata); pthread_mutex_lock(&lock); // 进入临界区 if (strstr(ddata, wordd) != NULL) { sscanf(ddata, "deposit A%d %d", &acc_num, &deposit); account_array[acc_num - 1] += deposit; printf("Deposit: %d\n", deposit); printf("Account Number: %d\n", acc_num); } if (strstr(ddata, wordw) != NULL) { //sscanf(ddata, "withdraw A%d %d", &acc_num, &withdraw); if (account_array[acc_num - 1] - withdraw >= 0) { account_array[acc_num - 1] -= withdraw; printf("Withdrawal: %d\n", withdraw); printf("Account Number: %d\n", acc_num); } } free(args); pthread_mutex_unlock(&lock); // 退出临界区 return NULL; } int main() { int line = 0; char data[max_lines][max_len]; int num_account = 0; int num_client = 0; int num_account_count = 0; int num_client_count = 0; // 打开文件 FILE *fp; fp = fopen("assignment_5_input.txt", "r"); if (fp == NULL){ printf("File could not open.\n"); return 1; } while (!feof(fp) && !ferror(fp)){ if (fgets(data[line], max_len, fp) != NULL){ line++; } } fclose(fp); // 统计账户和客户端数量 for (int i = 0; i < line; i++){ if (sscanf(data[i], "A%d balance ", &num_account)){ num_account_count++; } if (sscanf(data[i], "C%d ", &num_client)){ num_client_count++; } } // 初始化账户余额 int account[num_account_count]; for (int i = 0; i < num_account_count; i++) { int balance = 0; sscanf(data[i], "A%d balance %d\n", &num_account, &balance); account[num_account - 1] = balance; } // 初始化互斥锁并创建线程 pthread_mutex_init(&lock, NULL); pthread_t threads[num_client_count]; for (int i = num_account_count; i < line; i++) { struct ThreadArgs *args = malloc(sizeof(struct ThreadArgs)); args->data = data[i]; args->account = account; pthread_create(&threads[i], NULL, transaction, (void *)args); } // 等待线程结束 for (int i = 0; i < num_client; i++){ pthread_join(threads[i], NULL); } pthread_mutex_destroy(&lock); // 输出结果 printf("No. of Accounts: %d\n", num_account); printf("No. of Clients: %d\n", num_client); for (int i = 0; i < num_account_count; i++) { printf("A%d balance: %d\n", i+1, account[i]); } return 0; }
问题根源与修复方案
1. 缺失字符串函数头文件
代码中使用了strstr、strlen、strcpy等字符串函数,但未包含<string.h>头文件。编译器无法识别函数原型,会引发未定义行为,包括段错误。
修复:在代码开头添加#include <string.h>。
2. sscanf匹配失败导致数组越界
处理客户端行(如C2 withdraw A1 300 deposit A4 200)时,strstr检测到deposit存在,但sscanf(ddata, "deposit A%d %d", ...)会匹配失败(行开头是C2而非deposit),此时acc_num保持初始值0,后续访问account_array[acc_num -1]即account_array[-1],直接触发段错误。
修复:
- 先跳过客户端行开头的
Cx标识,再逐个解析操作; - 每次调用
sscanf后检查返回值,确认匹配成功再执行后续逻辑; - 对包含多个操作的行,循环解析所有操作。
3. 线程创建与join的索引错误
创建线程时,循环变量i从num_account_count开始,但threads数组大小为num_client_count,直接使用threads[i]会导致数组越界。同时pthread_join使用num_client(最后一个客户端的编号)作为循环次数,而非客户端实际数量,导致join逻辑错误。
修复:
- 创建线程时用独立索引变量,避免越界;
pthread_join循环使用num_client_count作为次数。
4. withdraw变量未初始化且未赋值
withdraw初始值为0,且注释掉了赋值的sscanf,导致取款操作逻辑完全错误,若acc_num未正确初始化还会引发越界访问。
修复:取消注释sscanf行,确保acc_num和withdraw正确赋值后再执行取款。
修复后的核心代码片段
transaction函数修改
void *transaction(void *arg){ struct ThreadArgs *args = (struct ThreadArgs *)arg; char *data_array = args->data; int *account_array = args->account; char ddata[strlen(data_array) + 1]; strcpy(ddata, data_array); printf("Processing: %s", ddata); pthread_mutex_lock(&lock); // 跳过开头的Cx标识 char *ptr = strchr(ddata, ' '); if (ptr == NULL) { pthread_mutex_unlock(&lock); free(args); return NULL; } ptr++; // 跳过空格 // 循环解析所有操作 int acc_num, amount; while (*ptr != '\0') { if (sscanf(ptr, "deposit A%d %d", &acc_num, &amount) == 2) { if (acc_num >=1 && acc_num <= num_account_count) { account_array[acc_num - 1] += amount; printf("Deposit: %d to A%d\n", amount, acc_num); } // 移动指针到下一个操作 while (*ptr != ' ' && *ptr != '\0') ptr++; while (*ptr == ' ') ptr++; } else if (sscanf(ptr, "withdraw A%d %d", &acc_num, &amount) == 2) { if (acc_num >=1 && acc_num <= num_account_count && account_array[acc_num -1] >= amount) { account_array[acc_num -1] -= amount; printf("Withdraw: %d from A%d\n", amount, acc_num); } else { printf("Failed withdraw: %d from A%d (insufficient balance or invalid account)\n", amount, acc_num); } // 移动指针到下一个操作 while (*ptr != ' ' && *ptr != '\0') ptr++; while (*ptr == ' ') ptr++; } else { break; } } free(args); pthread_mutex_unlock(&lock); return NULL; }
main函数线程创建与join修改
pthread_t threads[num_client_count]; int thread_idx = 0; for (int i = num_account_count; i < line; i++) { struct ThreadArgs *args = malloc(sizeof(struct ThreadArgs)); args->data = data[i]; args->account = account; pthread_create(&threads[thread_idx], NULL, transaction, (void *)args); thread_idx++; } for (int i = 0; i < num_client_count; i++){ pthread_join(threads[i], NULL); }
额外注意事项
- 访问数组前必须检查索引合法性,避免越界;
- 所有标准库函数必须包含对应头文件;
- 多线程环境下,共享资源的访问必须通过互斥锁正确保护;
- 调用输入处理函数时,务必检查返回值,确认参数是否被正确赋值。
内容的提问来源于stack exchange,提问作者Danial Mithani

