c语言 - 函数的基本用法及传参

news/2025/3/20 0:12:32/

函数

  • 函数是一个完成特定功能的代码模块,其程
    序代码独立,通常要求有返回值,也可以是
    空值。
  • 一般形式如下:
    <数据类型><函数名称>( <形数说明> )
    {语句序列;return[(<表达式>)];}
  • 函数的声明就是指函数原型
  • 其中,<形式参数声明>可以缺省声明的变量名称,但类型不能缺省
  • 例如,
    double Power(double x, int n);
    double Power(double,int); //缺省写法,但这种写法只能在单独一句话函数声明时使用,不能在函数实现时缺省
  • 函数的类型不为 void 的时候有返回值,这时要写 return 语句,反之无
  • 函数要先声明再调用,不能放在 main 函数 后面声明,如果不想把大段的函数全部放在main函数的上方,那么用简短的语句在main函数上方进行声明,然后在main函数后面再实现也可以,例如:
#include<stdio.h>double power(double x, int n);//先进行声明,在后面实现也可以int main()
{printf("2的2次方为:%lf\n",power(3,3));return 0;
}double power(double x, int n)//函数具体的实现
{double product = 1;int i;for (i=0; i<n; i++){product *= x;}return product;
}

以上代码格式为 函数先声明,再调用,然后实现 的顺序

函数的参数传递

函数之间的参数传递方式:

  • 全局变量
  • 复制传递方式
  • 地址传递方式

全局变量

  • 全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的
  • 全局变量一经定义后就会在程序的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响。不好维护,不建议使用。
  • 例子:
#include<stdio.h>int x = 3, n = 2;//定义全局变量x,n
double power();int main()
{printf("%d的%d次方为:%lf\n",x,n,power());//这里无需再传参了return 0;
}double power()//这里不要参数也行了
{double product = 1;int i;for (i=0; i<n; i++){ //这里x和n直接使用全局变量product *= x;}return product;
}

复制(赋值)传递方式

  • 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
  • 形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参,见代码2
  • 复制传参例子(代码1):
#include<stdio.h>double power(double x, int n);int main()
{int x = 2;int n = 2;printf("2的2次方为:%lf\n",power(x,n)); //把x,n作为实参赋值给形参x,nreturn 0;
}double power(double x, int n)//形参x,n
{double product = 1;int i;for (i=0; i<n; i++){product *= x;}return product;
}

输出结果:

2的2次方为:4.000000

代码2:

#include<stdio.h>void exchange(int a, int b);int main()
{int c = 2;int d = 3;exchange(c,d);printf("c = %d, d = %d\n",c,d);//在main函数中打印return 0;
}void exchange(int a, int b)
{int t;t = a;a = b;b = t;
}

输出结果:

c = 2, d = 3

从上面这个结果可以看出实参 c 和 d 并没有发生交换,但是这具体是什么原因呢?

答案是:形参和实参在内存中的存储空间是不一样的,形参是另外开辟的存储空间,当 exchange 函数被调用时,c 的值被复制(赋值,下同)到形参 a 中,同样的,d 的值被复制到形参 b 中,也就是形参 a 和 b 中存的都是实参 c 和 d 的副本,而在 exchange 函数内部只会交换形参 a 和 b 的值,不会影响到实参 c 和 d。

如果想要实质性的交换需用指针,(PS:交换指针的值-地址没有用,除非直接在被调用函数中打印交换地址过后的 *a 和 *b )如果只是想看一下打印结果,可以把输出函数写到被调用函数(交换函数)中去,这里举一个用指针进行实质交换的例子:

#include<stdio.h>void exchange(int * a, int * b);//形参都为指针int main()
{int c = 2;int d = 3;exchange(&c,&d); //实参为 c 和 d 的地址printf("c = %d, d = %d\n",c, d); //在这里打印return 0;
}void exchange(int * a, int * b)
{int t;t = *a;*a = *b;*b = t; // * 取传入地址对应的值,然后通过 t 进行交换}

输出结果:

c = 3, d = 2

地址传递方式

  • 按地址传递,实参为变量的地址,而形参为同类型的指针
  • 被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作,相当于对实参本身的操作)
  • 例子就是上个代码

复制传递和地址传递的适用场景

在C语言中,参数传递可以通过值传递(复制传递)或引用传递(地址传递)来实现。选择何种方式取决于对函数的需求和要求。

  1. 复制传递(值传递):
    使用复制传递时,函数接收到的是实际参数的副本,而不是实际参数本身。这意味着在函数内部对形式参数进行的修改不会影响到实际参数的值。

    复制传递适用于以下情况:

    • 当函数不需要修改实际参数的值时。
    • 当实际参数是基本数据类型(如整数、浮点数等)或小型结构体时,复制传递的开销相对较小。

    示例代码中的exchange函数使用了复制传递,因为我们只是想在函数内部交换参数的值,并不需要修改实际参数的值。

  2. 地址传递(引用传递):
    使用地址传递时,函数接收到的是实际参数的地址,可以通过指针操作实际参数的值。在函数内部对形式参数的修改会影响到实际参数的值。

    地址传递适用于以下情况:

    • 当需要修改实际参数的值时。
    • 当实际参数是大型结构体或数组时,避免复制大量的数据。

    示例代码中的修改后的exchange函数使用了地址传递,我们通过传递指针来操作实际参数的地址,从而实现变量值的交换。

总而言之,在选择参数传递方式时,需要根据具体的需求和情况来决定。如果需要修改实际参数的值,或者实际参数是大型结构体或数组时,地址传递通常是更好的选择。而对于不需要修改实际参数的值,或者实际参数是基本数据类型或小型结构体时,复制传递是更简单和高效的方式。

函数的传参 - 数组

  • 全局数组传递方式
  • 复制传递方式(实参为数组的指针,形参为数组名(本质是一个指针变量))
  • 地址传递方式(实参为数组的指针,形参为同类型的指针变量)
  • 对于字符数组来说,传参的时候只穿数组名也行,因为程序可以根据 ‘\0’ 字符串结束符来判断结束,而 int 类型等类型的数组还必须要传进去数组元素个数,否则程序不知道数组的末尾在哪。

http://www.ppmy.cn/news/1058321.html

相关文章

LLaMA中ROPE位置编码实现源码解析

1、Attention中q&#xff0c;经下式&#xff0c;生成新的q。m为句长length&#xff0c;d为embedding_dim/head θ i 1 1000 0 2 i d \theta_i\frac{1}{10000^\frac{2i}{d}} θi​10000d2i​1​ 2、LLaMA中RoPE源码 import torchdef precompute_freqs_cis(dim: int, end: i…

vue若依导出word文件,简单的实现

首先前端导包,注意exportDocx的导包位置要修改成你自己的 import {exportDocx} from /utils/docUtil/docutil.js; import {addDays} from date-fns; import {listGongyi} from "/api/system/detail";然后新建一个测试按钮 <el-col :span"1.5"><…

13.4 目标检测锚框标注 非极大值抑制

锚框的形状计算公式 假设原图的高为H,宽为W 锚框形状详细公式推导 以每个像素为中心生成不同形状的锚框 # s是缩放比&#xff0c;ratio是宽高比 def multibox_prior(data, sizes, ratios):"""生成以每个像素为中心具有不同形状的锚框"""in_he…

呈现数据的精妙之道:选择合适的可视化方法

在当今数据时代&#xff0c;数据可视化已成为理解和传达信息的重要手段。然而&#xff0c;选择适合的数据可视化方法对于有效地呈现数据至关重要。不同的数据和目标需要不同的可视化方法&#xff0c;下面我们将探讨如何选择最佳的数据可视化方法来呈现数据。 1. 理解数据类型&a…

基于Jenkins构建生产CICD环境(第二篇)

基于Jenkins自动打包并部署Tomcat环境 传统网站部署的流程 在运维过程中&#xff0c;网站部署是运维的工作之一。传统的网站部署的流程大致分为:需求分 析-->原型设计-->开发代码-->提交代码-->内网部署-->内网测试-->确认上线-->备份数据-->外网更新…

vue3鼠标拖拽滑动效果

第一步 在utils下面新建一个directives.js文件&#xff0c;然后引入如下代码 const dragscroll (el) > {el.onmousedown ev > {const disX ev.clientX;const disY ev.clientY; // 需要上下移动可以加const originalScrollLeft el.scrollLeft;const originalScroll…

keepalived双机热备,keepalived+lvs(DR)

本节主要学习了keepalivedlvs的作用和配置方法主要配置调度器和web节点&#xff0c;还有keepalived的双击热备&#xff0c;主要内容有概述&#xff0c;安装&#xff0c;功能模块&#xff0c;配置双击热备&#xff0c;验证方法&#xff0c;双击热备的脑裂现象和VIP无法通信。 目…

【redis问题】Caused by: io.netty.channel

遇到的问题&#xff1a; 在使用 RedisTemplate 连接 Redis 进行操作的时候&#xff0c;发生了如下报错&#xff1a; 测试代码为&#xff1a; 配置文件&#xff1a; 问题根源&#xff1a; redis没有添加端口映射解决方案&#xff1a; 删除原来的redis容器&#xff0c;添加新…