在C和C++开发中,标准函数的return语句一次只能把一个对象交给调用者。但实际业务里经常需要同时拿到状态码、计算结果、长度信息等多个数据。要解决这个问题,并不是语言做不到,而是需要换一种数据传递思路。下面介绍几种在工程中稳定可用的做法,并给出可运行的代码示例。

使用结构体或类打包返回
最直观的方式是定义一个结构体,把要返回的多个字段都放进去,函数返回这个结构体实例。这样做语义清晰,调用方通过成员名取值,不易混淆顺序。
在C语言中,结构体是聚合数据的基本手段。下面例子用一个Result结构体同时携带商和余数:
#include <stdio.h>
typedef struct {
int quotient;
int remainder;
} DivResult;
DivResult divide(int a, int b) {
DivResult r;
r.quotient = a / b;
r.remainder = a % b;
return r;
}
int main() {
DivResult res = divide(17, 5);
printf("quotient=%d, remainder=%dn", res.quotient, res.remainder);
return 0;
}
C++中可以进一步用class并加上构造函数,或者使用std::tuple等模板类型。结构体的优点是类型稳定、字段命名明确,编译器也容易做返回值优化,避免不必要的拷贝。缺点是如果只偶尔返回两个临时值,定义新类型显得稍重。
通过指针或引用参数写回结果
另一种常见思路是函数本身返回状态码或主结果,其余数据通过指针(C)或引用(C++)形参传出来。函数内部给这些参数指向的变量赋值,调用方事先准备好变量地址。
C语言风格示例:
#include <stdio.h>
int divide(int a, int b, int* quotient, int* remainder) {
if (b == 0) return -1;
*quotient = a / b;
*remainder = a % b;
return 0;
}
int main() {
int q, r, ret;
ret = divide(17, 5, &q, &r);
if (ret == 0) {
printf("quotient=%d, remainder=%dn", q, r);
}
return 0;
}
C++推荐使用引用而非指针,能减少空指针风险,调用语法也更干净:
#include <iostream>
bool divide(int a, int b, int& quotient, int& remainder) {
if (b == 0) return false;
quotient = a / b;
remainder = a % b;
return true;
}
int main() {
int q, r;
if (divide(17, 5, q, r)) {
std::cout << "quotient=" << q << ", remainder=" << r << std::endl;
}
return 0;
}
这种方式的优势是无需构造聚合对象,特别适合已有变量直接接收结果的场景。问题在于接口不如返回值聚合那样自解释,调用时容易忘记传地址或传错变量。
C++专用:std::pair与std::tuple
如果返回的两个或三个值没有长期存在的业务含义,只是函数内部临时凑一组,C++标准库提供了pair和tuple。pair固定两个元素,tuple可容纳任意数量。
使用std::tuple的例子:
#include <iostream>
#include <tuple>
std::tuple<int, int> divide(int a, int b) {
return std::make_tuple(a / b, a % b);
}
int main() {
auto res = divide(17, 5);
std::cout << "quotient=" << std::get<0>(res)
<< ", remainder=" << std::get<1>(res) << std::endl;
return 0;
}
从C++17起还能用结构化绑定,让代码更接近多返回值语言的体验:
#include <iostream>
#include <tuple>
std::tuple<int, int> divide(int a, int b) {
return {a / b, a % b};
}
int main() {
auto [q, r] = divide(17, 5);
std::cout << "quotient=" << q << ", remainder=" << r << std::endl;
return 0;
}
pair和tuple轻量、无需预定义类型,在标准算法和临时计算中很方便。但元素通过位置访问,代码可读性弱于命名结构体,字段一多就容易记错顺序。
不同方案的选择建议
面对具体需求时,可参考下面的对照表快速决定:
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 返回固定业务对象多个属性 | 结构体/类 | 字段命名清晰,易维护 |
| C语言遗留接口 | 指针参数 | 无额外类型依赖 |
| C++需避免拷贝且要状态返回 | 引用参数 | 安全且语法简洁 |
| 临时聚合两三个值 | pair/tuple | 无需新类型,灵活 |
总体原则是把返回内容是否具备独立业务含义作为第一判断条件。有含义就打包成类型,无含义且数量少就用标准库容器。这样既能保证代码清楚,也能让后续维护者一眼看懂数据流向。