Skip to content

命令行参数

命令行参数是C程序从命令行接收输入的一种方式,它允许用户在运行程序时传递参数。本文将详细介绍C语言中命令行参数的使用方法和最佳实践。

命令行参数的基本概念

main函数的参数

C语言的main函数可以接收两个参数:

c
int main(int argc, char *argv[])
  • argc:参数计数(argument count),表示命令行参数的数量
  • argv:参数向量(argument vector),是一个指向字符串数组的指针,每个字符串表示一个命令行参数

命令行参数的结构

当运行一个程序时,命令行参数的结构如下:

./program arg1 arg2 arg3
  • argv[0]:程序的名称(包括路径)
  • argv[1]:第一个参数
  • argv[2]:第二个参数
  • argv[3]:第三个参数
  • argc:参数的总数(包括程序名称)

基本用法示例

简单的命令行参数程序

c
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("参数数量: %d\n", argc);
    
    for (int i = 0; i < argc; i++) {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    
    return 0;
}

运行示例:

bash
$ ./program hello world 123
参数数量: 4
argv[0]: ./program
argv[1]: hello
argv[2]: world
argv[3]: 123

处理数值参数

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

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("用法: %s <num1> <num2>\n", argv[0]);
        return 1;
    }
    
    // 将字符串转换为整数
    int num1 = atoi(argv[1]);
    int num2 = atoi(argv[2]);
    
    printf("%d + %d = %d\n", num1, num2, num1 + num2);
    printf("%d - %d = %d\n", num1, num2, num1 - num2);
    printf("%d * %d = %d\n", num1, num2, num1 * num2);
    
    if (num2 != 0) {
        printf("%d / %d = %d\n", num1, num2, num1 / num2);
    } else {
        printf("除数不能为零\n");
    }
    
    return 0;
}

运行示例:

bash
$ ./calculator 10 5
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2

处理选项参数

c
#include <stdio.h>

int main(int argc, char *argv[]) {
    int verbose = 0;
    int count = 0;
    
    // 处理命令行选项
    for (int i = 1; i < argc; i++) {
        if (argv[i][0] == '-') {
            switch (argv[i][1]) {
                case 'v':
                    verbose = 1;
                    break;
                case 'c':
                    if (i + 1 < argc) {
                        count = atoi(argv[i + 1]);
                        i++; // 跳过下一个参数
                    }
                    break;
                default:
                    printf("未知选项: %s\n", argv[i]);
                    return 1;
            }
        } else {
            printf("参数: %s\n", argv[i]);
        }
    }
    
    if (verbose) {
        printf("详细模式启用\n");
    }
    
    if (count > 0) {
        printf("计数: %d\n", count);
    }
    
    return 0;
}

运行示例:

bash
$ ./program -v -c 5 file.txt
详细模式启用
计数: 5
参数: file.txt

命令行参数的高级处理

使用getopt函数

getopt是标准库中用于处理命令行选项的函数,定义在<unistd.h>头文件中。

c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int opt;
    int verbose = 0;
    int count = 0;
    char *filename = NULL;
    
    // 处理命令行选项
    while ((opt = getopt(argc, argv, "vc:f:")) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                break;
            case 'c':
                count = atoi(optarg);
                break;
            case 'f':
                filename = optarg;
                break;
            case '?':
                fprintf(stderr, "未知选项: %c\n", optopt);
                return 1;
            case ':':
                fprintf(stderr, "选项 %c 需要参数\n", optopt);
                return 1;
            default:
                fprintf(stderr, "错误的选项: %c\n", opt);
                return 1;
        }
    }
    
    // 处理剩余的参数
    for (int i = optind; i < argc; i++) {
        printf("额外参数: %s\n", argv[i]);
    }
    
    // 打印结果
    if (verbose) {
        printf("详细模式: 启用\n");
    }
    
    if (count > 0) {
        printf("计数: %d\n", count);
    }
    
    if (filename) {
        printf("文件名: %s\n", filename);
    }
    
    return 0;
}

运行示例:

bash
$ ./program -v -c 10 -f input.txt extra
详细模式: 启用
计数: 10
文件名: input.txt
额外参数: extra

getopt函数的选项字符串

getopt的第三个参数是选项字符串,格式如下:

  • 单个字符表示一个选项
  • 选项后面跟冒号表示需要参数
  • 选项后面跟两个冒号表示参数是可选的

示例:

选项字符串含义
"abc"选项a、b、c,无参数
"a🅱️c"选项a、b、c,都需要参数
"a:🅱️c"选项a的参数可选,选项b、c的参数必选

使用getopt_long函数

getopt_long函数支持长选项(以--开头的选项),定义在<getopt.h>头文件中。

c
#include <stdio.h>
#include <getopt.h>

int main(int argc, char *argv[]) {
    int verbose = 0;
    int count = 0;
    char *filename = NULL;
    
    // 长选项结构
    struct option long_options[] = {
        {"verbose", no_argument,       &verbose, 1},
        {"count",   required_argument, NULL, 'c'},
        {"file",    required_argument, NULL, 'f'},
        {"help",    no_argument,       NULL, 'h'},
        {NULL, 0, NULL, 0}
    };
    
    int opt;
    int option_index = 0;
    
    while ((opt = getopt_long(argc, argv, "vc:f:h", long_options, &option_index)) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                break;
            case 'c':
                count = atoi(optarg);
                break;
            case 'f':
                filename = optarg;
                break;
            case 'h':
                printf("用法: %s [选项]\n", argv[0]);
                printf("  -v, --verbose     启用详细模式\n");
                printf("  -c, --count N     设置计数\n");
                printf("  -f, --file FILE   指定文件\n");
                printf("  -h, --help        显示帮助信息\n");
                return 0;
            case '?':
                return 1;
            default:
                return 1;
        }
    }
    
    // 打印结果
    if (verbose) {
        printf("详细模式: 启用\n");
    }
    
    if (count > 0) {
        printf("计数: %d\n", count);
    }
    
    if (filename) {
        printf("文件名: %s\n", filename);
    }
    
    return 0;
}

运行示例:

bash
$ ./program --verbose --count 5 --file data.txt
详细模式: 启用
计数: 5
文件名: data.txt

实际应用示例

1. 文件复制程序

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

void copy_file(const char *src, const char *dest) {
    FILE *source = fopen(src, "r");
    if (source == NULL) {
        fprintf(stderr, "无法打开源文件: %s\n", src);
        exit(1);
    }
    
    FILE *destination = fopen(dest, "w");
    if (destination == NULL) {
        fprintf(stderr, "无法打开目标文件: %s\n", dest);
        fclose(source);
        exit(1);
    }
    
    int ch;
    while ((ch = fgetc(source)) != EOF) {
        fputc(ch, destination);
    }
    
    fclose(source);
    fclose(destination);
    printf("文件复制成功: %s -> %s\n", src, dest);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "用法: %s <源文件> <目标文件>\n", argv[0]);
        return 1;
    }
    
    copy_file(argv[1], argv[2]);
    return 0;
}

运行示例:

bash
$ ./copy input.txt output.txt
文件复制成功: input.txt -> output.txt

2. 文本搜索程序

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

void search_in_file(const char *filename, const char *pattern) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        fprintf(stderr, "无法打开文件: %s\n", filename);
        return;
    }
    
    char line[1024];
    int line_num = 1;
    
    while (fgets(line, sizeof(line), file) != NULL) {
        if (strstr(line, pattern) != NULL) {
            printf("%s:%d: %s", filename, line_num, line);
        }
        line_num++;
    }
    
    fclose(file);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "用法: %s <文件> <搜索模式>\n", argv[0]);
        return 1;
    }
    
    search_in_file(argv[1], argv[2]);
    return 0;
}

运行示例:

bash
$ ./search file.txt hello
file.txt:5: hello world
file.txt:10: say hello to everyone

3. 数学计算程序

c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "用法: %s <操作> <数1> [数2]\n", argv[0]);
        fprintf(stderr, "操作: add, sub, mul, div, sqrt, pow\n");
        return 1;
    }
    
    char *operation = argv[1];
    double num1 = atof(argv[2]);
    double result;
    
    if (strcmp(operation, "add") == 0) {
        if (argc != 4) {
            fprintf(stderr, "用法: %s add <数1> <数2>\n", argv[0]);
            return 1;
        }
        double num2 = atof(argv[3]);
        result = num1 + num2;
        printf("%.2f + %.2f = %.2f\n", num1, num2, result);
    } else if (strcmp(operation, "sub") == 0) {
        if (argc != 4) {
            fprintf(stderr, "用法: %s sub <数1> <数2>\n", argv[0]);
            return 1;
        }
        double num2 = atof(argv[3]);
        result = num1 - num2;
        printf("%.2f - %.2f = %.2f\n", num1, num2, result);
    } else if (strcmp(operation, "mul") == 0) {
        if (argc != 4) {
            fprintf(stderr, "用法: %s mul <数1> <数2>\n", argv[0]);
            return 1;
        }
        double num2 = atof(argv[3]);
        result = num1 * num2;
        printf("%.2f * %.2f = %.2f\n", num1, num2, result);
    } else if (strcmp(operation, "div") == 0) {
        if (argc != 4) {
            fprintf(stderr, "用法: %s div <数1> <数2>\n", argv[0]);
            return 1;
        }
        double num2 = atof(argv[3]);
        if (num2 == 0) {
            fprintf(stderr, "错误: 除数不能为零\n");
            return 1;
        }
        result = num1 / num2;
        printf("%.2f / %.2f = %.2f\n", num1, num2, result);
    } else if (strcmp(operation, "sqrt") == 0) {
        if (num1 < 0) {
            fprintf(stderr, "错误: 平方根不能为负数\n");
            return 1;
        }
        result = sqrt(num1);
        printf("sqrt(%.2f) = %.2f\n", num1, result);
    } else if (strcmp(operation, "pow") == 0) {
        if (argc != 4) {
            fprintf(stderr, "用法: %s pow <底数> <指数>\n", argv[0]);
            return 1;
        }
        double num2 = atof(argv[3]);
        result = pow(num1, num2);
        printf("%.2f ^ %.2f = %.2f\n", num1, num2, result);
    } else {
        fprintf(stderr, "未知操作: %s\n", operation);
        fprintf(stderr, "可用操作: add, sub, mul, div, sqrt, pow\n");
        return 1;
    }
    
    return 0;
}

运行示例:

bash
$ ./calc add 10 5
10.00 + 5.00 = 15.00

$ ./calc sqrt 16
sqrt(16.00) = 4.00

$ ./calc pow 2 3
2.00 ^ 3.00 = 8.00

命令行参数的最佳实践

1. 提供帮助信息

c
void print_help(const char *program_name) {
    printf("用法: %s [选项]\n", program_name);
    printf("选项:\n");
    printf("  -h, --help        显示帮助信息\n");
    printf("  -v, --verbose     启用详细模式\n");
    printf("  -o, --output FILE 指定输出文件\n");
}

int main(int argc, char *argv[]) {
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_help(argv[0]);
            return 0;
        }
    }
    
    // 其他代码...
    
    return 0;
}

2. 检查参数数量

c
int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "错误: 参数不足\n");
        fprintf(stderr, "用法: %s <参数1> <参数2>\n", argv[0]);
        return 1;
    }
    
    // 其他代码...
    
    return 0;
}

3. 验证参数有效性

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

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "用法: %s <数字>\n", argv[0]);
        return 1;
    }
    
    char *endptr;
    long num = strtol(argv[1], &endptr, 10);
    
    if (*endptr != '\0') {
        fprintf(stderr, "错误: '%s' 不是有效的数字\n", argv[1]);
        return 1;
    }
    
    printf("你输入的数字是: %ld\n", num);
    return 0;
}

4. 使用枚举管理选项

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

typedef enum {
    OPTION_HELP,
    OPTION_VERBOSE,
    OPTION_OUTPUT,
    OPTION_UNKNOWN
} OptionType;

OptionType get_option_type(const char *arg) {
    if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
        return OPTION_HELP;
    } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "--verbose") == 0) {
        return OPTION_VERBOSE;
    } else if (strcmp(arg, "-o") == 0 || strcmp(arg, "--output") == 0) {
        return OPTION_OUTPUT;
    } else {
        return OPTION_UNKNOWN;
    }
}

int main(int argc, char *argv[]) {
    int verbose = 0;
    char *output_file = NULL;
    
    for (int i = 1; i < argc; i++) {
        OptionType opt = get_option_type(argv[i]);
        
        switch (opt) {
            case OPTION_HELP:
                printf("用法: %s [选项]\n", argv[0]);
                printf("  -h, --help        显示帮助信息\n");
                printf("  -v, --verbose     启用详细模式\n");
                printf("  -o, --output FILE 指定输出文件\n");
                return 0;
            case OPTION_VERBOSE:
                verbose = 1;
                break;
            case OPTION_OUTPUT:
                if (i + 1 < argc) {
                    output_file = argv[i + 1];
                    i++;
                } else {
                    fprintf(stderr, "错误: -o选项需要一个参数\n");
                    return 1;
                }
                break;
            case OPTION_UNKNOWN:
                fprintf(stderr, "错误: 未知选项 '%s'\n", argv[i]);
                return 1;
        }
    }
    
    // 其他代码...
    
    return 0;
}

5. 处理路径和文件名

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

char *get_filename(const char *path) {
    char *last_slash = strrchr(path, '/');
#ifdef _WIN32
    char *last_backslash = strrchr(path, '\\');
    if (last_backslash > last_slash) {
        last_slash = last_backslash;
    }
#endif
    
    if (last_slash) {
        return last_slash + 1;
    }
    return (char *)path;
}

int main(int argc, char *argv[]) {
    printf("程序名: %s\n", get_filename(argv[0]));
    
    // 其他代码...
    
    return 0;
}

常见问题与解决方案

1. 空格分隔的参数

问题: 如果参数中包含空格,会被当作多个参数处理

解决方案: 使用引号包围包含空格的参数

bash
$ ./program "hello world" "test 123"

2. 特殊字符处理

问题: 命令行中的特殊字符(如 *? 等)会被 shell 扩展

解决方案: 使用引号或反斜杠转义特殊字符

bash
$ ./program "*.txt"  # 传递字面量 *.txt
$ ./program \*.txt   # 传递字面量 *.txt

3. 环境变量

问题: 如何在命令行中使用环境变量

解决方案: 使用 shell 的环境变量展开

bash
$ ./program $HOME/file.txt
$ ./program ${PATH}

4. 重定向和管道

问题: 如何处理标准输入/输出重定向和管道

解决方案: C程序默认从 stdin 读取,向 stdout 写入,所以可以直接使用 shell 的重定向和管道

bash
$ ./program < input.txt  # 从 input.txt 读取输入
$ ./program > output.txt # 输出写入 output.txt
$ ./program | grep error # 输出通过管道传递给 grep

总结

命令行参数是C程序与用户交互的重要方式,它允许用户在运行程序时传递参数,使程序更加灵活和通用。本文介绍了:

核心概念:

  1. main函数的参数:argc(参数计数)和argv(参数向量)
  2. 命令行参数的结构:argv[0]是程序名,后续是用户传递的参数
  3. 基本用法:遍历argv数组获取参数
  4. 高级处理:使用getopt和getopt_long函数处理选项参数

实际应用:

  1. 文件操作程序
  2. 文本处理程序
  3. 数学计算程序
  4. 系统工具程序

最佳实践:

  1. 提供帮助信息
  2. 检查参数数量
  3. 验证参数有效性
  4. 处理错误情况
  5. 使用枚举管理选项

通过掌握命令行参数的使用,你可以编写更加灵活、强大的C程序,满足各种实际应用需求。