Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C语言函数调用:【错误码】和【返回值】传递的小思考

C语言函数调用:【错误码】和【返回值】传递的小思考

作者头像
IOT物联网小镇
发布于 2022-02-09 01:09:00
发布于 2022-02-09 01:09:00
2.9K00
代码可运行
举报
文章被收录于专栏:IOT物联网小镇IOT物联网小镇
运行总次数:0
代码可运行

目录

  • 第一种:输入、输出结果和错误码全部通过参数传递
  • 第二种:函数返回值表示错误码
  • 第三种:函数返回值表示输出结果
  • 小结
  • 如果函数输出结果是结构体呢?

C 语言是一门面向过程的编程语言,通过一个又一个函数,把计算、过程控制等逻辑,包装成一个个独立的处理单元。

既然是函数调用,就一定会有参数和返回值的传递问题,因此也就产生了多种不同的编程范式,比如:

  1. Posix 风格:函数返回值只用来表示成功(0)或失败(非0),其他的输出结果都使用参数来传递。
  2. Unix 风格:函数返回值即包括错误代码,也包括有用的输出结果。
  3. GAI 风格:与 Posix 有点类似,函数执行成功时返回0,否则就返回非0。

这篇文章就来轻松一下,聊一聊这些函数调用范式在开发过程中的一些小思考。

我们假设有一个算法函数,输入两个整型参数,输出一个整型结果,并且输出一个错误代码。

第一种:输入、输出结果和错误码全部通过参数传递

既然所有的信息都是通过参数来传递的,那么函数定义就应该是下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void func1(int a, int b, int *result, int *err_code)
{
    int c = a + b;
    *result = c;
    err_code = 0;  // 沿用 Linux 中的习惯,0 表示没有发生错误。
}

因为不需要返回任何数据,因此函数签名的返回类型就是 void 。

因为调用者需要获取输出结果和错误码,因此在形参中, resulterr_code需要传递指针类型的变量。

面对这样的函数签名,调用者就必须显示的定义两个变量resulterr_code,用来接收函数的输出。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 调用者代码

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参数,通过函数返回值赋值给调用者。

这种函数编程范式还是比较常见的,返回值只表示错误码,其他的输出结果都通过参数引用(指针)来传递。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int func2(int a, int b, int *result)
{
    int c = a + b;
    *result = c;
    return 0;   // 返回错误码
}

这样的函数范式跟POSIX风格很像了。

面对这样的函数,调用者的写法就会变成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 调用者代码

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);

看起来好像跟第一种方式没有什么本质区别,但是再看一下下面这样的写法呢:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 调用者代码

int result;
if (0 == func2(1, 2, &result))
    printf("Success. result = %d \n", result);
else
    printf("Failed.\n");

这样的代码风格,在Linux中是不是很常见?当不需要处理错误码时,这样的编程方式会更方便一些。

第三种:函数返回值表示输出结果

也就是把第一种方式中的result参数,通过函数返回值赋值给调用者。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int func3(int a, int b, int *err_code)
{
    int c = a + b;
    err_code = 0;
    return c;
}

这有点类似Unix中的风格:

  1. 返回结果中包括了有用的数据,但是它有一个局限:返回结果必须与错误码的类型一致。
  2. 另外还有一个问题:如果 int 型的返回结果也可能是负数, 所以 Unix 中还必须使用另一个全局变量 errno 来单独存储错误码,存在线程安全问题(可以使用线程局部存储来解决)。

面对这样的函数签名,调用者的调用方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 调用者代码

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是一个整型,如果它是一个结构体类型的变量,那么哪一种方式相对比较好呢?

这就要注意另外两点了

  1. 结构体的赋值是需要时间开销的;
  2. 结构体赋值时,需要考虑深拷贝、浅拷贝的问题;
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IOT物联网小镇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C语言函数大全--p 开头的函数
在上述的示例中,主线程调用了 pthread_create() 函数来创建一个新的线程,并传递一个函数指针 thread_func 作为新线程的入口点。新线程运行这个函数,并输出一条消息。主线程等待新线程结束,然后继续运行自己的代码。
huazie
2025/05/15
2000
C语言函数大全--p 开头的函数
C语言函数大全--m 开头的函数(下)
上述的示例代码,演示了如何使用 mmap() 函数将一个文件映射到内存中,并使用指针 ptr 访问这个映射区域 :
huazie
2025/05/10
1620
C语言函数大全--m 开头的函数(下)
Go基础——function函数
我们以写一个计算商品价格的函数为例,输入参数是单件商品的价格和商品的个数,两者的乘积为商品总价,作为函数的输出值。
羊羽shine
2019/05/28
6720
C语言总结_函数知识
这篇介绍C语言里函数的定义、子函数的使用。接着介绍变量的作用域,定义全局变量、静态变量、只读变量、变量初始化等等。
DS小龙哥
2022/05/17
3940
Rust学习笔记Day21 为什么Rust的错误处理与众不同?
在 C 语言中,如果 fopen(filename) 无法打开文件,会返回 NULL,调用者通过判断返回值是否为 NULL,来进行相应的错误处理。
用户1072003
2023/02/23
6830
Rust学习笔记Day21 为什么Rust的错误处理与众不同?
学习go语言编程之函数
函数的基本组成:关键字func,函数名,参数列表,返回值,函数体,返回语句。 示例如下:
编程随笔
2023/10/15
1860
【C语言】return 关键字详解 -《回家的诱惑 ! 》
return 关键字在 C 语言中用于终止函数的执行,并将控制权返回给调用者。根据函数的类型,return 还可以返回一个值。它是函数控制流中的重要组成部分。
LuckiBit
2024/12/11
1990
【C语言】return 关键字详解 -《回家的诱惑 ! 》
通过RealSense代码说明一些C语言问题
以前也读了这个RGBD相机的一些源码但是发现自己的基本功是一点也不好,所以就搁置了很久,今天试图来回答一些问题。
云深无际
2023/05/24
6000
通过RealSense代码说明一些C语言问题
Go语言错误码设计与管理实践
最近在做一个和前端、第三方平台(可以简单理解为公司别的部门或者客户软件)直接交互的服务,涉及到用户注册、登录、数据处理等模块。架构图大概如下:
xin猿意码
2023/10/18
8880
Go语言错误码设计与管理实践
Go语言必须支持多返回值函数,你知道为什么吗?
大多数编程语言的函数(方法)都只能返回一个值,这种函数也是在数学中的标准定义,如y = f(x),后面的f(x)不管多复杂,y永远只有一个。不过有少数编程语言,函数可以返回多个值,Go和Python就是这样的语言。其实这种返回多值的方式对于有些编程语言,如Python,就是一个语法糖。不过对于go来说,还真需要,那么为什么需要呢?继续往后看吧!
蒙娜丽宁
2021/11/30
1.7K0
Go语言必须支持多返回值函数,你知道为什么吗?
C++异常
(1)错误码不好设置。比如除0时,我们不好设置这个返回数,因为并不知道返回的是结果还是错误码。
小灵蛇
2024/06/06
1310
C++异常
降本增笑的P0事故背后,是开猿节流引发的代码异常吗?
软件开发中遇到异常才是正常,很少有人能写出完美的程序跑在任何机器上都不会报错。但极为正常的软件异常,却经常出自不同的原因,导致不同的结果。怎么样科学地认识异常、处理异常,是很多研发同学需要解决的问题。本文作者根据自己多年的工作经验,撰写了《异常思辨录》系列专栏,希望能体系化地帮助到大家。本文为系列第一篇,本篇文章将主要聚焦异常处理的几种方式展开,欢迎阅读。
腾讯云开发者
2023/12/06
1.2K3
降本增笑的P0事故背后,是开猿节流引发的代码异常吗?
另一种 C++ 程序错误处理方式
C++ 是一个很灵活的语言,这把双刃剑一方面使得 C++ 有很强大的表达能力,但也使得其编程风格相当混乱,就连错误处理到底是使用错误码还是异常都常常争论不休。例如在 C 中我们默认用错误码处理错误,而在 Python、Java 中, 则默认用异常来处理错误。而在 C++ 中,使用这两种形式的错误处理形式都有,而目前来看,在我所在的团队中,除非是外部库,否则基本都是使用错误码。在这篇文章中,我将聊一下 C++ 错误处理的方式优劣,以及我们团队是如何进行 C++ 错误处理的。
zhiruili
2021/08/10
1.1K0
Go 函数式编程篇(二):参数传递、变长参数与多返回值
上篇教程我们介绍了 Go 语言中函数的基本定义和调用,其中也涉及到了函数的传参和返回值,只不过那里演示的是最简单的场景,今天我们就更复杂的传参和返回值进行介绍。
学院君
2023/03/03
1.7K0
Go 函数式编程篇(二):参数传递、变长参数与多返回值
《Go语言程序设计》读书笔记(二)函数
《Go 语言程序设计》在线阅读地址:https://yar999.gitbooks.io/gopl-zh/content/
KevinYan
2019/12/30
4630
C语言函数
风中的云彩
2024/11/07
2250
C语言函数
Go函数及与函数相关机制 【Go语言圣经笔记】
函数可以让我们将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用。函数的机制可以让我们将一个大的工作分解为小的任务,这样的小任务可以让不同程序员在不同时间、不同地方独立完成。一个函数同时对用户隐藏了其实现细节(黑盒特性)。由于这些因素,对于任何编程语言来说,函数都是一个至关重要的部分。
Steve Wang
2021/12/06
1.2K0
C 语言函数完全指南:创建、调用、参数传递、返回值解析
例如,main() 是一个函数,用于执行代码,printf() 也是一个函数,用于向屏幕输出/打印文本:
小万哥
2024/01/07
5710
C 语言函数完全指南:创建、调用、参数传递、返回值解析
C++常见问题(二)——虚函数、类与结构、引用与值传递
本文介绍了C++中的虚函数、类与结构、异常处理、宏定义、存储空间、虚函数与纯虚函数、引用与值传递、类和结构以及成员变量的访问控制等方面的知识。
码科智能
2018/01/03
9420
C语言:跨平台环境下使用snprintf,vsnprintf系列函数要注意返回值的问题
标准C语言函数snprintf,vsnprintf系列函数可以向指定的缓冲区输出格式化打印的字符串。 如果指定的缓存区足够大,那么调用正常,返回值就是写入缓存区的字节长度(不含结尾'\0') 那么缓存区不够大的情况呢? 本文要说的是这系列函数的在缓存区长度不足以输出所有内容时的返回值在不同一编译器提供的实现表现是不同的。 我们用如下一段简单的测试代码来验证其返回值表现。
10km
2021/11/11
2K0
C语言:跨平台环境下使用snprintf,vsnprintf系列函数要注意返回值的问题
推荐阅读
相关推荐
C语言函数大全--p 开头的函数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档