9.C语言-函数

9.C语言-函数1 了解函数什么是函数函数就是 C 语言的模块 一块一块的 有较强的独立性 可以相互调用 换句话说 C 语言中 一个函数里面可以调用 n 个函数 即大函数调用小函数 小函数又调用 小小 函数 这就是结构化程序设计 所以面向过程的语言又叫结构

大家好,欢迎来到IT知识分享网。

1.了解函数

什么是函数

  • 函数就是 C 语言的模块,一块一块的,有较强的独立性,可以相互调用,换句话说,C 语言中,一个函数里面可以调用 n 个函数,即大函数调用小函数,小函数又调用“小小”函数。这就是结构化程序设计,所以面向过程的语言又叫结构化语言。
  • 函数就是一系列 C 语句的集合,能完成某个特定的功能。需要该功能的时候直接调用该函数即可,不用每次都堆叠代码。需要修改该功能时,也只需要修改和维护这一个函数即可。

为什么需要函数

  • 将语句集合成函数的好处是方便代码重用。所谓“重用”,是指有一些代码的功能是相同的,操作是一样的,只不过针对的数据不一样,这时就可以将这种功能写成一个函数模块,以后用到这个功能时只需要调用这个函数模块就可以了,不需要再重复地编写同样的代码。这样可以解决大量同类型的问题,避免重复性操作。
  • 将语句集合成函数方便代码的维护。哪个功能出问题了,或者需要修改某个功能,那就只需要修改某个功能的函数就可以了。
return_type function_name( parameter list ) { body of the function } 
  • 返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void
  • 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • 函数主体:函数主体包含一组定义函数执行任务的语句。
#include<stdio.h> int add(x, y); void main() { int x, y; scanf_s("%d,%d", &x, &y); int sum = add(x, y); printf("%d", sum); } int add(int x, int y) { return x + y; } 

计算球体面积与体积

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> const double PI = 3.1415; double Area(int r) { return 4 * PI * r * r; } double Volume(int r) { return 4 / 3 * PI * r * r * r; } void main() { printf("地球面积:%f\n", Area(6371)); printf("地球体积:%f", Volume(6371)); } 

注:解决了代码重用

无参函数

通常用来执行一些功能比较固定单一的语句。

有参函数

通常通过处理传递过来的参数,将函数值返回给调用处。

写一个按键功能

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<Windows.h> void main() { //按下WIN键 keybd_event(VK_LWIN, 0, 0, 0); keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0); } 

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event

库函数

由C语言系统提供用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数定义的头文件

自定义函数

用户在程序中根据需要而编写的函数;

常用库函数

标准头名

功能

描述

<assert.h>

诊断

仅包含assert宏。可以在程序中使用该宏来诊断程序状态(例如某个变量是否为0等),若检查失败,程序终止。

<ctype.h>

字符处理

包含判断字符类型及大小写转换的函数。

<errno.h>

错误监测

提供了errno。可以在调用特定库函数后检测errno的值以判断调用过程中是否有错误发生。

<float.h>

浮点数特性

提供了描述浮点数特性的宏。

<limits.h>

整型特性

提供了描述整数类型和字符类型特性的宏。

<locale.h>

本地化

提供了一些支持程序国际化的函数。

<math.h>

数学计算

提供了大量用以数学计算的函数。

<setjmp.h>

非本地跳转

提供了用于绕过正常的函数返回机制,从一个函数跳转到另一个正在活动的函数的setjmp和longjmp函数。

<signal.h>

信号处理

提供了包括中断和运行时错误在内的异常情况处理函数。

<stdarg.h>

不定参数

提供了支持函数处理不变个数的参数的工具。

<stddef.h>

常用定义

提供了常用的类型和宏。

<stdio.h>

输入输出

提供了大量输入输出函数。

<stdlib.h>

常用实用函数

提供了大量实用的函数。

<string.h>

字符串处理

提供了大量字符串处理函数。

<time.h>

日期和时间

提供了获取、操纵和处理日期的函数。

注意顺序

void hi(); void main() { hi(); } void hi() { printf("HI..."); } 

默认返回int

hi() { printf("HI..."); return 0; } 

创建一个.h头文件

#include<stdio.h> void Hi() { printf("Hi..."); } 

载入自定义头

#include "hi.h" void main() { Hi(); } 

一个约束

头文件只放申明,实体放到c文件中

.h文件

#include<stdio.h> void Hi(); 

.c文件

#include "hi.h" void Hi() { printf("HI...."); } 

main调用

#include "hi.h" void main() { Hi(); } 

2.函数的调用

函数间通过参数来传递数据,即通过主调函数中的实际参数(实参)向被调用函数中的形式参数(形参)进行传递。

实参向形参传递数据的方式:实参将值单向传递给形参,形参的变化不影响实参值。

#include<stdio.h> #include<stdlib.h> int main() { int a, b; void swap(int a, int b); scanf_s("%d,%d", &a, &b); swap(a, b); printf("main:a=%d,b=%d\n", a, b); system("pause"); return 0; } void swap(int a, int b) { int t; t = a; a = b; b = t; printf("swap:a=%d,b=%d\n", a, b); } 

在main中,a,b的值没有交换。

9.C语言-函数

  • 形参交换了数据,而实参保持原数据不变。这是单向的值传递,所以形参的值改变后而实参的值没有改变。
  • 形参在函数中是变量名,在函数调用时,形参被临时分配相应的内存,调用结束后,形参单元被释放,而实参单元保留并维持原值。
  • 实参是表达式,负责向对应的形参标识的内存单元传递数据,实参向形参的数据传递是“值传递”。 实参与形参必须个数相同
  • 对应的形参和实参的类型必须一致

传入指针

形参为指向实参地址的指针,当对形参的指向操作时,就相当于操作实参本身。

#include<stdio.h> #include<stdlib.h> int main() { int a, b; void swap(int *a, int *b); scanf_s("%d,%d", &a, &b); swap(&a, &b); printf("main:a=%d,b=%d,&a=%p,&b=%p\n",a, b,&a,&b); system("pause"); return 0; } void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; printf("swap:a=%d,b=%d,&a=%p,&b=%p\n", *a, *b, a, b); } 

函数嵌套

#include<stdio.h> #include<stdlib.h> void Run(); void Do(); void Go(); int main() { Run(); system("pause"); return 0; } void Run() { printf("Run start...\n"); Do(); printf("Run end...\n"); } void Do() { printf("Do start...\n"); Go(); printf("Do end...\n"); } void Go() { printf("Go...\n"); } 
9.C语言-函数

注:这个调用是套娃试调用,每进入一个子函数,都得先将子函数执行完毕再回到父函数。

带返回函数

#include<stdio.h> #include<stdlib.h> int add(int x, int y); void main() { int x = add(10, 20);//int x = add(add(10, 20), 100); printf("%d\n", x); system("pause"); } int add(int x, int y) { return x + y; } 

参数顺序

void add(int x, int y) { printf("x=%d,y=%d", x, y); } void main() { int x = 100; add(x, ++x);//这里是先计算表达式,再调用函数 } 
void add(int x, int y,int z) { printf("x=%d,y=%d,z=%d", x, y,z); } void main() { int x = 100; add(x, ++x,--x);//这里是先计算表达式,再调用函数,从右向左计算 } 

3.可变参数

可变参数的函数必须至少有一个强制参数,可选参数的类型可以变化。可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定。

对于每一个强制参数来说,函数头部都会显示一个适当的参数,像普通函数声明一样。参数列表的格式是强制性参数在前,后面跟着一个逗号和省略号(…),这个省略号代表可选参数。

语法:

type funcName(paramType1 param1, ...) { // 执行语句... return val } 
#include<stdio.h> #include<stdlib.h> #include<stdarg.h> int add(int x,...);//可变参数 void main() { int x = add(5,1,2,3,4,5); printf("%d\n", x); system("pause"); } int add(int x,...) { va_list args;//创建一个char指针 va_start(args, x);//读取x个参数,把地址放到args中 int result = 0; for (int i= 0; i < x; i++) { result += va_arg(args, int);//逐一读取参数 } va_end(args);//清理va_list变量的内存 return result; } 

使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:

  • 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
  • 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
  • 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
  • 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
  • 使用宏 va_end 来清理赋予 va_list 变量的内存。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<stdarg.h> #include<string.h> void printString(int x,...);//可变参数 void main() { printString(5,"hi",",","this","is","test"); system("pause"); } void printString(int x,...) { va_list args;//存地址 va_start(args, x); for (int i = 0; i < x; i++) { char str[50]; strcpy(str, va_arg(args, char*));//读取字符串到char[] printf("%s ", str); } va_end(args); } 

4.递归

函数的递归调用是指:一个函数在他的函数体内直接或间接地调用它自身。分为:直接递归(函数直接调用自身)和间接递归(函数通过其他函数调用自身)。可分为“回溯”和“递推”两个阶段。

9.C语言-函数

一个简单例子

void run(int n) { printf("%d run...\n",n); if (n == 1) { return; } else { run(n - 1); } } void main() { run(5); } 

阶乘递归

int fac(int n) { int f; if (n < 0) { return 0; } else if (n == 0 || n == 1) { f = 1; } else { f = n * fac(n - 1); } return f; } void main() { int n = 5; printf("%d", fac(n)); } 

斐波那契数列

int fib(int i) { if (i == 0) { return 0; } if (i == 1) { return 1; } return fib(i - 1) + fib(i - 2); } void main() { int i; for (int i = 0; i < 20; i++) { printf("%d\n", fib(i)); } } 

递归的条件

每一个递归函数都应该只进行有限次的递归调用,否则它就会进入死胡同,永远也不能退出了,这样的程序是没有意义的。

要想让递归函数逐层进入再逐层退出,需要解决两个方面的问题:

  • 存在限制条件,当符合这个条件时递归便不再继续。对于 factorial(),当形参 n 等于 0 或 1 时,递归就结束了。
  • 每次递归调用之后越来越接近这个限制条件。对于 factorial(),每次递归调用的实参为 n – 1,这会使得形参 n 的值逐渐减小,越来越趋近于 1 或 0。

5.函数返回值生命周期

int add(int x, int y) { int z = x + y; printf("%p", &z); return z; } void main() { printf("%d", add(1, 2)); getchar(); } 

函数内部定义的变量,返回以后,变量就会被销毁

9.C语言-函数

修改成全局变量

int z = 0; int add(int x, int y) { z = x + y; printf("%p", &z); return z; } void main() { printf("%d", add(1, 2)); getchar(); } 

这时会发现内存不会销毁。

9.C语言-函数

int add(int x) { int z = 0; z = z + x; z = z + x; printf("%p", &z); return z; } void main() { printf("%d", add(3)); getchar(); } 

这里可以查看cpu寄存器内容。

6.几个注意事项

函数调用的时候,形参分配内存,新建一个变量,存储传递过来的实际参数的值会自动完成数据类型转换

void add(int x) { printf("x = %d\n", x); } void main() { add(12.5); } 

局部变量

  • 定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。
  • 在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时,main 函数中也不能使用其它函数中定义的变量。main 函数也是一个函数,与其它函数地位平等。
  • 形参变量、在函数体内定义的变量都是局部变量。实参给形参传值的过程也就是给局部变量赋值的过程。
  • 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。
  • 在语句块中也可定义变量,它的作用域只限于当前语句块。

全局变量

  • 在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。
int x = 10; void add(int x) { x = x + 100;//这里是局部变量 printf("x = %d\n", x); } void main() { add(12); printf("%d", x);//这里对应的全局x } 

一对大括号之间就是一个块区域,其中作用域也就是块区域中。

void main() { int x = 10; { int x = 99;//这里会屏蔽外面的x printf("%d\n", x); }; printf("%d\n", x); } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/165182.html

(0)
上一篇 2024-12-29 09:26
下一篇 2024-12-29 09:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信