C++学习笔记
存储持续性、作用域和链接性
C++和C语言对于变量定义的方式类型,定义的不同方式确定了变量的生存周期、作用范围以及可以被谁使用的“权限”问题。
存储持续性
一般来讲我们把存储的持续性简称为变量在程序中定义的位置,有以下三个位置:
1.自动存储持续性:简称自动变量,该变量定义在具体的函数块中,并且不加(static)这种修饰符,该类变量从函数被调用时代码块创建开始到函数执行结束时被释放。
2.静态存储持续性:在函数块外定义的变量(全局变量)和使用关键字static定义的变量的存储特性都是静态的,该类变量在程序的整个生命周期都存在。
3.动态存储持续性:这类内存一般是使用malloc或new申请,通过free或delete进行释放。需要程序员自己进行内存的管理。
如下面这个例子:
//存储数据的三种方案
#include <iostream>
using namespace std;
int global_value = 1; //静态存储持续性
void func1(void);
void func1(void)
{
int func1_auto_value = 0; //自动存储持续性
static int func1_static_value = 0; //静态存储持续性
}
int main(int argc, char **argv)
{
int auto_value = 0; //自动存储持续性
int *p_value = NULL;
func1();
// p_value为自动存储持续性,而p_value所申请的内存为动态存储持续性
p_value = (int *)malloc(sizeof(int) * 100);
if(p_value == NULL){
cout << "the memory is full!" << endl;
exit(1);
}
free(p_value); //p_value所申请的内存需要及时释放
return 0;
}
作用域
关于作用于只要记住以下几点即可:
链接性
关于链接性建议大家可以阅读一本较为底层的书《程序员的自我修养》,其中的部分章节详细的介绍了静态链接、动态链接以及符号相关的知识。
如果是linux或unix操作系统建议大家熟练掌握,objdump和redelf这两个可以查看汇编以及底层链接属性的命令,对于寄存器级别的调试非常有用。
说明符和限定符
在C++中我们还需要注意存储说明符和cv限定符。这里和C语言有着少许差异和扩充。
说明符
说明符包含以下几个:
auto (修饰自动类型变量)
register (用于声明中尽量把变量放置在寄存器中)
static (表示内部链接性,修饰的变量不再是全局符号)
extern (引用外部的定义变量或者函数)
thread_local(C++11新增)
mutable
其中需要着重解释下thread_local和mutable
thread_local相当于把修饰的变量当做是线程内部的一部分。
//thread_local test
thread_local int value = 1;
void func1(void);
void func1(void)
{
m.lock();
value++; //value编程了每个线程内部的变量,各个线程的value不会共享
m.unlock();
}
int main(int argc, char **argv)
{
std::thread t1(func1);
std::thread t2(func1);
t1.join();
t2.join();
// value is 1
return 0;
}
mutable的用来指定即使结构体或者类变量被定义为const,但是被mutable修饰的变量依然可以修改。
// mutable test
struct Test
{
int a;
mutable int b;
};
int main(int argc, char **argv)
{
const Test test1 = {12, 20};
test1.a = 20; //false
test1.b = 30; // true
return 0;
}
cv-限定符
const (常性)
volatile (去掉编译器的优化)
const修饰的变量在定义之后不可以被修改(实际上这句话是有缺陷的)。
//const修饰的变量不能被修改的探讨
int main(int argc, char **argv)
{
const int value = 10;
value = 30; //false
int *p_int = (int *)&value;
*p_int = 30; //true
}
也就是说被修饰的value当然不能直接更改,但是如果有int *指针指向了他,使用指针去间接修改value,这种漏洞编译器是很难察觉的,但是作为程序员我们不能去欺负编译器的粗心!!!
volatile的存在是因为在程序中如果发现某个值被多次赋值,则编译器会进行优化,不再每次都从内存取值,而是把该值放在了寄存器中,如果是单线程程序当然这是一个比较好的优化,但是对于多线程程序来说,在多次赋值的期间该变量有可能被其他线程已经修改,这样就会造成错误赋值的过程。为了避免这种过度的优化,我们使用了volatile关键字,这样就会提示编译器每次都去内存中取值。