目录
一.函数
(一).内联函数
1.使用场合和定义
记得以前看宏定义的时候看到说,为了尽量发挥编译器的作用,不提倡使用宏定义,而是建议用const常量和内联函数.
使用场合:协调效率和可读性之间的矛盾.
int IsNumber(char ch) { return ch>='0'&&ch<='9'?1:0;}
int main()
{
char ch;
while(cin.get(ch),ch!='\n')
{
if(IsNumber(ch))
cout<<"yes"<<endl;
}
return 0;
}
每次调用IsNumber函数花费很多时间效率低,但是若是直接嵌入代码可读性太差.
这时使用内敛函数:
inline int IsNumber(char ch) { return ch>='0'&&ch<='9'?1:0;}
他与一般函数不同之处只在于函数调用的处理。一般函数进行调用时要将程序执行权转到被调用函数中,然后在返回调用他的函数中;编译器在编译过程遇到inline时,将为该函数建立一段代码,在每次调用时直接将该段代码嵌入到被调用函数当中,从而将函数调用方式变为顺序执行方式.
2.注意事项
(1)在内联函数内不允许用循环语句和开关语句(嵌套,while都不行).若有,则编译器将该函数视为普通函数那样调用代码,递归函数(自己调用自己)是不能作为内联函数的。内联函数只适用于1—5行的小函数。
(2)内联函数的定义必须出现在内联函数第一次被调用之前。(建议放在头文件)
二.修饰词
(一).宏定义define
(1)简单的宏定义:
- #define <宏名> 常量串
- 例: #define PI 3.1415926
(2) 带参数的宏定义
- #define <宏名> (<参数表>) 表达式串
- 例: #define A(x) x*x
一个讲得很详细的链接:指路
(二).static
1.基础介绍
静态变量的类型说明符是static。
静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存储空间(在程序整个运行期间都不释放),也可以认为是其内存地址不变,直到整个程序运行结束(相反,而auto自动变量,即动态局部变量,属于动态存储类别,占动态存储空间,函数调用结束后即释放)。静态变量虽在程序的整个执行过程中始终存在,但是在它作用域之外不能使用。(比如局部变量就算是静态的,也不能在作用域外面使用)
所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量。
另外,属于静态存储方式的量不一定就是静态变量。 例如:外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量(静态全局变量)。(外部变量在这里应该是全局变量的意思吧)
静态变量可以在任何可以申请的地方申请,一旦申请成功后,它将不再接受其他的同样申请。
静态变量并不是说其就不能改变值,不能改变值的量叫常量。 静态变量其拥有的值是可变的 ,而且它会保持最新的值。说其静态,是因为它不会随着函数的调用和退出而发生变化。即上次调用函数的时候,如果我们给静态变量赋予某个值的话,下次函数调用时,这个值保持不变。
2、静态局部变量:
1、 Static类内部变量同auto自动变量(即未加 Static 声明的局部变量)一样,是某个特定函数的局部变量,即只能在定义该变量的函数内使用该变量,2者作用域相同;两者的不同在于:auto自动变量会随着函数被调用和退出而存在和消失,而static类局部变量不会,它不管其所在的函数是否被调用,都将一直存在;不过,尽管该变量还继续存在,但不能使用它。倘若再次调用定义它的函数时,它又可继续使用,而且保存了前次被调用后留下的值。换言之,Static类型的内部变量是一种只能在某个特定函数中使用,但一 直占据存储空间的变量。
2、函数体内如果在定义静态变量的同时进行了初始化,则以后程序不再进行初始化操作(出现在函数内部的基本类型的的静态变量初始化语句只有在第一次调用才执行)。
3、静态局部变量的初始化表达式必须是一个常量或者常量表达式。即使局部静态变量定义时没有赋初值,系统会自动赋初值0(对数值型变量)或空字符(对字符变量);静态变量的初始值为0。而对自动变量auto来说,如果不赋初值则它的值将是个不确定的值。
4、当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。(这里提到的副作用就是 别的源文件也可以使用这个全局变量 这是一个非常不稳定的事情)
这段代码为了说明static变量是可以被更改的,好愚蠢啊哈哈哈
#include<stdio.h>
#include<iostream>
using namespace std;
void main()
{
//int a = 2, i;
static int c = 3;
int a = 0;
cin >> a;
cout << "a:" << a << endl;
cin.get();
cout << "c:" << c << endl;
c = a;
cout << "c:" << c << endl;//验证static是可以改的 好愚蠢的代码
cin.get();
}
结果是:
4(这个是我输入的)
a:3
c:3
c:4
这段代码是为了说明静态局部变量的存储方式?或者说是生存期?
#include<iostream>
#include<vector>
using namespace std;
long factor(int n)
{
static long int f = 1; //static
f*=n;
return f;
}
void main()
{
int i;
for (i = 1; i <= 5; i++)
{
printf("%ld\n", factor(i));
//static long int f = 1;//这个没用的
//if (i==3)
//{
// f=6;
//}
}
cin.get();
}
结果:
1
2
6
24
120
3、静态全局变量
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。
这两者的区别在于:
1、非静态全局变量的作用域是整个源程序 ,当一个源程序有多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。
2、静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效,在同一源程序的其它源文件(即声明了该变量的CPP文件,或包含该变量声明头文件的CPP文件)中不能使用它。
由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
4、总结
从以上分析可以看出————
把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
因此static这个说明符在不同的地方所起的作用是不同的。应予以注意。
五大内存分区(貌似与编译原理中不一样,不过道理是一样的,实际存在的东西总是会与理论有一定差距的)
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
1.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
2.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。
3.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)
4.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
5.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
1)static静态变量会被放在程序的全局存储区中(即在程序的全局数据区,而不是在堆栈中分配,所以不会导致堆栈溢出),这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2)static静态变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。——有信息隐蔽的作用。 (外部的 Static声明亦可用于声明函数。如果将函数声明为Static类型,则该函数名除了对该函数声明所在的文件可见外,其他文件均无法访问。 )
3)若全局变量仅在单个C文件中访问,则可将此变量改为静态全局变量,以降低模块间的耦合度;若全局变量仅由单个函数访问,则可将此变量改为该函数的静态局部变量,以降低模块间的耦合度。
4)设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题。 所谓"可重入"(也可以说是可预测的),即:只要输入数据相同就应产生相同的输出。
函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。
如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。(现在我终于明白了 因为如果不是static 局部变量随着函数调用的结束本身也消失了 那么这个指针完全指向了一个无意义的地址 加了static修饰符就不一样了)
三.指令
(一).条件编译指令
条件编译指令包括:
#if #else #ifdef #ifndef #endif #undef
1.用宏名作为编译条件
格式
#ifdef 宏名
程序
#else
程序
#endif
程序可以是一般程序或是编译预处理指令,可以在前面安排宏定义来控制编译不同的程序段.(用处一)
比如在程序调试期间,为了输出一些语句
#define DEBUG
#ifdef DEBUG
cout<<"a="<<a;
#endif
调试完程序之后删除#define DEBUG就好了.
#ifndef意思就是#ifdef的反过来,当没有定义的时候.
#undef指令用来取消#define指令所对应的符号,和#define一起组合用来打开和关闭符号
我自己用的场合和报错情况:
#ifndef LOOPCLOSING_H
#define LOOPCLOSING_H
#ifndef DEBUG
#define DEBUG
#endif
正文
#endif
#ifndef DEBUG
#define DEBUG
#endif//DEBUG
#ifndef KEYFRAMEDATABASE_H
#define KEYFRAMEDATABASE_H
正文代码
#endif//KEYFRAMEDATABASE_H
但是如果把DEBUG的endif放在后面 就会报错 一给磨牙???
用处二:防止文件重复包含的问题
一般来说头文件<iostream.h>里面包含<mem.h>的,但是有可能会有用户同时包含这两个文件,导致mem.h的重复包含
所以在mem.h里以下宏定义
#define MEM_H
在iostream.h中也有如下条件包含指令
#ifndef MEM_H
#include<mem.h>
#endif
2.用表达式的值作为编译条件
格式
#if 表达式
程序
#else
程序
#endif