4-8-实参与形参、函数声明与定义、函数读取顺序、可变参数的函数、函数生存周期

来源:互联网 发布:linux应用层 编辑:程序博客网 时间:2024/05/18 18:03

学习4-8期视频笔记

1、 形参与实参

 

//函数调用前,形参,也就是函数定义时()中的参数,值都不确定

//不确定的值,不会分配内存,只有调用函数的时候,才会新建一个变量

//接受实际参数的值,当函数调用结束时候,形参占据的内存会被回收

 

//实际参数是函数调用的时候,函数传递过来的确切值就是实际参数

//实际参数可以是常量,变量或者表达式


//形参与实参内存地址不同,占用不同的内存空间

#include<stdio.h>#include<stdlib.h>

void go(int num)  //int num 是一个形参,只有调用的时候,才会分配一个内存,函数调用的时候,形/参分配内存,新建一个变量,存储传递过来的实参{       num = 100;       printf("\ngo=%d %x", num,&num);}void main(){       int num = 10;       printf("num=%d %x", num,&num); //num=10       go(num);//num=100       go(3+5); //num=100    printf("\n num=%d %x", num, &num);//num=10       system("pause");}

实参给形参赋值,会自动进行数据据类型转换

 

2、 局部变量与全局变量

#include<stdio.h>#include<stdlib.h>inty=40;//全局变量,不属于任何一个函数,可被任何一个函数调用//局部变量调用完成,会被回收void go(){       int x = 100;  //x为局部变量,只可以在该函数内使用      printf("\nx=%d %x", x, &x);}void main(){       go();       //printf("num=%d%x", x, &x); //因为x为局部变量,这里不能输出x的地址和数值       system("pause");}


变量是全局变量还是局部变量,看变量声明的位置

同名情况,局部变量会屏蔽全局变量(后来者居上),c++中可以通过(::+变量名) 访问全局变量

块语句内部变量,作用域是整个块

 

全局变量生存周期很长,一直占用内存,除非程序退出

缺点:容易与局部变量重名,很容易被屏蔽,失效。值容易被修改(注入)

注意:1、名字要容易理解,尽量不与局部变量重名

2、 避免占用内存较大的变量使用全局变量,节约内存

3、 避免全局变量被错误的修改

正规的软件工程,写一个函数要修改全局变量,一定要注释(哪块修改,修改为什么值)

 

全局变量没有初始化,会默认为0

 

3、 函数的声明和定义

同名定义只可以有一个,但是声明可以有多个。

函数在调用之后,c可以不声明,c++必须声明

 

c语言编译比较宽泛,有库的路径,可以自动定位,不需要函数的生命;

c++比较严谨,必须声明函数声明、库文件、头文件。

 

c语言,没有函数声明,有库可以调用;没有函数声明有函数实体,可以调用

c++,没有函数声明,有库不可以调用;没有函数声明有函数实体,实体在调用前可以调用,在调用后不可以调用

 

全局变量可以有多个(三个int a;但是不能有赋值),这时候系统会将其当作声明处理。


4、 函数

(1)函数定义在程序中是平行的,即不允许在一个函数内部再定义一个函数;

(2)函数名是用户自定义标识符,当函数值为整型时类型名可省略(不推荐省略);当函数不需要向调用处返回值时,使用void类型名

(3)形参表中的形参是用户自定义标识符,没有参数时,圆括号不能省略,此时函数为无参函数

#include<stdio.h>#include<stdlib.h>int add(int a,int b) //这里int a,int b只有调用的时候才会分配内存,函数运行完后会自动释放{       a = 1;       b = 2;       return a + b;}void main(){       int a = 10;       int b = 20;       printf("%d", add(a, b));//函数参数除了数组以外,其他都是副本//c语言参数过多会警告,但会忽略多的,结果也不保证正确       system("pause");}

//结果为3

注意:(1)如果函数类型为void以外的任何一种类型,则函数内部必须出现return语句

(2)return语句中表达式的类型应与函数值类型一致。若不一致,以函数值类型为准

 

函数调用不能取地址

5、 函数参数读取顺序

#include<stdio.h>#include<stdlib.h>void show (int a,int b){printf(“a=%d,b=%d”,a,b)}void main(){inta=5;show(a,a++);}

//结果为a=6,b=5

//因为栈为先进后出,c语言是从下往上,从后往前压入栈中(这样才能保证顺序执行),所以这个调用函数内部是从右往左执行

 

6、 可变参数的函数

//第一个参数为总个数

//加法

#include<stdio.h>#include<stdlib.h>#include<stdarg.h>int add(int num,...)//随意输入多少个数相加{       int res = 0;//结果       va_list argp;//存储参数开始的地址       va_start(argp, num);//从首地址开始,读取num后面的数据       for (int i = 0; i <num; i++)       {              res +=va_arg(argp,int);//读取一个数据按照int型解析       }       va_end(argp);//结束读取       return(res);}void main(){       printf("%d\n", add(3, 1, 2,3));//第一个值为后面参数的个数,后面为具体数值       printf("%d\n", add(4, 2, 2,3,5));       printf("%d\n", add(5, 1, 2,3,4,5));        system("pause");     }

//运行多个程序

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<stdarg.h>void go(int num,...){       va_list argp;//存储参数开始的地址       va_start(argp, num);//从首地址开始,读取num后面的数据       for (int i = 0; i < num; i++)       {              char str[50];              sprintf(str,"%s",va_arg(argp,char*));//读取一个数据按照int型解析              system(str);       }       va_end(argp);//结束读取}void main(){       go(3, "notepad","calc", "tasklist");       system("pause");      }//未知个数的输出
//以-1结尾,表示输出结束void showint(int num, ...){       va_list argp;//存储参数开始的地址       va_start(argp, num);//从首地址开始,读取num后面的数据       int argvalue = num;       do       {              printf("%d", argvalue);        argvalue=va_arg(argp, int);//读取一个数据按照int型解析        } while (argvalue != -1);       va_end(argp);//结束读取 }void main(){       showint(1, 2, 3, 4, -1);       system("pause");}

7、 递归

//打开notepad

void main(){       system("<strong>start</strong> notepad");//并行打开无数个notepad       main();}


//通过给目标程序注入dll使程序栈溢出

#include<Windows.h>_declspec(dllexport) void go(){Sleep(1);go();}


 

//叠加从0-num

int add(int num){if(num==0){printf(“%d”,sum)}else{returnnum+add(num-1)}}


例子

树状递归 f(n)=f(n-1)+f(n-2)

线性递归 f(n)=f(n-1)+n

树状递归速度很慢,递归很慢,函数调用返回都需要时间(进栈出栈)

递归=循环+栈

int getn(int num)//递归{if(num==1||num==2){return1;}else{returngetn(num-1)+getn(num-2);}}int getN(int num){ //循环    if(num==1||num==2)return 1;    else{        intf1=1;        intf2=1;        intf3;        for(inti=3;i<=num;i++){            f3=f1+f2;            f1=f2;            f2=f3;        }    }    returnf3;}

8、 函数参数的生存周期

函数返回值有副本机制,返回时会另保存一份

printf(“%d”,add(1,2))l//这里打印的就是副本,内存中的数据就会被销毁

 

返回值(临时变量)存储在寄存器,原数据被销毁,返回副本数据(寄存器中的数据)

返回值如果是全局变量,则不会被回收

 

 

 

 

 

 

 

 

 

 

 

 

 

0 0
原创粉丝点击