【C语言/C++】函数,形参与实参

前言:

我们遇到一个问题,如果要实现多次核心代码一样的代码段,我们发现会重复输入几次,并且其代码维护量也会代数增长,而且会发现每次重复在浪费时间。我们在想我们能不能有一个东西能来帮助我们解决这个问题,这也便是我们这篇章要讲的重点之一——函数

我们认为,一个代码要具有简洁性,并且可维护性高,显而易见100段行功能相似的代码段,如果出错要逐行更改困难;
我们注意到,实际上#include<stdio.h>#include<iostream>本质上就是封装了许许多多代码段综合而成的,编译后产生的是许多行stdio.hiostream所包含的代码;
而我们将这种使用一个“压缩包”把许多段代码段封装的“压缩包”称为函数,而#include<>是函数的一种,我们称之为库函数

e.g.1向数组 a [ 5 ] a[5] a[5]输入五个数字,并乘以k倍,打印之前与之后的数组。

分析:可以看到打印数组 a [ 5 ] a[5] a[5]前后都是要操作的,这是重复的,我们如果采用两次

for(int i=0;i<5;i++){
	printf("%d",a[i]);
}

是否会点呆?虽然说我是在勉强说法。
我们这时候采用函数的形式来写,写成

void print();

void print(int *p){
for(int i=0;i<5;i++){
	printf("%d",*(p+i));
	}
}

会使得我们后续调用也好,修改也好,维护也好,都变得更加简单。
因此,我们分析完输出后,这道题也便游刃而解。

#include<stdio.h>
void print(int a[]);
void change(int a[],int k);
int main(){
	int k,a[5];
	for(int i=0;i<5;i++){
	scanf("%d",&a[i]);
	}
	print(a[]);
	scanf("%d",&k);
	change(a[],k);
	print(a[]);
	return 0;
}

void print(int *p){
for(int i=0;i<5;i++){
	printf("%d",*(p+i));
	}
}

void change(int *p,int k){
for(int i=0;i<5;i++){
	*(p+i)=*(p+i)*k;
	}
}

e.g.2P5736 【深基7.例2】质数筛

输入 𝑛 个不大于 1 0 5 10^5 105的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。
输入格式:
第一行输入一个正整数 n n n,表示整数个数。
第二行输入 n n n 个正整数 a i a_i ai,以空格隔开。
输出格式:输出一行,依次输出 a i a_i ai 中剩余的质数,以空格隔开。

#include<stdio.h>
int Judgement_Prime(int n){
    if(n<=1)return 0;
    for (int i = 2; i*i <= n; i++) {
        if (n % i == 0) {
            return 0;
        }
    }
    return 1;
}

int main() {
    int a[101], n;
    scanf("%d",&n);
    for (int i = 0; i < n; i++){
        scanf("%d", &a[i]);}
    for (int i = 0; i < n; i++){
        if (Judgement_Prime(a[i])) {//利用1,0表示真假语句,符合2进制运算 
            printf("%d ", a[i]);
        }
    }
    return 0;
}

函数的声明和定义

实际上刚刚上两段例子都涉及函数的声明,区别在于其在main函数的前后位置不同,这是由于main()函数称之为主函数,一个C程序总是从main()函数开始执行的。而其位置的不同早就了声明和定义。
虽然说程序总是从main()函数开始执行的,但是运行程序前,会先解释,解释是指按照我们编写的行数逐行往下解释,我们如果将函数的声明或者定义写在主函数前,那么我们在主函数中运行便不会显得突兀,而如果没有,主函数便不能理解。
e.g.1中,我写

void print(int a[]);
void change(int a[],int k);
......
void print(int *p){
	context;
}

void change(int *p,int k){
	context;
}

这就是函数的声明,有点像春节前跟父母说自己找了个对象,我们把Ta大致描述给了父母听,比如哪里人,人叫什么,人长什么样,也就是返回值类型函数名形参等等,如果我们不事先跟父母(主函数)说我们有对象了,过年期间把Ta带回家,全家人很难在第一时间反应过来。
反过来说,我们事先将对象带回家,并跟父母说自己对象哪里人,叫什么,父母知道的就会更加具体,而且不再是那个抽象且不具体的人,父母便知道了Ta长啥样,有怎样的声音,待人如何等。

int Judgement_Prime(int n){
	context;
}

int main(){
	Judgement_Prime(n);
}

这就是函数的定义,我们先把函数名,以及它的实参,返回类型,以及它能干什么写好,再写到main函数中,我们便知道它有何功效。

生命周期与作用域——形参变量与实参变量

生命周期和作用域是两个联系非常紧密的概念。作用域是从空间上来描述标识符,那么生命周期就是从时间上来描述它。生命周期就是一个标识符拥有内存的那段时间,就是标识符的生命周期。
举个例子,intsome_int;会分配4个字节的数据内存空间。而intsquare(intn){}是一 个函数的定义,会分配一段代码内存空间。内存分配分为数据内存和代码内存,这两块内存都会在定义的时候进行分配。分配时,这段内存就开始存活起来,而内存也是可以进行释放的,对一块内存进行释放的时候,那么这块内存就消亡了。

局部作用域和全局作用域

​ 作用域分为局部的和全局的两种。怎么区分局部的和全局的呢? 只要是在花括号里 面定义的标识符,无论是几层花括号里,只要是在花括号里面定义的就是局部的(local)标 识符。在花括号的外面,那就是全局的(global)标识符。这就是局部全局的概念。如下,函数Test01中的int n可以在函数Test01内部访问,却无法在函数Test02内部访问。
并且Test01定义的n在Test02中是不能访问到的

void Test01(){
    int n;
}

void Test02(){
    context//访问不到Test01中的n;
}

而全局作用域下,是每一个局部都可以访问到的。

int n=423;

void Test01(){
    context;//访问得到n
}

void Test02(){
    context//访问得到n;
}

动态变量和静态变量

局部的标识符分成两类,一种是局部动态的(auto),一种是局部静态的(static)。局部
标识符缺省情况下都是动态的。
什么叫缺省的情况呢? 就是这个auto关键字写和不写都是一回事,为了减少代码量,一般局部变量定义之前是不写auto这个关键字的。静态的话就一定要写上static关键字。
局部的动态标识符和局部的静态标识符的生命周期是不 一样的。它们的作用域是完全一样的。作用域都是按照之前讲过的规则。如下代码中

{auto int n;static int a;}

n和a都是局部变量,但是int n是局部动态变量,static int a是局部静态变量。它们的作用范围都是一样的,都是从定义的地方到右花括号结束为止。但是它们的区别在于它们的生命周期不一样。它们的生命周期怎么区别呢? 局部的动态变量的生命周期随着它的作用域开始而开始,随着它的作用域的消失而消亡,这就是局部动态标识符的生命周期。它的生命周期和它的作用域是同步的。它的作用域开始,它的生命周期就开始了,就为它分配内存了,到它的作用域消失的时候,它的生命周期就消失了,它的那块内存就释放了,内存释放代表的意思就是它里面的数据信息也没有了。但是局部静态的标识符却不一样。它的生命周期是随着它的作用域开始而开始,它的消亡却是要到整个程序结束的时候。所以即使静态标识符退出了右花括号,它的作用域结束了,但是它的生命周期并没有结束,它还活着,它要等到整个程序结束,也就是 main()函数结束的时候才会消亡。

动态变量和静态变量的应用

int sum(int m){ //函数sum的定义
	static int n=0;
	n=n+m;
	return n;
}

因为缺省的时候局部动态标识符的关键字auto是可以省略的。所以就可以直接书写int m;
m就是局部动态变量。n是局部静态变量。
局部静态变量的生命周期是随着它的作用域开始而开始,但是它是随着整个程序的结束而消亡。首先我们第一次调用这个sum(10)函数;调用的时候传一个10给 m; static int n=0;
然后n+=m;这句话等价于n=n+m;输出这个函数的结果就是10。然后第二次调用函数sum(5);的时候,5传给 m 之后,static int n=0;这句话是叫初始化,定义的时候就赋值叫初始化,静态局部变量还有另外一 个规则,这个规则就是静态局部变量的初始化语句只做一次。
所以第一次调用sum 函数的时候执行了这句语句,但是第二次调用的时候,这句语句其实是不执行的。所以第二次调用时,5传给 m,那么执行n+=m后,n的值是多少呢? 因为n的生命周期是要到整个程序结束才会结束,所以这个n是一直活着,上一轮的n的结果也就是10还留着。
所以再执行n+=m后,这个n的值就变成了15。同理再调用一次函数sum(30)的话,输出的n的结果就是45了。这就是通过静态局部变量实现了累加功能,也就是说静态局部变量的生命周期是随着它的作用域开始而开始,但是它是要到整个程序结束才会消亡。静态的变量在程序中的每一次调用的结果都是保存下来的。

int sum(int m){ //函数sum的定义
	static int n;
	n=0;
	n=n+m;
	return n;
}

之前讲过静态变量的初始化语句只执行一次,但是赋值的话每一次都会执行
所以再调用上述的sum (10)的结果就是10,sum(5)的结果就是5了。因为每一次都执行
了n=0;的清零操作,所以上一轮的结果都会被重置为0。

结语:

利用函数体时候,要注意好函数的声明,函数的定义之间的区别,其与主函数之间的关系又是如何?并且还要考虑到其生命周期,考虑到其是作为全局变量还是作为局部变量,以及其是动态变量还是静态变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值