You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pthreads与sscanf的C程序出现Segmentation Fault问题

段错误排查:transaction函数中sscanf触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:07:06