大家好,很高兴又和大家见面了!在上一篇的内容中我们遗留了一个问题,我们在编写交换两个整型变量数值的时候不能直接编写函数,而是要将参数取地址之后再传送给函数,然后函数需要通过指针来接收,最后解引用来完成交换,可是为什么我们在正常比较大小输出最大值的时候就不用呢?这就是我们今天要探讨的问题——函数的参数。
三、函数的参数
参数的分类
1.实际参数(实参)
定义:真实传给函数的参数,叫实参。实参可以是:变量、常量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
2.形式参数(形参)
定义:形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成后就自动销毁了。因此形式参数只在函数中有效。
怎么来理解这两个参数,下面我们继续借用上一篇的例子:
//函数的参数
void swap2(int* x, int* y)//这里的x、y就是形式参数;
{
int z = *x;
*x = *y;
*y = z;
}
void swap(int x, int y)//这里的x、y就是形参;
{
int z = x;
x = y;
y = z;
}
int main()
{
int a = 1;
int b = 2;
printf("a=%d b=%d\n", a, b);
swap(a, b);//这里的a,b就是实参;
swap2(&a, &b);//这里的&a和&b就是实参;
printf("a=%d b=%d\n", a, b);
return 0;
}
简单的理解就是我在调用函数时,传给函数的参数就叫做实参,在定义函数的时候,定义的参数就是形参。这里对于实参和形参的关系,我们需要了解一下:
当实参传给形参时,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的。
有了这个结论之后我们再回过头来分析swap和swap2这两个函数:
对于函数swap来说,形参x,y就是实参a,b的一份拷贝,这里拷贝的内容是a,b的数值,所以无论怎么修改形参,对a,b本身的值都是没有影响的;
但是在swap2中形参x,y是对实参&a,&b数值的一份拷贝,这里拷贝的内容是a,b的地址,这里我们可以理解为就是把a和b的家整个拷贝了过去,在通过解引用操作符把a和b从家里给叫出来,然后再对其进行操作;
所以在swap中发生变化的是形参x,y,但是在swap2中看似发生变化的是形参x,y实际上真正发生变化的是实参a,b。
为了帮助大家更好的理解这些内容,接下来咱们继续探讨下一个内容——函数的调用。
四、函数的调用
调用函数的方式
1.传值调用
简单的理解就是将实参的值传给形参,函数的实参和形参分别占用不同的内存块,此时对形参的修改不会影响实参。
2.传址调用
●传址调用就是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
●这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
在上面的例子中swap就是传值调用,因为实参和形参分别占用不同的内存块,也就是它们是张三、李四、王五、赵六四个人,对形参的修改,不会影响实参,也就是对王五、赵六的修改并不会影响张三和李四两个人;
swap2就是传址调用,此时形参是创建实参的内存地址,就好比形参是张三和李四的家,此时我们对*x和*y的修改就相当于是对画了妆的张三和李四进行修改,虽然外观变了,但是实质上还是它们俩儿,所以在函数内部可以直接操作实参;
3.调用情景
我们在函数中进行的操作的对象如果是实参的值,并不会改变实参本身,那我们就可以用传值调用,也就是我们上一篇提到的比较大小找出两数中的最大值;
我们在函数中进行的操作对象如果是实参本身,在函数体内需要对实参本身进行修改,那我们就要用传址调用,也就是我们刚刚的例子,交换两整型变量的值;
4.习题演练
在前面的学习中,我们是直接在主函数中完成了这些题的内容,现在我们需要通过自定义函数来完成,以此来帮助大家增强对自定义函数的理解及调用。
(1)写一个函数可以判断一个数是不是素数;
//写一个函数可以判断一个数是不是素数
int prime_number(int x)//viod——无返回类型;prime_number——素数——函数名;x——函数形参;
{
int a = 0;//函数体——函数如何实现;
for (a = 2; a <= sqrt(x); a++)//sqrt——开平方——数学函数,需要调用头文件<math.h>;
{
if (x % a == 0)
{
return 1;
}
}
if (a > sqrt(x))
{
return 2;
}
}
int main()
{
int i = 0;
scanf("%d", &i);
//判断是否为素数,只需要判断就行
int j = prime_number(i);//i——函数实参
if (1 == j)
printf("%d不是素数\n", i);
else
printf("%d是素数\n", i);
return 0;
}
这里我们分别输入5和50来看看结果:
(2)写一个函数判断一年是不是闰年;
//写一个函数判断一年是不是闰年
int leap_year(int x)//void——无返回类型;leap_year——闰年——函数名;x——函数形参
{
if ((x % 4 == 0 && x % 100 != 0) || (x % 400 == 0))//函数体——函数如何实现;
{
return 1;
}
else
return 0;
}
int main()
{
int year = 0;
scanf("%d", &year);
//判断是不是闰年,只需要有判断的功能就行
int a=leap_year(year);//year——函数实参;
if (a == 1)
printf("%d是闰年\n", year);
else
printf("%d不是闰年\n", year);
return 0;
}
现在测试一下1991和2020:
(3)写一个函数,实现一个整形有序数组的二分查找;
//写一个函数,实现一个整形有序数组的二分查找
int dichotomy(int arr[], int a, int b)
{
int left = 0;//左侧下标;
int right = b - 1;//右侧下标;
while (1)
{
int mid = (left + right) / 2;
if (a < arr[mid])
{
right = mid - 1;
}
else if (a > arr[mid])
{
left = mid + 1;
}
else
{
return mid;
}
if (right < left)
{
return 2;
}
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 0;
scanf("%d", &k);
int sz = sizeof(arr1) / sizeof(arr1[0]);//数组元素个数;
int result=dichotomy(arr1, k, sz);
if (result == 2)
{
printf("没找到,数组里没有%d", k);
}
else
{
printf("找到了,%d的下标是%d", k, result);
}
return 0;
}
下面我们输入10和11分别测试一下:
(4)写一个函数,每调用一次这个函数,就会将num的值增加1;
//写一个函数,每调用一次这个函数,就会将num的值增加1
void add(int* x)
{
(*x)++;//操作符优先级:() > ++ > *;
//*x++;这种编写方式,是先对x进行++再对x++进行解引用;
}
int main()
{
int num = 0;
//调用函数,使得num每次增加1;
//对实参本身进行修改,这里需要用传址调用;
add(&num);
printf("num=%d\n", num);
add(&num);
printf("num=%d\n", num);
add(&num);
printf("num=%d\n", num);
return 0;
}
下面我们看一下运行结果:
到这里咱们的内容就全部结束了,希望通过这几道习题能够帮助大家更好的理解形参与实参还有传址调用与传值调用的区别,接下来随着学习的深入,我会继续给大家分享我在学习过程中的感受,感谢大家的翻阅,咱们下一篇见。
标签:调用,函数,形参,int,小白,num,实参,历程 From: https://blog.51cto.com/u_16231477/7509100