C 返回值与命令行参数
更新时间:2026-08-26。本文是
languages/c/主题入门层第 20 篇。函数主题收尾前的最后一课:返回值的细节和 main 的命令行参数。返回值看着简单,但"返回指针"和"返回大结构体"各有一个经典坑。
本文要回答的问题
return除了返回值,还有什么隐藏作用?- 为什么"返回局部变量的地址"是灾难?
- 命令行参数
argc、argv怎么用?
一、return 的三个用途
c
int f(int x) {
if (x < 0) return -1; // 用途1:提前返回(错误信号)
if (x > 100) return 100; // 用途2:钳制返回值
return x; // 用途3:正常返回
}- 返回结果(最常见);
- 提前终止:
return;在 void 函数里直接结束; - 错误信号:C 惯例用返回值表示成功/失败(0 成功,非 0 失败)。
第三个用途最容易被新手忽略——main 的 return 0; 就是这个惯例:shell 用 echo $? 读退出码,脚本用 if cmd; then 判断成败。程序出错时 return 非 0,是给操作系统的交代。
二、返回指针:别返回局部变量的地址
c
int *bad(void) {
int local = 42;
return &local; // 灾难!local 在函数返回时就销毁了
}函数返回后,栈帧弹出,local 占用的内存"还回去"了。返回的地址指向一块已失效的栈内存——之后任何函数调用都可能覆盖它,读出来的是垃圾。这叫悬垂指针(dangling pointer)。
正确做法:数据要"活过"函数返回,就放到堆上(malloc,后面讲)或由调用者提供缓冲区:
c
void good(int *out) { // 调用者提供空间
*out = 42; // 写进调用者的内存
}字符串常量例外:
const char *s = "hi";返回的是字符串常量区地址,一直有效("hi"在只读数据段)。但普通局部变量不行。
三、返回结构体:按值拷贝
c
typedef struct { int x; int y; } Point;
Point make_point(int x, int y) {
Point p = {x, y};
return p; // 整个结构体按值拷回去(拷贝开销)
}返回结构体是"值拷贝",完全合法。结构体大时拷贝有开销(几十上百字节的复制),编译器一般会优化成在调用者的栈上直接构造(RVO 的 C 版)。新手阶段放心 return 结构体,性能考量见高手层结构体与性能。
四、命令行参数:argc 与 argv
main 有两个参数接收命令行输入:
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
./args hello 42 world
# 输出:
# 参数个数: 4
# argv[0] = ./args
# argv[1] = hello
# argv[2] = 42
# argv[3] = world| 项 | 含义 |
|---|---|
argc | 参数个数(含程序名本身) |
argv[0] | 程序名(或路径) |
argv[1] ~ | 真正的命令行参数(都是字符串) |
argv[argc] | 保证是 NULL,可作哨兵 |
注意 argv 里的数字也是字符串:argv[2] 是 "42" 不是整数 42,要转换用 atoi(<stdlib.h>):
c
#include <stdlib.h>
int n = atoi(argv[2]); // "42" → 42五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 返回局部变量地址 | 读出来是垃圾 | 调用者提供缓冲区或 malloc |
忘 return 0 | 退出码随机 | main 结尾 return 0 |
| argv 数字当 int 用 | 类型错误 | 用 atoi/strtol |
| 越界访问 argv | 段错误 | 先检查 argc |
| 返回指针指向已释放内存 | 悬垂指针 | 管理好生命周期 |
六、与本站主线衔接
- 悬垂指针的机制与调试(gdb 看栈帧),见内存泄漏与悬垂指针;
- 栈帧生命周期、返回值的寄存器约定(EAX/RAX),见专家层内存布局与 ABI;
- 命令行参数解析的完整工具(getopt),见高手层命令行参数解析。
一句话总结
return 三用:返回结果、提前结束、错误信号(0 成功非 0 失败);别返回局部变量的地址(悬垂指针),数据要活过函数就用堆或调用者缓冲;argc/argv 拿到命令行参数,注意数字参数是字符串,用 atoi 转换。