我的学习之旅:理解 C 语言

社区文章 发布于 2025 年 3 月 9 日

大家好!我刚开始学习 C 语言编程,我想通过分解这个程序来记录我的学习旅程。这些代码来自 alex the dev,我只是在代码中添加注释以理解发生了什么。目前我正在学习 C 语言的第五章,如果你想比较的话。格式化感谢 Copilot。


1. 引入必要的库

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

我学到了什么:

  • stdlib.h:
    提供动态内存分配函数(`malloc`、`realloc`、`free`)。
  • stdio.h:
    包含输入/输出操作函数,如 `printf`、文件操作(`fopen`、`fread`、`fclose`)。
  • string.h:
    提供字符串处理函数,如 `strcmp`(用于比较字符串)和 `memcpy`(用于复制内存)。

2. 定义一个结构体来保存参数

typedef struct arguments {
    char **files;
    unsigned int files_count;
} arguments;
  • 结构体(`struct`)在 C 语言中用于将相关变量组合在一起。
  • 结构体成员
    • char **files:
      这是一个双指针。
      • 一个 `char *` 表示一个字符串(字符数组)。
      • 一个 `char **` 表示一个字符串数组,意味着它可以保存多个文件名。
    • unsigned int files_count:
      跟踪已提供的文件名数量。使用 `unsigned int` 确保该数字永远不会是负数。

3. 解析命令行参数

void parse_arguments(int argc, char **argv, arguments *args) {
    args->files = malloc(argc * sizeof(char *));
    int index = 0;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--help") == 0) {
            printf("Usage: ./main [file1] or [--help]
");
            exit(0);
        } else {
            args->files[index] = argv[i];
            index++;
        }
    }

    args->files_count = index;
}

我学到了什么:

  • 函数参数
    • int argc:命令行参数的数量。
    • char **argv:字符串数组,其中每个字符串都是一个命令行参数。
    • arguments *args:指向我们的 `arguments` 结构体的指针,以便函数可以修改它。
  • 动态内存分配
    • args->files = malloc(argc * sizeof(char *));
      这里我们为指针数组分配内存。使用 `->` 运算符是因为 `args` 是一个指向结构体的指针。它是 `(*args).files` 的简写。
  • 循环遍历参数
    • 我们从索引 `1` 开始(因为 `argv[0]` 是程序名称)。
    • 如果参数是 `"--help"`,程序会打印帮助消息并退出。
    • 其他参数被假定为文件名并存储在 `files` 数组中。
  • 存储文件计数
    • 循环结束后,存储的文件名数量保存到 `args->files_count` 中。

4. 将文件读入内存

#define MAX_LEN 128

int read_file(char *path, char **buffer) {
    int tmp_capacity = MAX_LEN;
    char *tmp = malloc(tmp_capacity * sizeof(char));
    int tmp_size = 0;
    
    if (tmp == NULL) {
        perror("Memory allocation error");
        exit(1);
    }
    
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        perror("File opening error");
        exit(1);
    }
    
    int size = 0;
    do {
        if (tmp_size + MAX_LEN > tmp_capacity) {
            tmp_capacity *= 2;
            tmp = realloc(tmp, tmp_capacity * sizeof(char));
            if (tmp == NULL) {
                perror("Memory allocation error");
                exit(1);
            }
        }
        size = fread(tmp + tmp_size, sizeof(char), MAX_LEN, f);
        tmp_size += size;
    } while (size > 0);
    
    fclose(f);
    tmp[tmp_size] = '\0';
    *buffer = tmp;
    
    return tmp_size;
}

我学到了什么:

  • 定义常量
    • #define MAX_LEN 128 设置从文件中读取的块大小。
  • 文件内容的内存分配
    • 最初,分配一个大小为 `MAX_LEN` 的缓冲区 `tmp`。
    • 如果读取文件时需要更多空间,则使用 `realloc` 将缓冲区大小加倍。
  • 文件操作
    • 使用 `fopen` 打开文件。如果打开失败,则打印错误。
    • 使用 `fread` 分块读取文件,指针算术(`tmp + tmp_size`)确保新数据正确追加。
  • 缓冲区终止
    • 读取后,关闭文件并添加空终止符(`\0`)以标记字符串的结尾。
    • 通过 `*buffer` 返回指向已分配缓冲区的指针。

5. 主函数:将所有内容整合

int main(int argc, char **argv) {
    arguments args = {0};
    parse_arguments(argc, argv, &args);

    char *buffer = NULL;
    int buffer_size = 0;
    
    for (int i = 0; i < args.files_count; i++) {
        char *content = NULL;
        int size = read_file(args.files[i], &content);

        buffer = realloc(buffer, buffer_size + size + 1);
        memcpy(buffer + buffer_size, content, size);
        buffer_size += size;

        free(content);
    }

    printf("%s\n", buffer);
    free(buffer);
    free(args.files);   
    
    return 0;
}

🚀 最终思考

在记录这个程序的过程中,我真的学到了很多!
如果你也在学习 C 语言,希望这能有所帮助。如果你有任何问题(或者我解释错了 😅),请告诉我!

编程愉快!🎉

社区

注册登录 发表评论