C++学习笔记(一)-- 函数

参考资料:面向对象程序设计–赵宏

本笔记石墨文档链接

第五章 函数初步与变量的存储类型

5.1 函数的基本概念

函数是一个能够完成某个独立功能的程序模块。
一个c++程序至少且只能包含一个main()函数。main()函数是整个程序的入口,通过在main()函数中调用其他函数,这些函数还可以相互调用,甚至自己调用自己来实现整个程序的功能。
c++中的函数分为两类,一类是系统提供的标准函数,即库函数。一类是系统没有提供,用户根据待求解问题的需要自己定义的函数。

5.2 函数的定义

函数类型 函数名([形参名])
{
函数体
}
**函数名:**是一个符合 C++语法要求的标识符,命名规则与变量的命名规则相同。
**形参表:**关于函数参数的个数、名称和类型的列表。在函数定义时进行说明,因此成为形式参数。形参中参数个数多于1时,用 “,"分开。函数可以没有形参,但是()不能缺省。
**函数体:**通过一条或多条语句完成函数的功能。
**函数类型:**包含有值函数和无值函数。有值函数中,return <表达式> 返回函数的值,<表达式>的类型要与函数类型相一致。无值函数,定义函数时,函数类型要声明为 void 类型,如 void Add(),在函数体内不需要 return 语句,如果有,则其后的表达式为空,表示仅仅从函数返回。
如果没有说明函数类型,系统默认为 int 型。
无值函数:

void Add(int x, int y)
{
  cout<<x+y;
}

在函数体内直接将两个数的和输出到屏幕上,不需要返回值,定义为无值函数。

5.3 函数的传值调用及函数原型

5.3.1 函数的传值调用

在一个函数里对一个已经定义了的函数调用格式为:
函数名([<实参表>])
实参表是调用函数时,实际传递给函数的参数列表, 实参的个数,类型,顺序要和形参一一对应。在函数调用时,将实参的值传递给相应的形参。

#include<iostream>
const double PI=3.14;
using namespace std;
double Area(double x)
{
  double s;
  s=PI*x*x;
  return s;
}
int main()
{
  double r,ss;
  cout<<"请输入圆的半径:";
  cin>>r;
  ss=Area(r);
  cout<<"圆的面积为:"<<ss<<endl;
  return 0; 
}

函数调用过程:

  1. 传递参数。将实参 r=12的值传递给形参x:首先计算出实参表达式 r 的值12,然后为形参变量 x 分配内存空间,最后将实参的值12赋给形参变量 x。
  2. 执行函数体。执行 Area 函数的函数体:为变量 s 分配内存空间。函数结束时,释放 Area 函数中的变量 r 和 s 占用的内存空间,函数返回值为452.16。
  3. 返回函数调用表达式的位置,继续执行后面的语句。在此是执行赋值语句 ss=452.16.

提示:

  1. C++中不允许函数的嵌套定义,函数体内不可再定义函数。
  2. 函数调用时,只需要给出各实参,不需要说明他们的类型,但是需要实参和对应的形参类型一致。调用Area(double r)是非法的。
  3. 实参可以是常量、变量或任意表达式,函数调用时首先计算出各实参表达式的值,然后将值传递给相应的形参。
  4. 参数传递为单向传值,形参的变化对实参无影响。
  5. 函数的参数还可以是引用、数组、指针、函数名等其他类型,函数的调用可以是引用调用、嵌套调用、递归调用等。多个函数还可以用相同的名字,即函数重载。

5.3.2 函数原型

在调用函数之前,必须先定义这个函数。如果在函数调用之后再进行函数定义的话,需要在调用之前给出函数原型,也成为函数声明。编译系统根据函数原型确定函数调用时的函数名、参数个数、类型以及返回值类型。
函数原型的一般格式为:
类型 函数名(形参说明表);
函数原型就是函数头加上分号,函数原型中,形参名可以缺省。

double Area(double x);
替换为:
double Area(duble);

//example
#include<iostream>
using namespace std;
int Fun(int);
int main()
{
  int n;
  cout<<"请输入一个整数:1~10:";
  cin>>n;
  while(n!=0)
  {
    cout<<n<<"的阶乘为:"<<Fun(n)<<endl;
    cout<<"请输入一个整数:1~10:";
    cin>>n;
  }
  return 0;
}
int Fun(int n)
{
  int p=1;
  for(int i=2;i<=n;i++)
    p=p*i;
  return p;
}

5.4 变量的存储类型、作用域和生存期

根据变量定义的位置和方式的不同,变量的存储类型有全局变量和局部变量。而变量的存储类型又确定变量的作用域和生存期。

  1. 作用域:变量的作用范围,即变量在哪些函数中能够被识别和使用,描述的是变量的空间属性。
  2. 生存期:变量的寿命长短,即变量在整个程序运行过程中的哪个时间段是可见的。描述的是变量的时间属性。
  3. 在运行一个程序时,将操作系统分配给该程序的内存空间分为四个区域:

代码区:存放程序中各个函数的代码。
全局数据区:保存在其中的变量的寿命是全程的,存放全局数据和静态数据。
堆区:存放程序的动态数据。
栈区:存放程序中各个函数的局部数据。
跟 C++中初始化一个栈有没有什么联系呢?(师兄强势解答)
数据结构是一种概念,定义了我们应该怎么组织数据。C++ 里面,初始化一个数组、栈、队列,是我们在程序中自定义了一种数据结构,用来存储或者操作我们程序中的数据。程序是写在 C++文件里的,文件存储在硬盘里,编译好运行时,程序被加载到计算机的内存,然后 CPU 参与运算。运行完后,数据要么从内存释放、要么存储到硬盘里。
程序运行的东西要加载到内存,怎么加载过去呢?这一部分代码也不是我们写的。
这一部分是操作系统和编译器做的,写的都是非常底层的代码,C、C++、汇编什么的。
它们有自己的管理内存的方式,比如动态数据会存储到堆区,局部数据会存储到栈区。
这个管理内存方式就是怎么组织内存中的数据,栈区就是按栈这种数据结构组织的,堆区就是按堆这种数据结构组织的。

5.4.1 局部变量

局部变量是在函数内部定义的变量,函数的形参也是该函数的局部变量。若没有初始化,其值随机,存放在内存的栈区。
作用域:定义它的函数,其他函数不能识别和使用。
生存期:所在函数的生存期。函数被调用时,函数的局部变量在栈区中被分配空间,获得生命,函数调用结束时,局部变量在栈中的空间被释放。函数再次调用时,为局部变量重新分配空间,重新获得生命。

5.4.2 全局变量

全局变量是在所有函数之外定义的变量,一般在程序顶部文件包含语句之后定义。若没有初始化,自动初始化为0。全局变量存储在全局数据区。
作用域:变量的定义位置开始到定义该变量的文件结束为止。全局变量是各函数公用的变量,每个函数中都可以使用和改变它的值。
生存期:全程,程序开始运行时分配空间,程序结束时释放内存。
在全局变量定义前加一个关键字 static,则该变量称为静态全局变量。作用域:定义该变量的源文件。生命周期:全程。

#include<iostream>
const double PI=3.14;
double g_ss;
using namespace std;
void Area(doubel r)
{
  g_ss=PI*r*r;
}
int main()
{
  int r;
  cout<<"请输入圆的半径: ";
  cin>>r;
  Area(r);
  cout<<"圆的半径为:"<<g_ss<<endl;
  return 0;
}

**解析:**其中 g_ss是全局变量在 Area 函数中直接将圆的面积赋值给 g_ss,Area()调用结束后直接输出 g_ss 的值,此时 Area 不需要返回值,,所以被定义为 void 类型。main()函数中也只调用了 Area 函数,不需要再对 g_ss 进行赋值。
提示:

  1. 虽然全局变量能简化程序,但是会降低函数之间的独立性,不符合结构化程序设计的思想,所以尽量使用局部变量实现。
  2. 同一源文件中,局部变量和全局变量同名是允许的,局部变量的作用域内,全局变量被屏蔽掉,不起作用。
  3. 不同的函数可以定义相同名称的局部变量,因为局部变量的作用域被限制在它所在的函数。

5.4.3 静态局部变量

静态局部变量定义在某个函数内部,定义的格式为:
static 类型 <变量名表>;
静态局部变量兼具全局变量和局部变量的特性。
作用域:与局部变量相同。
生存期:全程。第一次调用该函数时,静态局部变量被存放在全局数据区中,如果定义没有被初始化,则被自动初始化为0(和全局变量一样)。以后再调用该函数时,不再为函数中的静态局部变量分配空间,而是自动使用上次调用后的局部变量。

#include<iostream>
void Func();
using namespace std;
int main()
{
  int n;
  for(n=1;n<=10;n++)
    Func();
  return 0;
}
void Func()
{
  static int s_a=0;
  int b=0;
  s_a++;
  b++;
  cout<<"s_a:"<<s_a<<"b:"<<b<<endl;
}
//b为局部变量,每次调用Func()结束后会释放,下次调用时会在栈区为 b 重新分配空间,而 s_a 定义为静态局部变量,存放在全局数据区每次调用结束后不会释放内存,再次调用时继承上次调用后的值。因此这个代码的结构是 b 的值一直是1,而 s_a 会递增。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值