龙空技术网

9.C语言-函数

数字双碳王亮 54

前言:

今天看官们对“c语言中输出函数”大约比较着重,同学们都想要剖析一些“c语言中输出函数”的相关知识。那么小编在网摘上搜集了一些有关“c语言中输出函数””的相关知识,希望大家能喜欢,小伙伴们一起来学习一下吧!

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

库函数

由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的值没有交换。

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

传入指针

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

#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");}

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

带返回函数

#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.递归

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

一个简单例子

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

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

修改成全局变量

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

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

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

标签: #c语言中输出函数 #c语言函数的组成 #c语言函数的组成是什么 #c语言程序设计1加到100怎么算 #c函数分文件编写