C语言 main() 函数参数详解

1. 核心理解

int main(int argc, char **argv)

1.1 char **argv 双重指针

argv 是双重指针,指向一个 char 类型的数组,每个 char 再指向一个字符串。

argv ─→ argv[0] ─→ "D:\Portable\RedPanda-CPP\main.exe"
     ─→ argv[1] ─→ "hello"
     ─→ argv[2] ─→ "world"
     ─→ argv[3] ─→ NULL

关键argv[argc] == NULL,数组以 NULL 结束。

// 当 argc = 3 时
argv[0] = "D:\Portable\RedPanda-CPP\main.exe";
argv[1] = "hello";
argv[2] = "world";
argv[3] = NULL;  // 数组结束标记

2. 基础示例

#include <stdio.h>

int main(int argc, char **argv) {
    for (int i = 0; i < argc; i++) {
        // &argv[i]  argv数组中第 i 个元素的地址
        // argv[i]   第 i 个参数字符串的首地址
        printf("argv[%d] address=%p *addr=%p value=%s\r\n", 
               i, &argv[i], argv[i], argv[i]);
    }
    return 0;
}

等价写法

int main(int argc, char **argv)    // 推荐(强调指针)
int main(int argc, char *argv[])   // 等价(强调数组)

运行示例

gcc main.c -o main.exe
D:\Portable\RedPanda-CPP\main.exe hello world

输出:

argv[0] address=0000002286705bf0 *addr=0000002286705c40 value=D:\Portable\RedPanda-CPP\main.exe
argv[1] address=0000002286705bf8 *addr=0000002286705d00 value=hello
argv[2] address=0000002286705c00 *addr=0000002286705d40 value=world

运行截图
运行截图


3. 内存布局

完整结构

argc = 3

栈上的 argv 数组              堆/数据段上的字符串
┌─────────────┐
│ argv[0]     │ ─────→ "D:\Portable\RedPanda-CPP\main.exe"
│ 0x2286705c40│
├─────────────┤
│ argv[1]     │ ─────→ "hello"
│ 0x2286705d00│
├─────────────┤
│ argv[2]     │ ─────→ "world"
│ 0x2286705d40│
├─────────────┤
│ argv[3]     │ ─────→ NULL
└─────────────┘

地址间隔:8 字节(64位指针)
&argv[0] = 0x2286705bf0
&argv[1] = 0x2286705bf8
&argv[2] = 0x2286705c00

三层地址关系

argv[1] = "hello" 为例:

&argv[1]      // char ** → argv数组中第1个指针的地址 (0x2286705bf8)
argv[1]       // char *  → 字符串首地址 (0x2286705d00)
*argv[1]      // char    → 第一个字符 ('h')

argv[1][0]    // 'h'
argv[1][1]    // 'e'
argv[1][5]    // '\0'

4. 参数说明

参数 含义
argc 参数个数(≥1)
argv[0] 程序名称(路径)
argv[1] ~ argv[argc-1] 用户传入的参数
argv[argc] NULL(数组结束标记)

5. 实用示例

参数校验

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

int main(int argc, char **argv) {
    if (argc != 3) {
        printf("用法: %s <数字1> <数字2>\n", argv[0]);
        return 1;
    }

    int a = atoi(argv[1]);
    int b = atoi(argv[2]);
    printf("%d + %d = %d\n", a, b, a + b);
    return 0;
}

解析命令行选项

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

int main(int argc, char **argv) {
    int verbose = 0;
    char *input_file = NULL;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-v") == 0) {
            verbose = 1;
        } else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
            input_file = argv[i + 1];
            i++;
        }
    }

    if (verbose) printf("详细模式已开启\n");
    if (input_file) printf("输入文件: %s\n", input_file);
    return 0;
}

6. 常见错误

数组越界

错误:直接访问 argv[1] 而不检查 argc,无参数时会崩溃。
// ❌ 错误
int main(int argc, char **argv) {
    printf("%s\n", argv[1]);  // 崩溃
    return 0;
}

// ✅ 正确
int main(int argc, char **argv) {
    if (argc < 2) {
        printf("需要至少 1 个参数\n");
        return 1;
    }
    printf("%s\n", argv[1]);
    return 0;
}

类型转换

argv 都是字符串,需要手动转换:

int num = atoi(argv[1]);              // 字符串 → 整数
float f = atof(argv[1]);              // 字符串 → 浮点数
long l = strtol(argv[1], NULL, 10);   // 更安全的转换

字符串修改

⚠️ 警告:argv 指向的字符串通常是只读的,直接修改可能崩溃。需要修改时先复制到缓冲区。
// ⚠️ 危险
argv[1][0] = 'X';

// ✅ 安全
char buffer[256];
strncpy(buffer, argv[1], sizeof(buffer) - 1);
buffer[0] = 'X';

7. 快速参考

表达式 类型 含义
argc int 参数个数(≥1)
argv char ** 指向字符串数组的指针
argv[i] char * 第 i 个字符串的首地址
argv[i][j] char 第 i 个字符串的第 j 个字符
&argv[i] char ** argv 数组第 i 个元素的地址
argv[argc] char * NULL(数组结束标记)