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

处理29个文件后C语言代码崩溃问题求助

命令行文件重命名工具崩溃问题排查与修复

问题概述

基于C语言开发的命令行文件重命名工具,在Arch Linux环境用gcc 12.2.0编译,处理少量文件时功能正常,但每次处理到第29个文件时程序崩溃。工具预期将文件按「前缀+序号+原扩展名」格式重命名,例如bg.jpeg→file-1.jpeg。

问题代码

renameFiles函数代码

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

int renameFiles(char *files[], char *path, char *prefix) {

  int i = 0;
  FILE *f;
  char *filename;
  char *ext;
  char *buffer;
  long numbytes;

  while ( files[i] != NULL ){

    filename = files[i];

    char filepath[sizeof path + sizeof filename];
    strcpy(filepath, path);
    strcat(filepath, filename);

    f = fopen(filepath, "rb");

    if (f == NULL)
      return 1;

    fseek(f, 0L, SEEK_END);
    numbytes = ftell(f);
    fseek(f, 0L, SEEK_SET);

    buffer = (char*)calloc(numbytes, sizeof(char*));

    if (buffer == NULL)
      return 1;

    fread(buffer, sizeof(char), numbytes, f);
    fclose(f);


    remove(filepath);

    ext = strrchr(filename, '.');

    if (!ext)
      ext = "";

    char int_str[sizeof i];
    sprintf(int_str, "%d", i + 1);

    char newfilename[sizeof prefix + sizeof int_str + sizeof ext];
    strcpy(newfilename, prefix);
    strcat(newfilename, int_str);
    strcat(newfilename, ext);

    char newfilepath[sizeof path + sizeof newfilename];
    strcpy(newfilepath, path);
    strcat(newfilepath, newfilename);


    f = fopen(newfilepath, "w");

    if (f == NULL)
      return 1;

    fwrite(buffer, 1, numbytes, f);
    printf("%s\n", newfilepath);
    fclose(f);

    i++;
  }

  return 0;
}

Main.c代码

#include "files.h"
#include <readline/readline.h>
#include <readline/history.h>

int main(int argc, char *argv[]) {

  char *files[1024] = {};
  getFilesList(files, argv[1]);

  int length = 0;

  while( files[length] != NULL ){
    length++;
  }

  printf("%d files found on %s\n", length + 1, argv[1]);


  char *userInput = readline("Continue (y/n): ");

  if (strcmp(userInput, "y") != 0){
    return 0;
  }

  renameFiles(files, argv[1], argv[2]);

  return 0;
}

崩溃原因分析

  1. 缓冲区溢出(核心原因)
    代码中多次用sizeof(指针)计算字符串数组大小,比如char filepath[sizeof path + sizeof filename];。这里path和filename是指针类型,sizeof返回的是指针自身的字节数(64位系统为8字节),而非字符串的实际长度。这导致数组容量远小于实际需求,字符串拼接时会破坏栈内存。当处理到第29个文件时,序号为30(字符串长度2),加上前缀、扩展名后总长度超过了过小的数组容量,触发崩溃。

  2. 内存分配错误
    buffer = (char*)calloc(numbytes, sizeof(char*));中,sizeof(char*)是指针大小(8字节),但buffer是char类型数组,应使用sizeof(char)。此错误会导致分配的内存是实际需要的8倍,造成内存浪费,但不是直接崩溃原因。

  3. 低效且危险的文件操作
    代码通过「读取原文件内容→删除原文件→写入新文件」的方式实现重命名,完全没必要。这种方式不仅效率低下,还会在处理大文件时占用大量内存,同时增加IO出错风险。

  4. 内存泄漏
    每次循环中用calloc分配的buffer未释放,会导致内存泄漏,但不会直接引发崩溃。

修复方案

核心修复点

  • 用strlen()获取字符串实际长度,或定义足够大的静态缓冲区(如#define MAX_PATH 4096,符合Linux路径长度限制)。
  • 替换错误的内存分配参数为sizeof(char)。
  • 用系统调用rename()直接重命名文件,替代读写文件的低效方式。
  • 释放动态分配的内存,避免泄漏。

修正后的renameFiles函数

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#define MAX_PATH 4096  // Linux系统路径最大长度一般不超过4096字节

int renameFiles(char *files[], char *path, char *prefix) {
    int i = 0;
    char *filename;
    char *ext;
    char filepath[MAX_PATH];
    char newfilepath[MAX_PATH];
    char int_str[10];  // 足够存储整数转为字符串的长度(支持到10位数字)

    while (files[i] != NULL) {
        filename = files[i];

        // 安全拼接原文件路径
        snprintf(filepath, sizeof(filepath), "%s%s", path, filename);

        // 获取文件扩展名
        ext = strrchr(filename, '.');
        if (!ext)
            ext = "";

        // 生成序号字符串
        snprintf(int_str, sizeof(int_str), "%d", i + 1);

        // 安全拼接新文件路径
        snprintf(newfilepath, sizeof(newfilepath), "%s%s%s%s", path, prefix, int_str, ext);

        // 调用系统函数直接重命名
        if (rename(filepath, newfilepath) != 0) {
            fprintf(stderr, "重命名失败: %s\n", strerror(errno));
            return 1;
        }

        printf("%s\n", newfilepath);
        i++;
    }

    return 0;
}

额外优化

  • 使用snprintf()替代strcpy()+strcat(),从根源避免缓冲区溢出。
  • 移除不必要的文件读写逻辑,大幅提升效率,同时消除内存分配相关问题。
  • 增加错误信息输出,便于排查重命名失败的具体原因。

其他注意事项

  • 主函数中文件数量统计存在错误:printf("%d files found on %s\n", length + 1, argv[1]);应改为printf("%d files found on %s\n", length, argv[1]);,因为length已经是遍历到NULL时的有效文件计数。
  • 确保getFilesList()函数正确遍历目录,返回的文件名列表以NULL结尾,且不会超出files数组的容量(1024)。

内容的提问来源于stack exchange,提问作者Naapuri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 14:40:48