向argv指针传参的三种方式问题:第三种为何触发段错误?
argv风格函数传参的三种方式及第三种段错误原因 我来帮你拆解这三种传递argv风格参数的方式,重点排查第三种方法的崩溃问题:
三种传参方式概述
方式一(TEST1):直接用字符串字面量初始化指针数组
代码简洁到没朋友,但因为C里字符串字面量是const char*类型,强制转成char**会触发C11以来的弃用警告(-Wc++11-compat-deprecated-writable-strings),要是开了-Werror直接编译失败——本质是字符串字面量是只读的,强转成可写指针本身就存在未定义行为的风险。方式二(TEST2):用
malloc分配内存后拷贝字符串
完全合法,既没编译警告也能正常运行,但代码确实有点啰嗦,还得手动管理内存的分配和释放,不够省心。方式三(TEST3):用二维字符数组传参 → 直接触发段错误
这是咱们重点解决的问题,先看你这段代码:char test_cmd[2][10]; memset(&test_cmd[0], 0, 10); memset(&test_cmd[1], 0, 10); memcpy(&test_cmd[0], "hello1", sizeof("hello1")); memcpy(&test_cmd[1], "hello2", sizeof("hello2")); int result = console_tester(2, (char**)test_cmd);
第三种方式段错误的根本原因
核心问题出在类型强制转换的不兼容,说直白点就是你把两种完全不同的内存结构硬凑到一起了:
首先,
char test_cmd[2][10]是一个二维字符数组,它在内存里是连续的20个字节:先存hello1\0(占7字节)+ 3个填充0,接着是hello2\0+3个填充0,整个数组是一块连续的内存块。而
console_tester期望的char**是指向char指针的指针——意思是它指向的内存里,存的是一串char*类型的指针,每个指针再各自指向对应的字符串缓冲区。当你把
test_cmd强制转换成char**时,程序会错误地把二维数组的起始地址当成char**来解析:argv[0]会被当成取test_cmd起始地址处的8字节(64位系统)作为指针,但这里实际存的是"hello1"的前8个字符('h' 'e' 'l' 'l' 'o' '1' '\0' '\0'),把这些乱码字节当成内存地址去访问,不触发段错误才怪。
修复第三种方式的正确做法
如果想用栈上的二维数组传参,有两个靠谱的解决方向:
方法1:修改函数参数适配二维数组
把console_tester的参数改成支持二维数组的类型:
// 两种写法都可以,本质是一样的 int console_tester(int argc, char argv[][10]) { printf("argv: %s\n", argv[0]); return 0; } // 调用时不需要任何强制转换 int result = console_tester(2, test_cmd);
方法2:用中间指针数组做适配
如果不能修改console_tester的参数类型,就在栈上建一个指针数组,指向二维数组的每一行:
char test_cmd[2][10]; memset(test_cmd[0], 0, 10); memset(test_cmd[1], 0, 10); strcpy(test_cmd[0], "hello1"); strcpy(test_cmd[1], "hello2"); // 中间指针数组,每个元素指向二维数组的对应行 char *argv[2] = {test_cmd[0], test_cmd[1]}; int result = console_tester(2, argv);
这样argv就是标准的char**类型,完美匹配函数参数,调用起来安全又顺畅。
内容的提问来源于stack exchange,提问作者rookie

