【C语法学习】函数 - 1

1 函数是什么

函数是已命名的、执行特定任务的独立C代码段。

  1. 函数是已命名的。每个函数都有独一无二的函数名,在程序中使用函数名,可以执行该函数中的语句,称为函数调用;
  2. 函数执行特定任务。例如pritnf(输出到屏幕)、scanf(读取键盘输入)、strlen(统计字符串长度)、strcmp(比较两字符串大小)等库函数都是实现某一项特定的功能;
  3. 函数是独立的。函数可独立执行任务,无需程序其他部分干预。

2 函数的分类

C语言中的函数可以分成两类:

  1. 库函数
  2. 自定义函数

2.1 库函数

C语言的库函数是指提供给开发人员使用的预定义函数,这些函数通过标准的C语言库提供。C语言的库函数可以加快开发过程,提供常见的功能和算法,以及通过封装底层操作系统接口实现与操作系统的交互。

一些常见的C库函数如下表所示,详情可参考:C语言标准库
在这里插入图片描述
需求:将一个字符串拷贝到另一个字符串中,并打印两个字符串,示例代码如下所示:

#include <stdio.h>
#include <string.h>

int main ()
{
   char str1[20] = { 0 };
   char str2[] = "hello world";

   strcpy(str1, str2);

   printf("%s\n", str1);
   printf("%s\n", str2);

   return 0;
}

2.2 自定义函数

自定义函数是由程序员自己编写的一段具有特定功能的代码块,通过给函数一个名称和参数列表,并在需要时调用该函数来执行特定的任务。

自定义函数通过将一系列的语句组合在一起,形成一个独立的模块,以便在程序中重复使用,提高代码的可维护性和重用性。

自定义函数的使用通常分为两个步骤:

  1. 函数定义:在程序的任意位置,通过编写函数的定义代码来创建一个自定义函数。
  2. 函数调用:在需要使用该函数的地方,通过函数名称和参数列表来调用函数,并执行其中的代码。

3 函数定义

自定义函数由函数头和函数体组成:

  1. 函数头包括函数名、返回类型以及形参列表;
  2. 函数体是由一对大括号括起来的代码块。

自定义函数的语法格式如下所示:

返回类型 函数名 (形参列表) 
{
    函数体
}

3.1 函数头

3.1.1 返回类型

函数的返回类型指定了函数返回值的数据类型,函数的返回值相当于函数的输出。

  1. 函数的返回类型可以是C语言的任意数据类型;
  2. 函数的返回类型可以是void,不返回任何值给调用程序;
  3. 函数的返回类型不是void的时候,函数的返回值只能有一个。

3.1.2 函数名

  1. 函数名的命名规则遵循C语言变量的命名规则;
  2. 函数名必须唯一,不能与其他函数和变量同名;
  3. 函数名应反应该函数的功能或用途。

3.1.3 形参列表

函数的形参列表指定了函数可接受的实参的类型和数量,形参相当于函数的输入。

  1. 函数的形参由形参类型和形参名组成,多个形参之间用逗号隔开;
  2. 函数的形参可以是C语言的任意数据类型;
  3. 函数的形参可以是void,不接收调用程序提供的任何实参。

3.2 函数体

  1. 函数体位于函数头后边的花括号中;
  2. 函数的功能是在函数体中实现的;
  3. 调用函数时,从函数体的第一条语句开始执行,直至函数体的最后一条语句或遇到return语句为止。

3.2.1 局部变量

  1. 函数体中声明的变量是局部变量,局部变量可以是任意类型,可以在声明时初始化局部变量;
  2. 局部变量属于函数私有,只能在函数内部使用,可以与函数外部的变量同名;
  3. 函数的形参列表可视为函数的局部变量声明,可在函数内部使用形参列表中的变量;
  4. 尽管在函数中可以使用全局变量,但是建议尽量不使用全局变量,因为局部变量使得函数的独立性更强,可以在不同的程序中移植或复用。

再次强调:在函数中声明的变量,完全独立于程序其他部分声明的变量,函数内外声明的变量可同名,示例代码如下图所示。

void demo (void)
{
   int x = 88;
   int y = 99;

   printf("Within         demo(), x = %d, y = %d\n", x, y);
}

int main()
{
   int x = 11;
   int y = 22;

   printf("Before calling demo(), x = %d, y = %d\n", x, y);
   demo();
   printf("After  calling demo(), x = %d, y = %d\n", x, y);

   return 0;
}

示例代码运行结果如下图所示:

在这里插入图片描述

实例代码运行结果分析:demo()函数内部的局部变量x和y完全独立于demo()函数外部的全局变量。

在函数中使用变量需遵循以下3条规则:

  1. 要在函数中使用变量,必须现在函数头或函数体中声明变量(全局变量除外);
  2. 要在函数中获得调用程序的值,必须将该值作为实参传递给函数;
  3. 要在调用程序中获得函数的值,必须将该值从函数中显示返回。

3.2.2 函数语句

  1. 在函数中可以使用任意C语言;
  2. 在函数中可以调用库函数和自定义函数;
  3. 在函数中唯一不能做的就是定义其他函数;
  4. 在结构化程序设计中,每个函数都应该只完成一个较简单的任务。

3.2.3 返回值

return 表达式;
  1. 关键字关键字右边的表达式可以是任何有效的C表达式;
  2. 函数使用return关键字将右边表达式的值返回给调用程序;
  3. 函数执行到return语句时,计算右边表达式的值,将计算结果返回调用程序;
  4. 函数可以包含多个return语句,只有第一个被执行的return语句有效。

示例1:没有return语句,代码如下图所示:

void print (void)
{
	printf("hello world");
}

示例代码分析:上述代码中,print()函数无参数,无返回值,无return语句。

示例2:有多条return语句,代码如下图所示:

int max (int a, int b)
{
   if (a > b)
   {
      return a;
   }
   else
   {
      return b;
   }
}

示例代码分析:上述代码中有两条return语句,根据形参a和b的大小关系,只有一条return语句会被执行。

4 函数原型

在C语言中,函数原型用于声明函数名称、参数类型以及返回类型。函数原型可以理解为函数的“说明书”,用于编译器在编译时进行函数匹配和类型检查。函数原型的一般格式如下所示:

返回类型 函数名 (形参列表);
  1. 在形式上:函数原型相当于函数头 末尾加上分号;
  2. 在功能上:函数原型将函数的基本情况告知编译器,编译器在编译时对函数的参数和返回值进行检查(函数实参的数量和类型是否与函数形参的数量和类型相符、函数返回值是否与返回类型相符);
  3. 在使用上:建议将程序所有函数的函数原型统一放在一个头文件中,便于更好地组织和管理程序中的函数,使代码更加清晰易懂。

5 函数传参

5.1 实参

实参即实际参数:

  1. 实参是函数调用程序传递给函数的实际值;
  2. 实参可以是任何有效的C表达式,例如常量、变量、数据表达式、逻辑表达式和有返回值的函数等;
  3. 每次调用函数时,传递给函数的实参类型和数量必须相同,但实参的值可以不同

实参是函数返回值示例:

x = half(third(square(half(y)))); // half()、square()和third()都是由返回值的函数
<=>
a = half(y);
b = square(a);
c = third(b);
x = half(c);

5.2 形参

形参即形式参数:

  1. 形参是只在函数内部有效的局部变量,它在函数被调用时自动创建(分配内存单元),在函数调用完后自动销毁(释放内存单元)。

5.3 传参

  1. 实参通过形参传入函数;
  2. 实参的值按照顺序依次复制到对应的形参中去;
  3. 实参和形参在类型和数量上要完全匹配。

我们知道,变量对应一段内存空间:

  1. 这段内存空间里存储的值就是变量的值,在程序中直接使用变量其实就是使用变量的值;
  2. 这段内存空间的起始地址就是变量的地址,在程序中需要用取地址运算符才能得到变量的地址;

5.3.1 传值

传值:

  1. 把变量的值作为实参传递给形参;
  2. 形参是实参在内存中的一份临时拷贝;
  3. 形参和实参完全独立;
  4. 对形参的操作不会对实参产生任何影响。

写一个函数交换两个变量的内容,代码如下所示:

void Swap(int x, int y)
{
   int z = 0;
   z = x;
   x = y;
   y = z;
}

int main()
{
   int a = 0;
   int b = 0;

   //输入
   printf("输入a和b的值:>\n");
   scanf("%d %d", &a, &b);
   //交换前
   printf("交换前:a=%d b=%d\n", a, b);
   //交换中
   Swap(a, b);
   //交换后
   printf("交换后:a=%d b=%d\n", a, b);
   //
   printf("\n");

   return 0;
}

代码运行结果如下图所示:

在这里插入图片描述

函数Swap并未实现交换变量a和b的内容的功能,代码及运行结果分析:

  1. 实参a和b只是将其值传递给形参x和y;
  2. 形参x和y只是实参a和b在内存中的一份拷贝;
  3. 形参x和y与实参a和b完全独立;
  4. 交换形参x和y的值不会对实参a和b造成任何影响。

5.3.2 传址

传址:

  1. 把变量的内存地址作为实参传递给形参;
  2. 传址可以让函数的形参和函数外部的变量之间建立起联系,即函数通过操作形参来操作函数外部的变量。

写一个函数交换两个变量的内容,代码如下所示:

void Swap(int* px, int* py)
{
   int z = 0;
   z = *px;
   *px = *py;
   *py = z;
}

int main()
{
   int a = 0;
   int b = 0;

   //输入
   printf("输入a和b的值:>\n");
   scanf("%d %d", &a, &b);
   //交换前
   printf("交换前:a=%d b=%d\n", a, b);
   //交换中
   Swap(&a, &b);
   //交换后
   printf("交换后:a=%d b=%d\n", a, b);
   //
   printf("\n");

   return 0;
}

代码运行结果如下图所示:

在这里插入图片描述
函数Swap能够实现交换变量a和b的内容的功能,代码及运行结果分析:

  1. 通过传址的方式将变量a和b的地址传递给函数Swap的形参px和py;
  2. 函数Swap通过操作px和py(即变量a和b的地址)来"远程"改变变量a和b的值。

5.3.3 总结

什么情况下传值?什么情况下传址?这里就只需关注一点,即函数是否需要改变外部变量的值;如果需要,就传址;如果不需要,就传值。

6 函数调用

6.1 嵌套调用

函数之间是可以互相调用的,称为函数的嵌套调用,示例代码如下:

void new_line(void)
{
   printf("hello world\n");
}

void three_line(void)
{
   int i = 0;
   for (i = 0; i < 3; i++)
   {
      new_line();
   }
}

int main()
{
   three_line();

   return 0;
}

代码运行结果如下:

在这里插入图片描述

代码及运行结果分析如下:

  1. main()函数调用three_line()函数,three_line()函数调用new_line()函数。

特别说明:函数可以嵌套调用,但是不能嵌套定义,即在一个函数的函数体中定义另外一个函数。

6.2 链式访问

一个函数的返回值作为另外一个函数的实参,称为函数的链式访问,示例代码如下所示:

int main()
{
   printf("%d\n", strlen("hello world"));
   printf("%d\n", printf("%d", printf("%d", 43)));

   return 0;
}

代码运行结果如下图所示:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值