以下内容基于 Linux 5.15+ 内核、glibc 2.35+、x86_64 架构,结合源码、标准与实战经验,系统展开:
标准形式的主函数的参数:
cpp
int main(int argc, char *argv[]); // 等价于 char **argv
// 或
int main(void); // 无参数(不推荐用于需命令行参数的程序)
argc (Argument Count) 非负整数,表示 argv 中有效指针数量(含程序名)。argc == 0 理论允许但极罕见(POSIX 要求 argc >= 1)。
argv (Argument Vector) 字符串指针数组,argv[0] 为程序调用名(非绝对路径 ,取决于调用方式)。argv[argc] == NULL(标准保证,可用于安全遍历)。可修改性 :C 标准明确允许修改 argv 指向的字符串内容及指针本身(如 argv[1][0] = 'X'),但修改后影响仅限当前进程。
在Linux下,主函数有三个参数,多了一个环境变量
cpp
int main(int argc, char *argv[], char *envp[]); // 部分编译器支持(如 GCC)
envp:以 NULL 结尾的字符串数组,格式 "KEY=VALUE"。
更可移植方式:通过全局变量 extern char **environ;(需包含 <unistd.h>)访问环境变量
举例:
cpp
#include <stdio.h>
#include <unistd.h> // for environ
int main(int argc, char *argv[]) {
// 遍历命令行参数(安全:依赖 argv[argc]==NULL)
for (int i = 0; i < argc; i++)
printf("argv[%d] = \"%s\"\n", i, argv[i]);
// 遍历环境变量(推荐方式)
printf("\nEnvironment variables:\n");
for (char **env = environ; *env != NULL; env++)
printf("%s\n", *env);
// 修改示例(合法但需谨慎)
if (argc > 1) argv[1][0] = 'M'; // 修改第一个参数首字符
return 0;
}
编译运行:
gcc main.c -o app && ./app hello world
输出包含参数与环境变量,并验证修改生效
Linux上:
Linux 特有机制与调试
/proc 文件系统
/proc/self/cmdline:原始命令行参数(\0 分隔,需转义查看:cat /proc/self/cmdline | tr '\0' ' ')
/proc/self/environ:原始环境变量(同样 \0 分隔)
辅助向量(Auxiliary Vector)
内核通过栈传递额外信息(如 AT_EXECFN 含真实执行路径),可通过 getauxval()(glibc)或解析栈获取,不作为 main 参数。
调试技巧
gdb 启动时设置参数:gdb ./app → (gdb) run arg1 arg2
查看栈布局:p argv、x/s argv[0] 等
参数传递的完整生命周期:
execve 系统调用链路:
cpp
// 用户态调用(父进程)
execve("/bin/ls", argv, envp); // argv[0]="ls", argv[1]=NULL
// 内核路径(简化)
do_execveat_common()
→ bprm_stack_limits() // 栈空间校验(RLIMIT_STACK)
→ copy_strings_kernel() // 复制 argv/envp 字符串到新进程栈
→ setup_arg_pages() // 映射栈内存,设置栈指针
→ create_elf_tables() // 构建栈上数据结构(关键!)
→ copy_strings() // 复制参数/环境字符串
→ copy_argv_envp() // 复制指针数组
→ elf_map() // 映射辅助向量(AuxVec)
→ start_thread() // 设置 RIP=RSP=程序入口
关键限制:MAX_ARG_STRLEN(单参数 128KB)、MAX_ARG_STRINGS(总参数数 0x7FFFFFFF),超限返回 E2BIG
栈顶布局(x86_64,从高地址→低地址):
cpp
[环境变量字符串...]
[命令行参数字符串...]
[平台字符串(如"x86_64")]
[NULL] ← envp 结束
[envp[0], envp[1], ..., NULL]
[NULL] ← argv 结束
[argv[0], argv[1], ..., NULL]
[argc (8字节)]
[辅助向量 (AT_*, 16字节/项)] ← RSP 初始指向此处
[ELF 头信息...]
用户态启动流程:
cpp
; _start (crt1.o) 汇编入口
_start:
xor %ebp, %ebp ; 清除帧指针(标记栈底)
mov %rsp, %rdi ; 将栈指针(含argc/argv)传入 RDI
call __libc_start_main ; 跳转至C库初始化
cpp
// glibc sysdeps/generic/libc-start.c 简化逻辑
__libc_start_main(
main, argc, argv,
init, fini, rtld_fini,
stack_end // 即 RSP 初始值(指向辅助向量)
) {
// 1. 从 stack_end 向上解析:先跳过 argc,定位 argv
// 2. 通过 argv[argc]==NULL 定位 envp 起始
// 3. 通过 envp 末尾 NULL 定位辅助向量
// 4. 初始化 TLS、设置全局 environ
// 5. 调用 main(argc, argv, __environ)
}