为何argc与argv在main函数外仍能正常工作?
为什么argc和argv在main函数外的函数中也能正常工作?
我意外发现argc和argv在main函数外的函数里也能正常输出命令行参数,以下是复现代码(使用GCC编译):
main.c
#include <stdio.h> int test(void); int main(void) { test(); }
test.c
#include <stdio.h> int test(int argc, char **argv) { printf("argc = %i\n", argc); printf("args:\n"); for (int i = 0; i < argc; i++) { printf("- %s\n", argv[i]); } printf("test called\n"); return 0; }
这本质是未定义行为,只是在GCC的x86/x86_64调用约定下碰巧“正常工作”了,并非标准C允许的行为。
具体原因:
- 在x86_64 System V调用约定(GCC在Linux、macOS等系统默认使用的规则)中,函数的前几个参数通过寄存器传递:
rdi存储第一个参数,rsi存储第二个参数。 main函数启动时,操作系统会把argc放入rdi寄存器,argv放入rsi寄存器。- 调用
test()时没有传递任何参数,这些寄存器的值刚好保留了main启动时的初始值,因此test函数读取rdi和rsi时,就拿到了argc和argv的值。
但这完全是巧合,不具备通用性:
- 如果在调用
test()之前调用了其他函数,那些函数可能会修改rdi/rsi寄存器,test就会拿到错误的值。 - 换用其他调用约定(比如Windows下的MSVC调用约定)或编译器(比如Clang在特定优化级别下),这个现象大概率会消失,甚至程序会崩溃。
- 标准C明确规定,调用函数时参数的数量和类型必须与声明匹配,否则属于未定义行为,编译器可以做出任何处理,包括看似“正常工作”的情况。
内容的提问来源于stack exchange,提问作者t2a
相关产品推荐
相关产品推荐

