当前位置:网站首页>【C语言】函数哪些事儿,你真的get到了吗?(2)
【C语言】函数哪些事儿,你真的get到了吗?(2)
2022-08-02 13:04:00 【凡人编程传】
作者:凡人编程传
系列:C语言初阶(适合小白入门)
说明:以凡人之笔墨,书写未来之大梦
*前言
今天我们接着了解函数的知识,对上一节的知识进行更深入的一个补充,让大家能够更能知道如何了解设计函数,设计自定义函数的时候应该注意哪些地方,好了,咱们直接进入正题了!
*实参和形参
在上一节中的两个自定义函数的例子都可以知道在调用函数的时候都要传递参数,在定义函数时也要创建函数参数来,接受调用函数时传递的参数。具体情况请看上一节例子【C语言】函数哪些事儿,你真的get到了吗?(1).那么我们把函数调用时,传递的参数叫做函数的实际参数(实参),把定义函数时创建的参数叫做形式参数(形参)
具体概念:
1.实参:真是传给函数的参数,叫实参。实参可以是:常量,变量,表达式,函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些传给形参。
2.形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中被实例化(分配内存单元),所以叫做形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
*函数的调用
传值调用
函数的形参和实参分别占用不同的内存块(地址),对形参的修改不会影响实参。
这也就是上一节的交换两个变量例子的第一个写法为什么没有完成交换的根本原因.
可以看到实参a和b与形参x和y的内存块(地址)是不同的。所以通过改变形参x和y并不能改变实参a和b。
传址调用
1.传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
2.这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
这就是上一节中交换两个变量的第二个写法能达到交换变量的原因。
因为我们在传实参的时候直接把实参的地址传给了形参,因而形参和实参是同一块内存块(地址)所以能够改变实参。
*练习题
设计一个判断素数的函数
素数的判断方法我有在【C语言】夏日一题 —— 如何判断素数?这一节中具体讲述,这一节我们具体讲如何设计这个函数不讲算法。不知道的老铁可以去瞅一瞅。
有的知道算法的老铁可能咔咔马上就写出来如下代码:
#include<stdio.h>
#include<math.h>
void is_prime(int k)
{
int i = 0;
for(i = 2; i<sqrt(k); i++)
{
if(k % i==0)
{
break;
}
}
if(i>sqrt(k))
{
printf("%d是素数\n",k);
}
}
int main()
{
int n;
printf("请输入一个数:");
scanf("%d",&n);
is_prime(n);
return 0;
}
运行结果:
虽然说这样写的结果是对的,但是这不符合我们自定义函数的设计格式。我们这里是直接在函数内部进行了判断并且输出,但是我们这个函数的主要作用是判断一个数字是不是素数而不是输出,因为在后面工程项目中,有的程序员在调用使用你这个函数的时候只是需要这个函数来判断是不是素数,而不需要输出,如果在函数内部实现输出,那么函数就缺乏了独立性。所以我们应该把函数的结果返回一个数,让别人调用这个函数的时候,根据函数的返回值来判断是不是输出,如果需要就在主函数里面自行输出。
代码如下:
#include<stdio.h>
#inlclude<math.h>
int is_su(int a)
{
int i = 0;
for (i = 2; i < (int)sqrt(a); i++)
{
if (a % i == 0)
{
return 0;
}
}
return 1;
int main()
{
int n;
printf("请输入一个数:");
scanf("%d", &n);
if (is_su(n) == 1)
printf("%d是素数\n", n);
return 0;
}
设计一个函数在一个有序数组中二分查找
同样,二分查找这个算法在【C语言】虐打循环结构练习题有详细讲解,不知道的同学可以去看看。
我们设计的这个函数只需要把那个查找到的元素的下标返回即可,而最终查找到的那个元素的下标刚好是我们中间元素下标。当然也有可能查找不到那个元素,那么这个时候就返回一个 -1,因为数组下标不可能是负数,然后根据函数的返回值来判断是否在数组中找到即可。
这里我们直接上代码来研究:
#include<stdio.h>
int half_search(int a[],int key,int s) //这里为什么可以写成int a[]因为这里本来因果接收地址用指针int* a来接收的,而又因为数组可以写成*(a+n)来的指针形式(后续指针章节会讲),从而访问数组元素
{
int left = 0;
int right = s-1;
while(left <= right)
{
int mid=(left+right)/2;
if (a[mid] < key)
{
left = mid + 1;
}
else if (a[mid] > key)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[]={
1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
int k=7;
int t=half_search(arr,k,sz); //这里注意,arr是数组首元素地址传参,后续数组章节会讲到
if(t >= 0) //如果找到了,返回的数组下标肯定是大于等于0的
{
printf("找到了,下标是%d\n",t);
}
else
{
printf("找不到\n");
}
return 0;
}
运行结果:
很多同学这就会说了,为啥不可以把sz直接在函数内部算出来呢。这样不是给调用函数的人更方便?我们不妨假设让sz在函数内部实现试一试。
#include<stdio.h>
int half_search(int a[],int key) //这里为什么可以写成int a[]因为这里本来因果接收地址用指针int* a来接收的,而又因为数组可以写成*(a+n)来的指针形式(后续指针章节会讲),从而访问数组元素
{
int sz = sizeof(a)/sizeof(a[0]);
int left = 0;
int right = sz-1;
while(left <= right)
{
int mid=(left+right)/2;
if (a[mid] < key)
{
left = mid + 1;
}
else if (a[mid] > key)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[]={
1,2,3,4,5,6,7,8,9,10};
int k=7;
int t=half_search(arr,k); //这里注意,arr是数组首元素地址传参,后续数组章节会讲到
if(t >= 0) //如果找到了,返回的数组下标肯定是大于等于0的
{
printf("找到了,下标是%d\n",t);
}
else
{
printf("找不到\n");
}
return 0;
}
运行结果:
这次你会惊奇发现,明明7在数组里面却找不到,在函数里面实现sz就找不到了,还真是有趣,我们这时候不妨调试一番。
可以看到sz的值和right的值居然是1和0,说明:int sz = sizeof(a)/sizeof(a[0]);的结果为1,这是为什么呢?这明明是算数组个数啊,注意这条语句是能算出数组个数但不假,但是前提是这个a的类型是整个数组,可是看清楚那个形参a的类型是int* 也就是一级指针,指针的大小是4个字节(32位机器)/或者8个字节(64位机器),这里我的平台上是32位机器,故而是4个字节,那么sz=4/4也就是1,所以我们不能把这个sz在函数里面实现(不是完全不能,后面章节的知识学了就可以了).
*结言
好了,这一节的内容就到这里,期待下一节把,希望你有收获,下节更精彩!我们下节见!
边栏推荐
猜你喜欢
随机推荐
Automatically generate code generator recommendation-code-gen
一款强大的js弹出alert插件
30 lines of code to realize serverless real-time health code recognition -- operation manual
LeetCode_377_Combination Sum IV
如何通过DBeaver 连接 TDengine?
Scala基础语法入门(三)Scala中的各种运算符
photo-sphere-viewer Chinese documentation
高效代码静态测试工具Klocwork 2022.2——Portal全新升级、支持RLM
企业用直播平台能实现什么
SQL Server 2014 installation tutorial (nanny-level graphic tutorial)
pytorch model to tensorflow model
新特性解读 | MySQL 8.0 GIPK 不可见主键
First acquaintance of scrapy framework 1
A powerful js pop-up alert plugin
How to turn off hardware acceleration [easy to understand]
FreeRTOS--stack experiment
0801~ Interview questions
Openlayers 快速上手教程
RESTful style (detailed introduction + case implementation)
TFRecord简介,原理分析,代码实现?[通俗易懂]