从零带你认识函数(一)

简介: 从零带你认识函数

1.函数是什么?


数学中我们常见到函数的概念。但是你了解C语言中的函数吗?

维基百科中对函数的定义:子程序


1.在计算机科学中,子程序,是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。

2.一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。


2.C语言中函数的分类


在C语言中函数主要分为两种,一个是自带的库函数,还有就是我们自定义的函数。


2.1.库函数


像我们熟悉的printf()函数,scanf()函数就是C语言自带的函数,我们在使用它的时候只需要#include包含对应的头文件即可,使用printf()、scanf()函数包含stdio.h即可。

如何学习查找需要的库函数?

这里给大家分享个网站,供大家学习。

链接: www.cplusplus.com


415f9927c5da487181d1661c90f36264.png


我们在search栏里搜索memset的库函数,我们来看看结果:


ba7791648c704fb7b6c57faa27ff51ae.png


上面有函数的各个参数以及返回值等的介绍,如果英文看着难受也可以翻译成中文来学习,我们来看最下面,最下面非常直观的给出了一个例子,包括头文件的引用,以及输出结果。


2.2.自定义函数


我们要知道并不是所有的事库函数都能干。很多时候都要自己设计一个函数来完成要求,这便是自定义函数。

自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。

函数的组成:


ret_type fun_name(para1, * )
{
  statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数


我们举一个例子:


写一个函数可以找出两个整数中的最大值。


#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
  return (x>y)?(x):(y);     
}
int main()
{
  int num1 = 10;
  int num2 = 20;
  int max = get_max(num1, num2);
  printf("max = %d\n", max);
  return 0;
}


再举个例子:


写一个函数可以交换两个整形变量的内容。


#include <stdio.h>
//实现成函数,但是不能完成任务
void Swap1(int x, int y)         //只改变了形参的值,实参并未发生变化。
{                                
  int tmp = 0;
  tmp = x;
  x = y;
  y = tmp;
}
//正确的版本
void Swap2(int *px, int *py)    //通过变量地址解引用来找到实参,并将其改变。
{
  int tmp = 0;
  tmp = *px;
  *px = *py;
  *py = tmp;
}
int main()
{
  int num1 = 1;
  int num2 = 2;
  Swap1(num1, num2);
  printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
  Swap2(&num1, &num2);
  printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
  return 0;
}


3.函数的参数


3.1.实际参数(实参):


真实传给函数的参数,叫实参。

实参可以是:常量、变量、表达式、函数等。

无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形

参。


3.2.形式参数(形参):


形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内

存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。


在上面代码中:Swap1 和 Swap2 函数中的参数 x,y,px,py 都是形式参数。在main函数中传给 Swap1 的 num1 ,num2 和传给 Swap2 函数的 &num1 , &num2 是实际参数。

这里我们对函数的实参和形参进行分析:


41e7e6ec65fc444abc4d6ded6b1cd525.png


这里可以看到 Swap1 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。

形参px、py存放的是num1、num2的地址,我们可以通过解引用来找到num1、num2并进行操作。


4.函数的调用


4.1.传值调用


函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。


4.2.传址调用


传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操

作函数外部的变量。


4.3. 练习


1.写一个函数可以判断一个数是不是素数。


void f(int m)
{
  int i = 0;
  int p = 0;
  for (i = 2; i < m; i++)
  {
  if (m % i == 0)
  {
    printf("不是素数\n");
    p++;
    break;
  }
  }
  if (p == 0)
  printf("是素数\n");
}


2.写一个函数判断一年是不是闰年。


void jud(int x)
{
  if ((x % 100 != 0 && x % 4 == 0) || (x % 400 == 0))
  printf("是闰年\n");
  else
  printf("不是闰年\n");
}


3.写一个函数,实现一个整形有序数组的二分查找。

(要求:找到了就打印数字所在的下标,找不到则输出:找不到。)


#include<stdio.h>
void f(int arr[],int b)
{
  int a = 0;
  int c = 0;
  int m = 0;
  scanf("%d", &c);     //输入要查找的数
  while (a <= b)
  {
  m = (a + b) / 2;
  if (c > arr[m])
  {
    a = m + 1;
  }
  else if (c < arr[m])
  {
    b = m - 1;
  }                     //为什么要m+-1呢?
  else                  //如果要查找的数不在数组内,那a一直+1或b一直-1,直到a>b退出循环
  {
    printf("找到了,下标为:%d\n", m);
    break;
  }
  }
  if (a > b)
  printf("找不到\n");
}
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  int b = sizeof(arr) / sizeof(arr[0]) - 1;
  f(arr,b);
  return 0;
}


4.写一个函数,每调用一次这个函数,就会将 num 的值增加1。


void f(int* m)
{
  (*m)++;
}
int main()
{
  int num = 0;
  //调用函数,使得num每次增加1
  f(&num);
  f(&num);
  printf("%d", num);
  return 0;
}


5. 函数的嵌套调用和链式访问


函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。


5.1. 嵌套调用


#include <stdio.h>
void new_line()
{
  printf("hehe\n");
}
void three_line()
{
  int i = 0;
  for(i=0; i<3; i++)
  {
  new_line();    //该函数被调用三次
  }
}
int main()
{
  three_line();
  return 0;
}


函数可以嵌套调用,但是不能嵌套定义(函数里面不能定义函数)。


5.2. 链式访问


把一个函数的返回值作为另外一个函数的参数。


#include <stdio.h>
#include <string.h>
int main()
{
  char arr[20] = "hello";
  int ret = strlen(strcat(arr,"bit"));//strlen函数是将后面的字符追加到前面的字符串后面,返回值是前面字符串的起始地址                   
  printf("%d\n", ret);               //strlen函数是计算字符串的长度并返回长度大小
  return 0;
}


输出:8


#include <stdio.h>
int main()
{
  printf("%d", printf("%d", printf("%d", 43)));
  //结果是啥?
  //注:printf函数的返回值是打印在屏幕上字符的个数
  return 0;
}


输出:4321

为什么是4321呢,我们来看最右边printf,它打印43并返回2,然后中间的printf,打印2并返回1,然后左边的printf,打印1。三个打印就是4321了。


6. 函数的声明和定义


6.1 函数声明:


  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
  2. 声明决定不了。
  3. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
  4. 函数的声明一般要放在头文件中的


6.2 函数定义:


函数的定义是指函数的具体实现,交待函数的功能实现。


test.h的内容

放置函数的声明


#ifndef __TEST_H__
#define __TEST_H__
    //函数的声明
int Add(int x, int y);


test.c的内容

放置函数的实现


#include "test.h"      //包含头文件
    //函数Add的实现
int Add(int x, int y)
{
  return x+y;
}


在三子棋和扫雷时已经使用过这种书写形式。


相关文章
|
存储 监控 安全
推荐5款极具效率的实用工具软件
每次分享实用的软件,都会给人一种踏实和喜悦的感觉,这也是我热衷于搜集和推荐高效工具软件的原因。
341 1
|
安全 Linux 调度
在Linux中,如何实现,每星期天早8点服务器定时重启?
在Linux中,如何实现,每星期天早8点服务器定时重启?
|
10月前
|
关系型数据库 MySQL 数据处理
【MySQL】函数
MySQL 提供了丰富的函数集,涵盖字符串处理、数值运算、日期时间操作和聚合计算等多个方面。这些函数在日常数据库操作中极为重要,通过合理使用这些函数,可以大大提高数据处理和查询的效率。用户还可以通过自定义函数,扩展 MySQL 的功能以满足特定需求。
214 3
|
12月前
|
XML 数据采集 API
MechanicalSoup与BeautifulSoup的区别分析
MechanicalSoup与BeautifulSoup的区别分析
152 2
MechanicalSoup与BeautifulSoup的区别分析
|
12月前
|
机器学习/深度学习 存储 人工智能
揭秘机器学习背后的神秘力量:如何高效收集数据,让AI更懂你?
【10月更文挑战第12天】在数据驱动的时代,机器学习广泛应用,从智能推荐到自动驾驶。本文以电商平台个性化推荐系统为例,探讨数据收集方法,包括明确数据需求、选择数据来源、编写代码自动化收集、数据清洗与预处理及特征工程,最终完成数据的训练集和测试集划分,为模型训练奠定基础。
290 3
|
11月前
|
存储 Oracle NoSQL
【赵渝强老师】Oracle的体系架构
Oracle数据库的核心在于其体系架构,主要包括数据库与实例、存储结构、进程结构和内存结构。数据库由物理文件组成,实例则是内存和进程的组合。存储结构分为逻辑和物理两部分,进程结构涉及多个后台进程如SMON、PMON、DBWn等,内存结构则包含SGA和PGA。掌握这些知识有助于更好地管理和优化Oracle数据库。
326 7
|
Rust 开发者
揭秘Rust编程:模块与包的终极对决,谁将主宰代码组织的新秩序?
【8月更文挑战第31天】在软件工程中,模块化设计能显著提升代码的可读性、可维护性和可重用性。Rust 作为现代系统编程语言,其模块和包管理机制为开发者提供了强有力的工具来组织代码。本文通过对比模块和包的概念及使用场景,探讨了 Rust 中的最佳实践。
173 2
|
XML Java 测试技术
Java异常处理神器:Guava Throwables类概念与实战
【4月更文挑战第29天】在Java开发中,异常处理是保证程序稳定性和可靠性的关键。Google的Guava库提供了一个强大的工具类Throwables,用于简化和增强异常处理。本篇博客将探讨Throwables类的核心功能及其在实战中的应用。
215 2
R语言错误处理与调试:如何高效调试R代码
【8月更文挑战第28天】调试R代码是一项需要不断练习和提高的技能。通过理解常见的错误类型、使用`traceback()`查看错误路径、逐步执行代码、利用`tryCatch()`捕获和处理错误、设置更严格的警告级别、利用RStudio的调试工具以及编写可复现的示例,你可以更加高效地调试R代码,并快速解决遇到的问题。
|
Rust Cloud Native 安全
哇塞!Rust 在云原生环境中搞大事啦!构建微服务竟如此酷炫,你还不来看看?
【8月更文挑战第31天】《构建微服务:Rust 在云原生环境中的实践》探讨了 Rust 语言凭借其内存安全、高性能及可靠性等特性,在快速发展的云计算领域构建微服务的优势。书中介绍了选择合适框架(如 Axum 和 Tide)、容器化部署、服务间通信及确保服务可靠性等方面的内容,并展示了 Rust 在云原生环境中的广泛应用前景。
609 1