一、函数定义
<函数类型> <函数名>(形式参数表)
{
函数内容
}
形式参数是指这些参数实际上并不存在,只是在形式上代表运行时实际出现的参数。形式参数有如下特点:
1) 当被调用的函数有参数时,主调函数和被调用函数之间通过形参实现数据传递。
2) 函数的形参进在函数被调用时才由系统分配内存,用于接受主调函数传递来的实际参数。
#include <QCoreApplication>
int max(int x,int y)
{
return x>y?x:y;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int a = 10,b = 9;
int res = max(a,b); // a,b为实参
qDebug("%d",res);
return a.exec();
}
参数传统需要遵守的规则是,形参和实参个数必须相同,形参和实参的数据类型相同,形参和实参的顺序必须一一对应。
二、函数的参数传递方式分析
详见《函数的参数传递方式分析》
三、变量的存储类型
存储器 | register | auto | static | extern |
存储位置 | 寄存器 | 主存 | ||
生存期 | 动态生存期 | 永久生存期 | ||
作用域 | 局部 | 局部或全局 | 全局 |
3.1 变量的生存期
1) 在编译时分配存储单元,程序执行开始后这种变量即被创建,程序执行结束即被撤销。这种变量的生成周期为程序执行的整个过程,在该过程中占有固定的存储空间。通常称它们为永久存储。
2)只在程序执行的某一段时间内存在。例如,函数的形参和在函数体或分程序中定义的变量,知识在程序进入该函数或分程序时才分配存储空间,当该函数或分程序执行完后存储空间又被撤销,这种存储方式称为动态存储。
3.2 动态变量
动态变量时在程序执行的某一时刻被动态地建立并在另一时刻被动态撤销的一种变量,它们存在于程序的局部,也只在这个局部中可以使用。
1) 自动变量是局部变量
2) 在对自动变量赋值之前,它的值是不确定的。
3) 对同一函数的两次调用之间,自动变量的值是不保留的。
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int i;
qDebug("%d",i);
return a.exec();
}
输出:1
3.3. 寄存器变量
寄存器变量具有与自动变量完全相同的性质,当把一个变量制定为寄存器存储类别时,系统就讲它存在CPU中一个寄存器中。通常把使用频率较高的变量定义为register类别。
3.4 静态变量
静态变量时相对于动态变量的。
1) 静态变量的存储空间在程序的整个运行期间是固定的(static),而不是像动态变量是在程序执行当中被动态建立、动态撤销的。一个变量被指定为静态变量(固定),在编译时就为其分配存储空间,程序一开始执行便被建立,直到该程序执行结束都是存在的。而不像动态变量仅存在于函数或分程序被调用期间。
2)静态变量的初始化使在编译时进行的。在定义时只能使用常量或常量表达式进行显示初始化,未显示初始化时,编译将把它们初始化为0;
static 数据类型 变量名【=初始化常数表达式】
动态变量时在程序运行过程中被创建的,因而没有初始化问题,只有静态变量和外部变量才是在编译时被创建的,才有初始化问题。
3) 在函数多次被调用的过程中,静态局部的值具有可继承性;
4)静态局部变量的值只能在本函数(分程序)中使用
在一个函数中定义的变量时局部变量,它们只能自其局部范围内被使用,static类别的变量在函数调用结束后其存储单元不释放,其值具有继承性。
#include <QCoreApplication>
int add()
{
int sum = 0;
return ++sum;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("sum:%d",add());
qDebug("sum:%d",add());
qDebug("sum:%d",add());
return a.exec();
}
输出结果:
1
1
1
#include <QCoreApplication>
int add()
{
static int sum = 0;
return ++sum;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("sum:%d",add());
qDebug("sum:%d",add());
qDebug("sum:%d",add());
return a.exec();
}
输出结果:
1
2
3
3.5 外部变量
定义所有函数之外的变量,称为外部变量。外部变量是全局变量,作用域是从定义开始到文件末尾
#include <QCoreApplication>
int sum = 0;
int add()
{
return ++sum;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("sum:%d",add());
qDebug("sum:%d",add());
qDebug("sum:%d",add());
return a.exec();
}
输出结果:
1
2
3
1) 限定本文件的外部变量只能在本文件使用,可以在外部变量前加上static,使其局部化, 称作为静态外部变量
#include <QCoreApplication>
static int sum = 0;
int add()
{
return ++sum;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("sum:%d",add());
qDebug("sum:%d",add());
qDebug("sum:%d",add());
return a.exec();
}
输出结果:
1
2
3
注意:外部变量在是在编译时分配存储单元的,它也不随函数的调用与退出而建立与撤销,也就是它的生存周期是整个程序的运行期间。外部加上static只是限定其作用域(与生存周期是两个概念)在本文件使用,这样好处是当有多个文件时,其他文件命名时可以不用考虑是否重名。
2) 一个外部变量的作用域是其定义点到本文件末尾,对于位于定义点之前的函数,可以用extern说明符使变量的作用域扩充到需要用到它的函数。
#include <QCoreApplication>
//! 声明为外部变量
extern int x,y;
void first()
{
x = 10;
qDebug("x:%d,y:%d",x,y);
}
//! 定义外部变量
int x,y;
void second()
{
qDebug("x:%d,y:%d",x,y);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
x = 100;
qDebug("x:%d,y:%d",x,y);
first();
second();
return a.exec();
}
输出结果:
x:100,y:0
x:10,y:0
x:10,y:0
3.)如果将外部变量扩充到其他文件,这时需要在用到这些外部变量的文件中对变量用extern声明即可。
#include <QCoreApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int x=1,y=2;
int z = mytest.add(x,y);
qDebug("%d",z);
return a.exec();
}
#include "test.h"
class test mytest;
int test::add(int a, int b)
{
return a+b;
}
#ifndef TEST_H
#define TEST_H
class test{
public:
int add(int a, int b);
};
extern class test mytest;
#endif // TEST_H
3.6 外部变量声明与定义注意事项
在C++中定义全局变量是应该尽量在.cpp文件中定义,而不要在.h 文件中 定义,定义好了之后,可以在.h文件中利用 extern关键字进行 声明.如果在.h文件中定义的话,多层包含可能会引起重复定义的错误.下面是一个示例
在base.cpp中定义全局变量
base.cpp
- int g_MaxTime;
- int g_MinTime;
- int g_MaxCount;
- int g_MinCount;
base.h
- extern int g_MaxTime;
- extern int g_MinTime;
- extern int g_MaxCount;
- extern int g_MinCount;
然后其他文件要使用这些变量的时候
只要#include "base.h"就可以了,而且不会引起重复定义的错误
3.7 块级变量
C 语言还允许出现单独的代码块,它也是一个作用域。
#include <QCoreApplication>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int n = 100;
{
int n = 200;
qDebug("2-n:%d", n);
}
qDebug("1-n:%d", n);
return a.exec();
}
输出结果:
2-n:200
1-n:100
3.8 程序内存分配
在内存的数据区分为两个部分:静态存储区和动态存储区,动态变量和形参存放在动态存储区,而静态局部变量和外部变量存在静态存储区中。
详见:C/C++程序内存的分配
四、预处理
详见《深入浅出之预处理》。
预处理的输出是“翻译单元”,他是存放在内存中的临时文件,编译器接受预处理输出,并将源代码转换成包含机器语言指令的目标文件。
4.1 宏替换
1) 字符串替换
格式: #define 宏名 宏体
其中宏名与宏体均为字符串。预处理时,将把程序中该宏定义之后的所有的宏名用宏体替换。
如:
#define PI 3.14
宏替换的好处:
(1) 提高程序的可读性
(2)易修改性好
2) 带参数宏定义格式
#define 标识符(参数表) 宏体
例如:
#define PI 3.14
#define RAD 2.0
#define AREA return(PI*RAD*RAD)
带参数宏替换与函数区别:
(1) 时空效率不同,带参数宏替换效率比函数高
(2) 宏虽然可以带有参数,但宏替换过程中不像函数那样可以进行参数值的计算、传递及结果返回等操作;宏替换只是简单的字符替换,不进行计算。因此一些过程中不能用宏替换,比如递归调用;
4.2 书写#define命令应注意事项
1) 宏名与宏体之间应以空格相隔,所以宏名中不能含有空格
2) 宏名不能用引号括起来
比如#define “YES” 1 不进行宏替换
3) 较长的宏定义在一行中如果写不下时,可以在本行末尾使用反斜杠表示要续行
#define PRX printf(“ssss \
ddddd”)
4) 对带参数宏定义,宏体及其各个形参应该用圆括号括起来
#include <QCoreApplication>
//#include "test.h"
#define f(x) x*x
#define g(x) (x)*(x)
#define h(x) ((x)*(x))
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("1:%d",f(2+2));
qDebug("2:%d",g(2+2));
qDebug("3:%d",h(2+2));
return a.exec();
}
输出:
1:8 【2+2*2+2=8】
2:16【(2+2)*(2+2)=16】
3:16【((2*2)*(2*2))=16】
5) 宏定义不是C语言,不必再行末加分号
6)宏被定义后,一般不能再重新定义,而只能使用#undef命令终止该宏定义作用域(#undef 宏名)
4.3 文件包含
格式1: #include “文件标识符”
按这种格式定义时,预处理程序首先在原来的源文件目录中检索该指定的文件;如果没有找到,则按系统指定的标准方式检索其他文件目录,直至找到为止;
格式2:#include <文件标识符>
按这种格式定义时,预处理程序只在系统库中寻找指定文件。
4.4 条件编译
条件编译可使同一源程序在不同的编译条件得到不同的目标代码。
格式1:
#ifdef 标识符
程序段1
#else
程序段2
#endif
格式2:
#ifndef 标识符
程序段1
#else
程序段2
#endif
在工程的地方条件指示符#ifndef的最主要目的是防止头文件的重复包含和编译。
#ifndef _头文件名_H
#define _头文件名_H
....
#endif
格式3:
#if 表达式1
程序段1
#elif 表达式2
程序段2
#endif