一、const 关键字总结:
作为一个程序员,我们看到关键字const时,首先想到的应该是:只读。因为,它要求其所修饰的对象为常量,不可对其修改和二次赋值操作(不能作为左值出现)。看几个例子的中const作用:
1. 修饰常量
用const修饰的变量是不可变的,对const变量赋值:一是在声明时对变量初始化,二是在函数中声明const形参,函数调用时会得到实参的值。以下两种定义形式在本质上是一样的:
const int a = 10;
int const a = 10;
2. 修饰指针
如果const位于*的左侧,eg:const int* a,则const就是用来修饰指针所指向的变量,此时为常量指针,即指针指向*a为常量;
如果const位于*的右侧,eg:int* const a,const就是修饰指针本身,此时为指针常量,即指针本身a是常量。
规则:
- const离谁近,谁就不能被修改;
- 因为常量在定义以后就不能被修改,所以使用const定义变量时必须初始化。
因此,推荐使用int const* p,而不是使用const int* p(虽然两者意义完全一样),这样更容易理解。
int a = 10;
const int* p = &a; // 常量指针;指针指向的内容不能变,*p不能变
int const* p = &a; // 同上
int* const p = &a; // 指针常量;指针本身不能变, p不能变
const int* const p = &a; // 两者都不能变
int const* const p = &a; // 同上
3. 修饰引用
以下两种定义形式在本质上是一样的:
int a = 10;
const int& b = a;
int const& b = a;
4. 修饰函数参数
用const修饰函数参数,传递过来的参数在函数内不可以改变。
void func (const int &n)
{
n = 10; // 编译错误
}
5. 修饰函数返回值
用const修饰函数返回值的含义和用const修饰普通变量以及指针的含义基本相同。
注:
1、若函数的返回值是指针,且用const修饰,则函数返回值指向的内容是常数,不可被修改,此返回值仅能赋值给const修饰的相同类型的指针。
2、不要把函数int fun1() 写成const int func1(),当函数返回值是数值(by value)时,返回值会被复制到外部临时的存储单元中,故返回值用const 修饰没有任何代价的。
3、如果返回值是对象,将函数A fun2() 改写为const A & fun2()的确能提高效率。但此要注意,要确定函数究竟是想返回一个对象的“copy”,还是仅返回对象的“别名”便可,否则程序会犯错。
const int* func() // 返回的指针所指向的内容不能修改
{
// return p;
}
二、static 关键字总结:
下面我们看个例子:
(1)变量不加 static 修饰
#include <stdio.h>
void test()
{
int a = 0;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
for (i = 0; i < 8; i++)
{
test();
}
return 0;
}
(2)变量被 static 修饰
#include <stdio.h>
void test()
{
static int a = 0;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
for (i = 0; i < 8; i++)
{
test();
}
return 0;
}
分析:
- 不加static修饰,为自动变量,存储于堆栈中。函数或代码块内部中的变量在函数或者代码块执行完毕后就自行销毁了,每次执行都会重新分配内存,每次执行完都会销毁。
- 加 static 修饰,为静态变量,存储于静态内存中,在整个程序执行过程中一直存在。static的局部变量,并不会随着函数的执行结束而被销毁,当它所在的函数被再次执行时,该静态局部变量会保留上次执行结束时的值,直到程序结束 static 变量才会被回收。
总结:
- 当 static 作用于函数定义时,或者用于代码块之外的变量声明时,static关键字用于修改标识符的链接属性。从外部链接属性(external)变为内部链接属性(internal),但标识符的存储类型和作用域不受影响。也就是说变量或者函数只能在当前源文件中访问,不能在其他源文件中访问。
- 当static 作用于代码块内部的变量声明时,static关键字用于修改变量的存储类型。从自动变量变为静态变量,但变量的属性和作用域不受影响。
拓展 :
a) static在面向过程编程中的使用场景包括三种:
1) 修饰函数体内的变量(局部)
2) 修饰函数体外的变量(全局)
3) 修饰函数
第一种情况,static延长了局部变量的生命周期,static的局部变量,并不会随着函数的执行结束而被销毁,当它所在的函数被第再次执行时,该静态局部变量会保留上次执行结束时的值
对于后面的两种情况,static是对它修饰的对象进行了作用域限定,static修饰的函数以及函数外的变量(全局变量),都是只能在当前的源文件中被访问,其它的文件不能直接访问。当多个模块中有重名的对象出现时,我们不妨尝试用static进行修饰。
b)在面向对象编程中,static可以被用来修饰类内数据成员和成员函数。
1) 修饰数据成员
- 被static修饰的数据成员实际上相当于类域中的全局变量。因此,对于类的每个对象来说,它是共有的。它在整个程序中只有一份拷贝,只在定义时分配一次内存,供该类所有的对象使用,其值可以通过每个对象来更新。由于静态数据成员存储在全局数据区,因此,在定义时就要分配内存,这也就导致静态数据成员不能在类声明中定义。
- 静态数据成员的初始化举例: int ClassTest::num = 0;
- 静态数据成员的访问举例: ClassTest ClassInstance; ClassInstance.num 或者 ClassTest::num
#include <iostream>
class CTest
{
public:
CTest() {};
~CTest() {};
public:
static int num; //声明
};
int CTest::num = 10; //定义
int main(void)
{
CTest obj;
std::cout << obj.num << std::endl; //访问
std::cout << CTest::num << std::endl; //访问
return 0;
}
2)修饰成员函数
- 同静态数据成员一样,静态成员函数也是属于类,而不属于任何一个类的实体对象,因此,静态成员函数不含有this指针。同时,它也不能访问类中其它的非静态数据成员和函数。(非静态成员函数可以访问静态数据数据成员和静态成员函数)
- 静态成员函数的访问方式: 既可以通过访问操作符(.)或者(->)来访问,也可以通过 <类名> :: <函数名> 的方式访问。
- 参考博客------https://blog.csdn.net/yayawy/article/details/51425889#commentBox
三、extern关键字总结:
1、对于变量:
extern关键字可以用来修饰变量,表示该变量在别的文件中已有声明。
佷显然使用extern关键字修饰的变量都是全局变量,因为在其它文件中引用局部变量是不会有意义的,也超出了局部变量的作用域。
注:只能用于扩展没有被static关键字修饰的全局变量。默认情况下全局变量只能在定义它的文件中使用(从定义该全局变量开始到所在文件的文件尾),但如果在另一个文件中将这个变量声明为外部变量,那么这个变量的作用域将被扩展到另外一个文件中。也可以在定义全局变量之前声明该变量,从而在文件中可以在定义该全局变量前使用该全局变量。
简单的理解就是:若一个变量需要在同一个工程中不同文件里直接使用或修改,则需要将变量做extern声明。只需将该变量在其中一个文件中定义,然后在另外一个文件中使用extern声明便可使用,但两个变量类型需一致。
eg:
方法一:
在1.c中定义全局变量
int i;
在2.c和3.c中都用
extern int i;
声明一下就可以使用了
方法二:
在头文件a.h 中声明
extern int i;
在其他某个c文件中定义
int i =0;
其他要使用i变量的c源文件只需要include"a.h"就可以
2、对于函数:
在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数是外部函数,可以供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。在文件中要调用其他文件中的外部函数,则需要在文件中用extern声明该外部函数,然后就可以使用。
static 关键字和extern关键字比较:
从某种意义上来说,extern关键字与static关键字是相反的,extern关键字是声明想要调用的外部变量和函数。而static关键字正好声明为自己使用。当然使用static关键字声明一个变量时,同时也定义了该变量。而extern声明一个变量时,仅是声明,因为该变量早已在其他地方定义。
四、volatile关键字总结:
volatile关键字的基本作用:一个使用volatile关键字定义变量,其实就是告诉编译系统这变量可能会被意想不到地改变。那么编译时,编译器就不会自作主张的去假设这个变量的值,而进行代码的优化了。确切的说就是,编译器在编译代码时,优化器每次遇到这个变量,都会到内存中重新读取,而不会使用保存在寄存器里的备份来对代码进行优化。
我们已经了解了volatile关键字的基本作用。那么,在什么情况下使用volatile关键字呢?一般说来,在如下的几种情况通常会使用volatile关键字:
- 在中断服务程序中修改的,供其它程序检测的变量,通常需要定义为volatile;
- 在多任务环境下,各任务间共享的标志,通常也需要定义为volatile;
- 存储器映射的硬件寄存器通常也需要定义为volatile,因为每次对它的读写都可能有不同意义;
编译优化时,为提高存取速度,有时会把变量读取到寄存器,方便读取;但有时别的线程改变了变量的值,但寄存器值不变,造成程序读取值不一致,所以使用volatile从变量内存中读取。
修饰某个变量,表明某个变量的值可能随时被外部改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新读取,从变量的地址中(内存中)读取数据。
volatile的作用是告知编译器,它修饰的变量随时都可能被改变,因此,编译后的程序每次在使用该变量的值时,都会从变量的地址中读取数据,而不是从寄存器中获取。
参考博客----https://blog.csdn.net/ijn842/article/details/81273232