你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言中return的一些使用技巧

[复制链接]
gaosmile 发布时间:2020-5-6 15:42

[导读] 喜闻C语言重登王座,作为一个C语言用的相对较多的码农,还是有点兴奋。本文来聊一聊,你可能没在意的C语言中return的一些使用技巧,分享给大家。
' q  Y* q  K- q/ C) L

return是啥

return其英文含义就是返回,用在函数中,退出当前函数。这个想必都知道,分两种情况:

  • 函数不带返回值时,如+ g: ]6 W8 ]8 s* t, r" n2 v1 o
void func(void)
2 @0 x+ ~/ l1 i. R" p3 s$ z& a{8 I# [; z* S; ^$ [
   /*功能代码*/
6 g; O0 _  L3 W2 W   ...
4 K. }- j7 j/ y9 O; Z% K$ L   return;
0 m8 j" k! ~; D/ k  d}
5 W! ]/ X# I- T% s/ B5 m* a' r6 b

对于不带返回值函数返回,其语法为 return;对于void函数,通常不显式加return语句,程序会自动返回到其调用者现场。

所以上述函数如下改写一下,效果是一样的:

void func(void)
0 z$ P0 z, f5 o{
0 }% p* b9 C9 i   /*功能代码*/
+ k  s: c7 X3 S; i6 }1 X+ }7 k   ...3 c0 e3 M) w  V3 \' r9 ?
}

+ B+ `7 R5 A" C
  • 函数带返回值,如
    - f# k8 l& Y6 i( c
int add(int a, int b)
- T- K5 b; H( [2 n% l  t# b# L{8 V, R% ~- |/ {/ v7 w: Y- H
    int result = a+b;
4 `/ b0 m/ N+ H% T$ [    return result;$ X$ c: p0 g6 C/ ^) P  a
}
* ?7 f; B# g# B) w; |

对于带返回值函数返回,其语法为 return <value>;返回值可以是一些几种情况:

  • 一个常数
  • 一个变量
  • 计算式,例如(a + b)* c
  • 调用另一个返回值的函数
    & J/ ], q3 p- F

注意:返回值的类型须与函数返回值类型一致,如不一致,编译器会自动做类型转换,这里容易出现类型转换的错误

上代码:

/*Case 1,返回常量*/6 p7 R0 L! c4 I+ ~, J# z( z% W
/*返回常量,-1表示参数非法,0表示成功*/, W' }4 [, ~8 D. H5 j6 N# T0 _
int swap(int *a, int *b)! ]3 z% v" b( Y0 \' `
{
' v) a. B7 c! l! Y, h/ L    int temp;" V8 k6 d5 I8 `
    if(a==NULL || b==NULL)
% F7 e; ]3 O; T5 C        return -1;
  f( Q) o' V' [0 y: }5 y3 d    else
+ \; v; h' \/ a* ~$ A  K- n2 n, [    {
0 ?; }' W* i) \. V" b        temp = *a;) f; x1 t  k/ W: T0 t" h: p# u
        *a = *b;
! R/ }$ Q8 ?5 s& H        *b = temp;
) z7 k8 q- V' n2 k' y+ Q        return 0;1 m0 f, }9 a7 h1 R7 Y/ b
    }
3 r$ `" n  i# e) w% m}
; ^3 E+ Q8 l) N: i1 ]" K5 b$ v0 ]! X3 l, n3 R
/*Case 2:返回一个变量*/; P7 H% f9 o( ~* Q! O
int add(int a, int b)
2 S) O1 }- n. S: V{8 k2 J! }$ x0 I8 R# N
    int result = a+b;
; I, C  e2 z, M7 r& K    return result;$ A) S( i& V. R+ o0 D# O
}
. V7 M9 J& E3 `. A: L( L9 q1 V, J9 O- `( S9 y
/* Case 3:返回一个表达式,add函数改写一下*/
/ M  I% i6 n% b7 {int add(int a, int b). L4 I. |; W( o2 W: C. F
{
1 ^& I. s4 L; f3 O/ g    return (a+b);
# Q3 U- T2 i- i2 X}* R4 \% M% i$ @4 B( l

( K7 g% g9 A9 L9 A% D/* Case 4:返回一个函数调用 */6 D% K0 U' s9 q% I, B0 r
/*STM32编程:是时候深入理解栈文章中提到的递归计算阶乘*/
* j7 v6 u/ u# Q3 i( I* y/ M0 R2 }float factorial(uint32_t n)8 n! w4 }$ K+ x9 b; l
{
  H# j& `( ?9 B4 N% ~2 ^, j7 A9 ~    uint32_t sp = __get_MSP();   
- |5 l6 S) \: D    /*记录栈指针的变化情况*/" e; @; R, F3 m7 O
    spSatte[spIndex++] = sp;9 D& u/ |! \3 Q. D6 `
    if(n==0 || n==1)3 W; x7 d: d0 q, x6 ?
        return 1;' M- i, ?* U  [
    else
) T5 p( E3 `/ e        return (float)n*factorial(n-1);
# J- e# M! o+ E' |" Q* N1 ~+ P2 ]4 d}

3 ?& |" _% C! ]; M& G& Hreturn 咋工作的?

首先需要了解函数调用是如何工作的。当调用一个函数时,会发生两件事:

  • 当前函数的执行暂停。
  • 调用的函数执行。  n) }" g& b1 ^. A, v+ @

这就是所谓的的控制权转移。当调用一个函数时,程序的控制从调用函数转移到被调用函数。return语句将控制权返回到前一个调用它的函数。该函数将从暂停的地方继续执行。

在调用处,如果查看汇编代码,单片机或处理器会将当前CPU的PC指针、临时变量、相关寄存器压栈,调用函数返回时,会将栈的内容弹出到相应的寄存器或者临时变量,以恢复现场。关于栈的解释可以参见号内文章:<<STM32编程:是时候深入理解栈>>

void 函数真不需要加return吗?

事实上不然,这就是本文希望引起注意的一些要点,在哪些场景下需要显式的对void函数添加return语句呢?

  • 传入指针为空指针,这也是很多面试官会考察的要点之一,这样处理会增强程序的健壮性。% E3 A* B1 v' }9 N& V
void swap(int *a, int *b)
7 p" X/ t5 r8 k9 o" F# _4 j{4 d5 c- W2 @, H# X! l+ V
    int temp;6 G# f) L6 @1 k- H, v# c9 R" J) A
    /*不可操作空指针*/# K  ~, h. b4 c; B# g  u8 e: f; o
    if(a==NULL || b==NULL)0 D' ^6 U  S+ F  C+ [+ _
        return ;
3 }( P& m; J$ H    else
8 k3 d0 @6 p. b: r) b8 W$ _    {
; t2 y  \, k2 v5 o! X: Y/ h        temp = *a;" t" T' G' R6 D! F
        *a = *b;; g$ s, d. b3 S+ v, X
        *b = temp;
5 e% o+ H9 J1 Y: u- ^" e    }4 r0 L7 B) U; F$ l
}% V: ^8 p4 q3 X, n

  x- j7 j: }* J' M3 k1 C
  • 传入参数非法或者越界
    ( z) N: J: f: c! }/ l( [
enum E_DAY{
5 x, P. C% H3 o2 Y' |/ |    E_MON=0, * T9 f0 Y: G: w
    E_TUS,5 t0 \8 s3 b1 d* y* d+ ?1 C& z7 k
    E_WNd, 8 R4 w0 l+ Q7 B. B4 p& u1 C- \9 @: H
    E_THR,, M6 c; e+ |6 p& e+ i0 X/ J
    E_FRD,
+ L% t6 i% ^' F8 v    E_SAT,
/ h0 F$ q* r" O* A% C    E_SUN
9 g2 q7 _0 o4 U% p3 w};
1 R+ k$ {8 R  {7 hvoid set_day(E_DAY day,E_DAY *pDay)
9 z1 \6 l8 _: T$ B* p{ ! m% q2 L7 j( A( ^* O: w+ q
    /* 这里就需要判别day参数是否非法越界 */2 R* e$ e' k2 u$ O* E+ C/ H  ~
    if(day>E_SUN || day<E_MON || pDay ==NULL)
. h0 v' [9 U& a% c     return;1 {! e7 t& @7 J1 z
     " i1 S3 a) |, W
    *pDay = day;' P4 N$ }" q2 I( f7 H. d5 h  I
}

) W) w  f$ w4 T
  • 检测到异常时需立即退出当前程序的执行流。
    9 ]9 m& X0 J- O/ V7 P
void func(void)
5 C7 G8 r8 N' t  C6 U: w" k% S{
( x5 ]% ]% D* n0 D# O( r    FILE *pFile=fopen("./test.csv","wt+");
3 E/ c. w6 q+ t' ]8 p7 o" r    /*这里就检测到文件无法打开的异常,则需要马上退出*/
5 ~2 J- f  o; e" J2 {    if(pFile==NULL)3 a3 ?) C; l0 u! ]3 z7 h7 \- W# r
    {
; C% [1 [  e) k        printf("file opened failed");
2 _2 P5 K& y" o/ W' o        return ;
6 o& \! A) E, y0 R    }0 Y' }8 k! B4 j

6 s' `. }7 P! Q& T( p* v    .....
' E8 P  e* B3 y& U( c
/ d0 K- r. c) t& I    fclose(pFile);# i6 Q& B5 J* M
}

6 L: A5 f4 _- K# G3 B: s- Z总结一下

因为容易忽略,其实也是初学者可能不太注意的一些地方,所以在此总结一下:

  • return的具体做了什么,其内在工作原理需要栈的支持
  • 不带返回值的函数,可不加return语句
  • 带返回值的函数,注意返回值的类型与函数返回值类型一致
  • void函数,在一些特定的场合加上return语句,可增加程序的健壮性:
    1 L! f. y. s0 V3 u+ i7 ?* W9 _
    • 传入指针为空指针
    • 传入参数非法或者越界
    • 检测到异常时需立即退出当前程序的执行流
    • 其实非void函数,也墙裂建议这么处理。: C1 L1 k6 Y0 q7 Y

2 S5 N3 j8 F8 {
赞 收藏 评论1 发布时间:2020-5-6 15:42

举报

1个回答
李康1202 回答时间:2020-5-7 09:04:47
谢谢分享
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版