c语言笔记6-函数

1.函数的声明与定义:

程序中的声明可理解为预先告诉编译器实体的存在,如:变量,函数,等等

程序中的定义明确指示编译器实体的意义

2. 函数参数:

  函数参数在本质上与局部变量相同,都是在栈上分配空间

  函数参数的初始值是函数调用时的实参值

   函数参数的求值顺序依赖于编译器的实现,没有固定的求值顺序

C语言中大多数运算符对其操作数求值的顺序都是依赖于编译器的实现的

3. 程序中的顺序点:

 程序中存在一定的顺序点

 顺序点指的是执行过程中修改变量值的最晚时刻

 在程序达到顺序点的时候,之前所做的一切操作必须反映到后续的访问中

C语言中的顺序点:

 每个完整表达式结束时

 &&, ||, ?:, 以及逗号表达式的每个运算对象计算之后

 函数调用中对所有实际参数的求值完成之后(进入函数体之前)

1

#include <stdio.h>

int main()

{

   int k = 2;

   int a = 1;

   

    k= k++ + k++;

   

   printf("k = %d\n", k);

   

   if( a-- && a )

    {

       printf("a = %d\n", a);

    }

   

   return 0;

}

运行结果:k=6

2

#include <stdio.h>

int f(int i, int j)

{

   printf("%d, %d\n", i, j);//2,1

}

int main()

{

   int k = 1;

   f(k, k++);

   printf("%d\n", k);//2

   return 0;

}

运行结果:2,1,2

4. 函数的缺省认定:C语言会默认没有类型的函数参数为int

5. 可变参数:

 C语言中可以定义参数可变的函数

 参数可变函数的实现依赖于stdarg.h头文件

 va_list变量与va_start, va_endva_arg配合使用能够访问参数值

例:计算n个数平均值的函数

#include <stdio.h>

#include <stdarg.h>

float average(int n, ...)

{

   va_list args;

   int i = 0;

   float sum = 0;

   

   va_start(args, n);

   

   for(i=0; i<n; i++)

    {

       sum += va_arg(args, int);

    }

   

   va_end(args);

   

   return sum / n;

}

可变参数的限制

 可变参数必须从头到尾按照顺序逐个访问

 参数列表中至少要存在一个确定的命名参数

 可变参数宏无法判断实际存在的参数的数量

 可变参数宏无法判断参数的实际类型

警告:va_arg中如果指定了错误的类型,那么结果是不可预测的

6. 函数 VS

 宏是由预处理直接替换展开的,编译器不知道宏的存在

 函数是由编译器直接编译的实体,调用行为由编译器决定

 多次使用宏会导致程序代码量增加

 函数是跳转执行的,因此代码量不会增加

 宏的效率比函数要高,因为是直接展开,无调用开销

 函数调用时会创建活动记录,效率不如宏

宏的效率比函数稍高,但是其副作用巨大,容易出错

函数存在实参到形参的传递,因此无任何副作用,但是函数需要建立活动对象,效率受影响

宏参数可以是任何C语言实体

 宏编写的_MIN_参数类型可以是int,float等等

 宏的参数可以是类型名

#define MALLOC(type,n)(type*)malloc(n*sizeof(type))

 int*p = MALLOC(int,5);

7. 活动记录

 活动记录是函数调用时用于记录一系列相关信息的记录

 临时变量域:用来存放临时变量的值,如k++的中间结果

 局部变量域:用来存放函数本次执行中的局部变量

 机器状态域:用来保存调用函数之前有关机器状态的信息,包括各种寄存器的当前值和返回地址等;

 实参数域:用于存放函数的实参信息

 返回值域:为调用者函数存放返回值

8. 调用约定:

  调用约定是调用者和被调用者之间的调用协议,常用于不同开发者编写的库函数之间

 当一个函数被调用时,参数会传递给被调用的函数,而返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递到栈空间的,以及栈空间由谁维护。

 参数传递顺序

 从右到左依次入栈:__stdcall__cdecl__thiscall

 从左到右依次入栈:__pascal__fastcall

 调用堆栈清理

 调用者清除栈。

 被调用函数返回后清除栈

9. 递归函数

C递归函数有两个主要的组成部分:

 递归点–以不同参数调用自身

 出口–不在递归调用

C语言中的递归函数必然会使用判断语句

 递归函数在需要编写的时候定义函数的出口,否则栈会溢出

 递归函数是一种分而治之的思想

10. 函数设计技巧

 ①不要在函数中使用全局变量,尽量让函数从意义上是一个独立的功能模块,如果非要引用,则可以函数多加上一个参数来传递这个变量的值

 ②参数名要能够体现参数的意义

void str_copy (char*str1, char *str2);应改为:

void str_copy (char*str_dest, char *str_src);

③如果参数是指针,且仅作输入参数用,则应在类型前加const,以防止该指针在函数体内被意外修改

void str_copy (char*str_dest, const char *str_src);

④不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型

⑤在函数体的“入口处”,对参数的有效性进行检查,对指针的检查尤为重要

 ⑥语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁

  ⑦函数体的规模要小,尽量控制在80行代码之内

 ⑧相同的输入应当产生相同的输出,尽量避免函数带有“记忆”功能

 ⑨避免函数有太多的参数,参数个数尽量控制在4个以内

⑩有时候函数不需要返回值,但为了增加灵活性,如支持链式表达,可以附加返回值

 char s[64];

 int len =strlen(strcpy(s, “android”));

函数名与返回值类型在语义上不可冲突

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值