您现在的位置是:亿华云 > 数据库
C语言函数调用:【错误码】和【返回值】传递的小思考
亿华云2025-10-05 04:29:55【数据库】1人已围观
简介C 语言是一门面向过程的编程语言,通过一个又一个函数,把计算、过程控制等逻辑,包装成一个个独立的处理单元。既然是函数调用,就一定会有参数和返回值的传递问题,因此也就产生了多种不同的编程范式,比如:Po
C 语言是语言函用错一门面向过程的编程语言,通过一个又一个函数,数调思考把计算、误码过程控制等逻辑,和返回值包装成一个个独立的传递处理单元。
既然是语言函用错函数调用,就一定会有参数和返回值的数调思考传递问题,因此也就产生了多种不同的误码编程范式,比如:
Posix 风格:函数返回值只用来表示成功(0)或失败(非0),和返回值其他的传递输出结果都使用参数来传递。 Unix 风格:函数返回值即包括错误代码,语言函用错也包括有用的数调思考输出结果。 GAI 风格:与 Posix 有点类似,误码函数执行成功时返回0,和返回值否则就返回非0。传递这篇文章就来轻松一下,聊一聊这些函数调用范式在开发过程中的一些小思考。
我们假设有一个算法函数,输入两个整型参数,输出一个整型结果,并且输出一个错误代码。
第一种:输入、输出结果和错误码全部通过参数传递
既然所有的信息都是通过参数来传递的云南idc服务商,那么函数定义就应该是下面这样:
void func1(int a, int b, int *result, int *err_code) { int c = a + b; *result = c; err_code = 0; // 沿用 Linux 中的习惯,0 表示没有发生错误。 }因为不需要返回任何数据,因此函数签名的返回类型就是 void 。
因为调用者需要获取输出结果和错误码,因此在形参中, result和err_code需要传递指针类型的变量。
面对这样的函数签名,调用者就必须显示的定义两个变量result和err_code,用来接收函数的输出。
// 调用者代码 int result, err_code; func(1, 2, &result, &err_code); if (0 == err_code) printf("Success. result = %d \n", result); else printf("Failed. err_code = %d \n", err_code);这种函数范式的优点就是:在调用形式上统一,无论参数类型是什么(基础类型、结构体等待),都是整齐划一的函数调用写法。
缺点就是有点累赘。
面对任何一个函数,调用者都必须定义一个err_code变量传递进去。
如果一个函数是服务器托管过程控制类型的,压根就不会产生什么错误码,这样的函数调用就显得很臃肿,因为调用者压根就不需要检查错误码。
第二种:函数返回值表示错误码
也就是把第一种方式中的err_code参数,通过函数返回值赋值给调用者。
这种函数编程范式还是比较常见的,返回值只表示错误码,其他的输出结果都通过参数引用(指针)来传递。
int func2(int a, int b, int *result) { int c = a + b; *result = c; return 0; // 返回错误码 }这样的函数范式跟POSIX风格很像了。
面对这样的函数,调用者的写法就会变成这样:
// 调用者代码 int result, err_code; err_code = func2(1, 2, &result); if (0 == err_code) printf("Success. result = %d \n", result); else printf("Failed. err_code = %d \n", err_code);看起来好像跟第一种方式没有什么本质区别,但是再看一下下面这样的写法呢:
// 调用者代码 int result; if (0 == func2(1, 2, &result)) printf("Success. result = %d \n", result); else printf("Failed.\n");这样的代码风格,在Linux中是不是很常见?当不需要处理错误码时,这样的编程方式会更方便一些。
第三种:函数返回值表示输出结果
也就是把第一种方式中的源码库result参数,通过函数返回值赋值给调用者。
int func3(int a, int b, int *err_code) { int c = a + b; err_code = 0; return c; }这有点类似Unix中的风格:
返回结果中包括了有用的数据,但是它有一个局限:返回结果必须与错误码的类型一致。
另外还有一个问题:如果 int 型的返回结果也可能是负数, 所以 Unix 中还必须使用另一个全局变量 errno 来单独存储错误码,存在线程安全问题(可以使用线程局部存储来解决)。
面对这样的函数签名,调用者的调用方式如下:
// 调用者代码 int result, err_code; result = func3(1, 2, &err_code)) if (0 == err_code) printf("Success. result = %d \n", result); else printf("Failed.\n");这种方式的缺点与第一种一样:必须定义一个变量 err_code,来接收错误码。
在不必要检查错误码的场合中,显得有点多此一举。
小结
以上的这三种函数调用方式,没有好坏之分,只与每一位开发者的编码习惯有关系。
而且在实际的项目代码中,这三种方式都能看得到。
如果函数输出结果是结构体呢?
刚才讨论的三种方式中,函数输出结果reuslt是一个整型,如果它是一个结构体类型的变量,那么哪一种方式相对比较好呢?
这就要注意另外两点了:
结构体的赋值是需要时间开销的;
结构体赋值时,需要考虑深拷贝、浅拷贝的问题;
当看完以上几个小思考时,是不是觉得特别简单、不屑一顾?
不妨继续思考一步:在我们的实际编程过程中,是不是每次能够注意、遵守这些小细节问题呢?
如果团队中没有强制的代码规范,同事之间不会code review,我们是不是都会选择偷懒、放过自己呢?我就是^-^
本文转载自微信公众号「IOT物联网小镇」,可以通过以下二维码关注。转载本文请联系IOT物联网小镇公众号。
很赞哦!(91413)
相关文章
- 在更换域名后,并不是就万事大吉了,我们需要将旧域名做301重定向到新域名上,转移旧域名的权重到新域名上。
- 听说DNS根服务器只有13台,科学吗?
- 数据中心如何为飓风的到来做好准备
- 戴尔科技助力企业AI走可持续发展之路
- 公司在注册域名时还需要确保邮箱的安全性。如果邮箱不安全,它只会受到攻击。攻击者可以直接在邮箱中重置密码并攻击用户。因此,有必要注意邮箱的安全性。
- 通过添加新公钥加密和数字签名算法,抵御未来量子计算攻击
- 对数据中心停机说不,谈谈施耐德电气UPS产品的创新技术
- 初探 Azure 无服务器架构
- 在众多公司中,如果我们必须选择一家可信的公司,那当然是信得过的。
- 龙芯全新发布3C5000服务器处理器,用自主架构演绎超强算力