函数(基础+进阶)

函数

函数的定义

•函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。
•可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由自己来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。
•函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。
•C 标准库提供了大量的程序可以调用的内置函数。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。
•函数还有很多叫法,比如方法、子例程或程序,等等。
•强调一点,C语言不允许函数嵌套定义;也就是说对于C语言,不能在一个函数中定义另外一个函数,必须在所有函数之外定义另外一个函数。main() 也是一个函数定义,也不能在 main() 函数内部定义新函数,但是C++支持嵌套定义。

函数常用形式

返回值的类型   函数名(参数说明)

{

  说明部分;//函数的声明 类的声明 结构体的声明 等

  语句部分;//各种函数的调用与语句的使用

}

主函数

int main()

{

语句部分

}

C语言返回值说明

•1) 没有返回值的函数为空类型,用void表示。
•2) return 语句可以有多个,可以出现在函数体的任意位置,但是每次调用函数只能有一个 return 语句被执行,所以只有一个返回值(少数的编程语言支持多个返回值,例如Go语言)。
•3) 函数一旦遇到 return 语句就立即返回,后面的所有语句都不会被执行到了。从这个角度看,return 语句还有强制结束函数执行的作用。
•4) return 语句是提前结束函数的唯一办法

函数返回一个数据类型,需要使用关键字return

int max(int a , int b){

  if(a>b){

  return a;

  }else{

  return b;

  }

}

一个函数只能有一个返回值,但为什么上面的函数有两个return呢?

  因为一个函数,在执行到return这步的时候,函数已经返回了,也就是函数已经结束,不会再进行此return下面的一切语句

  上述例子也有一个细节,就是两个return分别在if , else语句的内部,我们已经学习过if , else语句,如果执行了if里面的语句就不会执行else里面的语句,所以体现不出两个return语句在一起是什么样子的

int main(int a , int b){

  return a;

  return b;

}

虽然一个函数里面只能有0个或1个返回值,但如果函数有多个return语句的话,编译器也是不会报错的,例如上述函数,函数执行了return a;后,函数就已经返回或者说结束了

返回值的说明

•1) 没有返回值的函数为空类型,用void表示。
•2) return 语句可以有多个,可以出现在函数体的任意位置,但是每次调用函数只能有一个 return 语句被执行,所以只有一个返回值(少数的编程语言支持多个返回值,例如Go语言)。
•3) 函数一旦遇到 return 语句就立即返回,后面的所有语句都不会被执行到了。从这个角度看,return 语句还有强制结束函数执行的作用。
•4) return 语句是提前结束函数的唯一办法

函数名称 

.1)对于函数名称,函数名称由程序员起名,根据函数的功能来起名,一般不同公司会有自己规定好的命名格式,通常为英文,英文用拼音很拉就对了

函数参数

函数的参数:给一个函数传入的数据就称为参数

参数分为形参(形式参数)和实参(实际参数)

在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参

函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参

类型调用类型描述
形参传值调用

该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数

实参引用调用

通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作

此处补充关于函数的调用的原理

调用一个函数 1.函数的声明   主函数中调用函数  函数的定义

                      2.函数的定义    主函数中调用函数

定义函数可以理解为定义一个操作的模板 之后调用该函数可以理解为根据模板操作

我们调用函数时候为了防止我们误操作破坏函数,因此我们调用的函数模板都是拷贝的。

调用的参数则需区分:一类为形参为全局区参数,一类为在函数语句中调用外部全局区的参数。

当外部参数作为形参时:(形参也就是函数调用括号内的参数)我们使用的外部参数也是拷贝的(存储在栈区,函数结束时销毁)因此我们对拷贝的参数操作并不会影响到全局区的原参数

(修改形参不影响全局区原函数)

当外部参数在函数语句中时:

因此在一个函数内部修改全局变量的值在下次调用函数修改该全局变量的值之前全局变量的值都是被函数修改后的值,在下次修改全局变量的函数调用全局变量时函数恢复到开始的值重新被修改,也就是全局变量的值在函数内部被修改后并不会自动恢复,它会一直保留该值,直到下次被修改

当我们想要彻底修改全局变量时只能使用指针和引用,指针和引用是用来找到储存的参数的工具,拷贝了的工具是完全相同的指向的最终目的地不会变,因此我们可以使用拷贝的工具(指针引用)来修改原来的参数a  也可以说只有指针和引用可以修改原来的参数 (后面会学到值传递和地址传递 用到的原理就是这个)-->指针和引用是针对内存内存储的数据进行修改

默认情况下,C 使用传值调用来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的实际参数

形参和实参的区别和联系

1) 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效(函数内部本质一个代码块),不能在函数外部使用。(结合后面代码块理解)
2) 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
3) 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。(函数可以在声明中传参,下面调用时可以用不等于定义中数量的参数调用函数,需要注意右向左 ,右向左赋值,也就是如果右边的参数未赋值而给左边的数赋值,下面少输入定义的参数扔无法调用

声明 void funs(int a, int b,4.2);

        void funs(int a,2,4.5);

声明 void funs(2,int b,4.2);

        void funs(int a,2,double c);

void funs(int a,int b,int c,double d)

{

cout<<"函数"<<std::endl;(std::endl;是std作用域下的换行用的等同于C中\n)

}

调用 funs(2,4);//可执行

        funs(1);     //可执行

        funs(2);     //不可执行

        funs(1,2.5);  //不可执行(就是报错了)

))
4) 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。

传值调用

默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。

引用调用

通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。

多个参数的函数

函数参数可以有0个,1个或者多个。每个参数之间用,隔开例如:

int a(int a , int b , int c)

{
return 0;
}

int b(int a , int b , int c ,int d)

{
return 0;
}

不管参数有多少个,函数的定义与函数的调用的参数个数和数据类型必须一一对应。

Ps:除非对函数进行函数重载,但C语言是不支持函数重载的,C++支持函数重载

函数的调用

在C语言中,函数调用的方式有多种,例如:

//函数作为表达式中的一项出现在表达式中

z = max(x, y);

m = n + max(x, y);
//函数作为一个单独的语句

max(a , b);
//函数作为调用另一个函数时的实参

printf( "%d", max(x, y) );     (C++)cout <<max(x,y);

total( max(x, y), min(m, n) );
//嵌套调用

 total(int a, int b){

  max(a , b);

}

函数声明

#include<stdio.h>            #include<iostream>

void main() {                      using namespace std;   (因为这里给了全局的 std覆盖 所以下面的 

  hello();                             void main()                         endl;不用再单独写作用域限定std::)

}                                        {  hello();  }

void hello() {                      void hello(){

  printf("123");                     cout<<"123"<<endl; }

}运行报错   编译是顺序执行的,hello()函数定义在main()主函数的后面,编译器执行main函数的时候还没有定义hello(),因此他不知道hello是什么东西,就会编译报错

解决办法:

1.将hello()函数定义在main函数(需要调用hello()函数的前面)

#include<stdio.h>                   #include<iostream>

void hello() {                           using namespace std; 

printf("123");                           void main() 

}                                            {  cout<<hello();  }

void main() {                          void hello(){

hello();                                   cout<<"123"<<endl; }

}

2.函数声明

#include<stdio.h>                        #include<iostream>

void hello();                                   using name space std;

void main() {                                void hello();

  hello();                                        void main(){

}                                                    hello();}

void hello() {                                  void hello(){

  printf("123");                                cout<<"123";

}                                                      }

全局变量和局部变量

形参和实参的区别中提到,形参变量要等到函数被调用时才分配内存,调用结束后立即释放内存。这说明形参变量的作用域非常有限,只能在函数内部使用,离开该函数就无效了。所谓作用域(Scope) ,就是变量的有效范围。

根据作用域的不同,变量可以分为全局变量和局部变量

定义在函数内部的变量称为局部变量(Local Variable)(也就是定义在代码块内)

在所有函数外部定义的变量称为全局变量(Global Variable)(定义在当前文件中 )

局部变量    (可以用extern将作用域扩大,用static来限制作用域或者缩小作用域(后面会用理解可能用得到现在做一个了解))

定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部(代码块中,因为东西只是放在栈区出来函数栈区的数据被销毁), 离开该函数后就是无效的,再使用就会报错。例如:

int f1(int a){

    int b , c;  //a , b ,c仅在函数f1()内有效

    return a + b + c;

}

int main(){

    int m , n;  //m , n仅在函数main()内有效

    return 0;

}

几点说明:
1) 在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时,main 函数中也不能使用其它函数中定义的变量。main 函数也是一个函数,与其它函数地位平等。
2) 形参变量、在函数体内定义的变量都是局部变量。实参给形参传值的过程也就是给局部变量赋值的过程。
3) 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。(int类型分配在int类型的区域各个类型分在各个区域)
4) 在语句块中也可定义变量,它的作用域只限于当前语句块。

全局变量

在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。如果给全局变量加上 static 关键字,它的作用域就变成了当前文件,在其它文件中就无效了

int a, b;  //全局变量

void func1(){

   
}

float x , y;  //全局变量

int func2(){

    

}

int main(){

    

    return 0;

}

a、b、x、y 都是在函数外部定义的全局变量。C语言代码是从前往后依次执行的,由于 x、y 定义在函数 func1() 之后,所以在 func1() 内无效;而 a、b 定义在源程序的开头,所以在 func1()、func2() 和 main() 内都有效。

全局变量和局部变量注意事项

当全局变量和局部变量同名时,在局部范围内全局变量被“屏蔽”,不再起作用。或者说,变量的使用遵循就近原则,如果在当前作用域中存在同名变量,就不会向更大的作用域中去寻找变量。

 C语言规定,只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量。对于 main() 函数,即使代码块中的 n 离输出语句更近,但它仍然会使用 main() 函数开头定义的 n

在一个函数内部修改全局变量的值会影响其它函数,全局变量的值在函数内部被修改后并不会自动恢复,它会一直保留该值,直到下次被修改。(全局变量在函数内部修改,是否需要global声名,要看是否对全局变量的指向做出了修改,如果修改了全局变量的指向,就必须使用global,如果涉及不到修改变量的指向,则不需要使用global声名)

•C语言规定,在同一个作用域中不能出现两个名字相同的变量,否则会产生命名冲突;但是在不同的作用域中,允许出现名字相同的变量,它们的作用范围不同,彼此之间不会产生冲突。这句话有两层含义:
•不同函数内部可以出现同名的变量,不同函数是不同的局部作用域;
•函数内部和外部可以出现同名的变量,函数内部是局部作用域,函数外部是全局作用域。

块级变量

块级变量:在代码块内部定义的变量

代码块:就是由{ }包围起来的代码。代码块在C语言中随处可见,例如函数体、选择结构、循环结构等。不包含代码块的C语言程序根本不能运行,即使最简单的C语言程序也要包含代码块。

C语言允许在代码块内部定义变量,这样的变量具有块级作用域;换句话说,在代码块内部定义的变量只能在代码块内部使用,出了代码块就无效了。  

int main()
{ 
代码块 
}

if()
{
代码块
}
else
{
代码块
}

while()
{
代码块
}

for(循环条件)
{
代码块
}

单独的代码块

C语言还允许出现单独的代码块,它也是一个作用域

#include <stdio.h>                             #include<iostream>

int main(){                                          using namespace std;

    int n = 22;  //编号①                        int main(){       

    //由{ }包围的代码块                        int n = 22;  //编号①   //由{ }包围的代码块    

    {                                                      {

        int n = 40;  //编号②                       int n = 40;  //编号②

        printf("block n: %d\n", n);              cout<<"blockn:"<<n<<endl;

    }                                                      }

    printf("main n: %d\n", n);                 cout<<"main n:"<<n<<endl;

    return 0;                                             return 0;

}                                                           }

运行结果:

block n:40

main  n:22

递归函数

一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

(C与C++相同部分下代码不重新敲了)

#include <stdio.h>                      #include<iostream>

//求n的阶乘

long factorial(int n) {

    if (n == 0 || n == 1) {

        return 1;

    }

    else {

        return factorial(n - 1) * n;  // 递归调用

    }

}

int main() {

    int a;

    printf("Input a number: ");                                  cout<<"lnput a number:"<<endl;

    scanf("%d", &a);                                                cin>>a;

    printf("Factorial(%d) = %ld\n", a, factorial(a));   cout<<"Factorial(a )="<<Factorial(a);

    return 0;

}

输入:5

输出:Factorial(5) = 120

在此感谢    符.休伯利安.手残之光.华   在下版本往逝乐土对于某只手残单推人下版本的减负----   一只符华单推人                                                             小识爸爸爱你

                                                                                                                        

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只符华单推人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值