函数(1)

简介: 函数(1)

1. 函数是什么?

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

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

在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,

subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组

成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。

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

件库。

2. C语言中函数的分类:

1. 库函数

2. 自定义函数

2.1库函数

为什么会有库函数?

1. 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定格式打印到屏幕上(printf)。2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)3在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。

像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,程序员进行软件开发。那怎么学习库函数呢?

这里我们简单的看看:www.cplusplus.com

简单的总结,C语言常用的库函数都有:

IO函数

字符串操作函数

字符操作函数

内存操作函数

时间/日期函数

数学函数

其他库函数                


2.1.1 如何学会使用库函数?

需要全部记住吗?No

需要学会查询工具的使用:

MSDN(Microsoft Developer Network)

www.cplusplus.com

http://en.cppreference.com(英文版)

http://zh.cppreference.com(中文版)

英文很重要。最起码得看懂文献。


使用库函数必须包含相应的头文件。

2.2 自定义函数

如果库函数能干所有的事情,那还要程序员干什么?

所有更加重要的是自定义函数。

自定义函数和库函数一样,有函数名,返回值类型和函数参数。

但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。

函数的组成:


我们举一个例子:

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

这个函数肯定要有一个返回值,并且是int类型,参数肯定是两个int类型的整形变量,函数名自己定义,那么函数体我们就能够轻易写出来了。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int get_max(int x, int y)
{
  //函数体
  if (x > y)  
    return x;
  else     
    return y;
}
int main()
{
  int num1 = 0;
  int num2 = 0;
  scanf("%d %d", &num1, &num2);
  printf("%d\n", get_max(num1, num2));
  return 0;
}

再举个例子:

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


我们交换两个值是不需要返回值的,所以返回值为void,交换两个变量我们需要创建一个临时变量来存储它们的值。但是为什么Swap1没有效果呢?因为这个函数没有返回值,函数里面的交换是不会在main函数里面实现的。main函数传递的是实参,Swap函数里面的的参数是形参,形参是实参的一份临时拷贝,对形参的修改,不会改变实参。所以只有当我们把实参的地址传递给形参,并且形参用指针来接受才能达到效果。

#define _CRT_SECURE_NO_WARNINGS 1
#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 是实际参数。

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


实参num1和num2,形参x,y使用的不是同一空间。


代码对应的内存分配如下:

这里可以看到 Swap1 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。

所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。


4. 函数的调用:

4.1 传值调用

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

4.2 传址调用

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

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

作函数外部的变量。

4.3 练习

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

首先我们遍历100-200之间的数,写一个函数判断这个数是不是素数,如果是素数,就打印这个数字,并且次数count++,接着我们在is_prime里面实现,这里我们用到bool函数,包含头文件stdbool.h.如果是素数返回true,不是则返回false,参数类型为int,拿2~sqrt(n)之间数字试除,就可以实现了。这里我们用到传值调用即可,因为我们只需要判断这个数是不是素数。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
//是素数返回1
//不是素数返回0
//C语言中有一个布尔类型
//C99 中引入的
//_Bool 类型的变量只有2种取值,true和false
bool is_prime(int n)
{
  //拿2~sqrt(n)之间数字试除
  int j = 0;
  for (j = 2; j <= sqrt(n); j++)
  {
  if (n % j == 0)
    return false;
  }
  return true;//是素数
}
int main()
{
  //打印100~200之间的素数
  int i = 0;
  int count = 0;
  for (i = 101; i <= 200; i+=2)
  {
  //判断i是否是素数?
  if (is_prime(i))
  {
    count++;
    printf("%d ", i);
  }
  }
  printf("\ncount = %d\n", count);
  return 0;
}

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

打印1000~2000年之间的闰年,使用闰年判断函数。

这里我们还是在main函数里面遍历1000~2000年,用is_leap_year来判断是否是闰年。也是用到bool函数,如果是闰年则返回ture,接下来就是我们熟悉的判断闰年的规则了。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
bool is_leap_year(int y)
{
  if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
    return true;
  else
    return false;
}
int main()
{
  int y = 0;
  int count = 0;
  for (y = 1000; y <= 2000; y++)
  {
    //判断y是否是闰年
    if (is_leap_year(y))
    {
      count++;
      printf("%d ", y);
    }
  }
  printf("\ncount = %d\n", count);
  return 0;
}

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

这里我们传参数组arr,查找的数字k,元素个数sz。返回类型为int,接下来我们用二分查找即可,如果找到了就返回这个数,找不到就返回-1。求两个数的平均值用(left+right)/2有时候是不可靠的,如果left和right都非常大,left+right就会超出整形的最大值,就会溢出。所以我们用left+(right-left)/2,画张图让大家理解一下。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int binary_search(int arr[], int k, int sz)
{
  int left = 0;
  int right = sz - 1;
  while (left<=right)
  {
    //int mid = (left + right) / 2;
    int mid = left + (right - left) / 2;
    if (arr[mid] < k)
    {
      left = mid + 1;
    }
    else if (arr[mid] > k)
    {
      right = mid - 1;
    }
    else
    {
      return mid;
    }
  }
  return -1;
}
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  int k = 0;
  scanf("%d", &k);
  int sz = sizeof(arr) / sizeof(arr[0]);
  int ret = binary_search(arr, k, sz);
  if (ret == -1)
  {
    printf("找不到\n");
  }
  else
  {
    printf("找到了,下标是:%d\n", ret);
  }
  return 0;
}

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

这里我们用到传址调用,每次+1即可。

void Add(int* p)
{
  *p = *p + 1;
}
int main()
{
  int num = 0;
  Add(&num);
  printf("%d\n", num);
  Add(&num);
  printf("%d\n", num);
  Add(&num);
  printf("%d\n", 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 链式访问

把一个函数的返回值作为另外一个函数的参数。这里我们把strcpy的返回值作为strlen的参数,又把strlen的返回值作为printf的参数,这就是链式访问。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
  char arr1[20] = {0};//abc\0.....
  char arr2[] = "abc";
  printf("%d\n", strlen(strcpy(arr1, arr2)));//链式访问
  return 0;
}


相关文章
|
网络协议
解决 curl: (7) Failed to connect to raw.githubusercontent.com port 443 ...
解决 curl: (7) Failed to connect to raw.githubusercontent.com port 443 ...
2779 1
|
9月前
|
Python
干货文:在 Mac 中卸载 Python 的方式
干货文:在 Mac 中卸载 Python 的方式
2411 1
|
9月前
|
数据库
什么是纯函数
纯函数是指在相同的输入下,总是返回相同的输出,且没有副作用的函数。具体来说,纯函数不会改变任何传入的参数,也不会在函数外部改变全局变量、文件系统、数据库等状态,它只是接收输入并返回输出,不会产生任何可观察的副作用。
100 0
|
3天前
|
人工智能 自然语言处理 Shell
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
仅用3分钟,百炼调用满血版Deepseek-r1 API,享受百万免费Token。阿里云提供零门槛、快速部署的解决方案,支持云控制台和Cloud Shell两种方式,操作简便。Deepseek-r1满血版在推理能力上表现出色,尤其擅长数学、代码和自然语言处理任务,使用过程中无卡顿,体验丝滑。结合Chatbox工具,用户可轻松掌控模型,提升工作效率。阿里云大模型服务平台百炼不仅速度快,还确保数据安全,值得信赖。
157353 24
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
|
5天前
|
人工智能 API 网络安全
用DeepSeek,就在阿里云!四种方式助您快速使用 DeepSeek-R1 满血版!更有内部实战指导!
DeepSeek自发布以来,凭借卓越的技术性能和开源策略迅速吸引了全球关注。DeepSeek-R1作为系列中的佼佼者,在多个基准测试中超越现有顶尖模型,展现了强大的推理能力。然而,由于其爆火及受到黑客攻击,官网使用受限,影响用户体验。为解决这一问题,阿里云提供了多种解决方案。
16994 37
|
13天前
|
机器学习/深度学习 人工智能 自然语言处理
PAI Model Gallery 支持云上一键部署 DeepSeek-V3、DeepSeek-R1 系列模型
DeepSeek 系列模型以其卓越性能在全球范围内备受瞩目,多次评测中表现优异,性能接近甚至超越国际顶尖闭源模型(如OpenAI的GPT-4、Claude-3.5-Sonnet等)。企业用户和开发者可使用 PAI 平台一键部署 DeepSeek 系列模型,实现 DeepSeek 系列模型与现有业务的高效融合。
|
5天前
|
并行计算 PyTorch 算法框架/工具
本地部署DeepSeek模型
要在本地部署DeepSeek模型,需准备Linux(推荐Ubuntu 20.04+)或兼容的Windows/macOS环境,配备NVIDIA GPU(建议RTX 3060+)。安装Python 3.8+、PyTorch/TensorFlow等依赖,并通过官方渠道下载模型文件。配置模型后,编写推理脚本进行测试,可选使用FastAPI服务化部署或Docker容器化。注意资源监控和许可协议。
1310 8
|
13天前
|
人工智能 搜索推荐 Docker
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
DeepSeek R1 + LobeChat + Ollama:快速本地部署模型,创建个性化 AI 助手
3416 117
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
|
8天前
|
人工智能 自然语言处理 API
DeepSeek全尺寸模型上线阿里云百炼!
阿里云百炼平台近日上线了DeepSeek-V3、DeepSeek-R1及其蒸馏版本等六款全尺寸AI模型,参数量达671B,提供高达100万免费tokens。这些模型在数学、代码、自然语言推理等任务上表现出色,支持灵活调用和经济高效的解决方案,助力开发者和企业加速创新与数字化转型。示例代码展示了如何通过API使用DeepSeek-R1模型进行推理,用户可轻松获取思考过程和最终答案。
|
5天前
|
人工智能 自然语言处理 程序员
如何在通义灵码里用上DeepSeek-V3 和 DeepSeek-R1 满血版671B模型?
除了 AI 程序员的重磅上线外,近期通义灵码能力再升级全新上线模型选择功能,目前已经支持 Qwen2.5、DeepSeek-V3 和 R1系列模型,用户可以在 VSCode 和 JetBrains 里搜索并下载最新通义灵码插件,在输入框里选择模型,即可轻松切换模型。
934 14

热门文章

最新文章