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

学习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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值