Tips: 1. 本人当初学习C/C++的记录。 2. 资源很多都是来自网上的,如有版权请及时告知! 3. 可能会有些错误。如果看到,希望能指出,以此共勉!
C及C++中变量的存储方式有三种:自动、静态、动态。在C++11中,额外增加了一种:线程存储方式(关键字thread_local)。(C没有)以下所谓的可见并不是指编程者可以直接使用,这里的可见是对编译器/连接器来说的,它们在编译链接时能够发现其他文件中的变量。如果编程者要使用,还是必须显示声明。 C++标准使用翻译单元表示编译的单位,下面以文件为编译单元来说的。
自动类型:程序执行到其所属的函数或代码块时,才为其分配内存,直到其所属的函数或者代码块结束,内存就被释放了。 静态类型:在整个程序运行过程中都存在。 动态类型:从new开始一直到delete或者程序结束。 线程存储方式:与所属的线程同生命周期。
C中: auto关键字来源于C语言,唯一作用就是显示指明所定义的局部变量为自动存储类型(默认就是auto,常省略),而且auto只能用于修饰默认为自动存储类型的变量(意味着,想定义auto int a = 0; 的全局变量是不可以的),因此几乎没有人使用它。 C++中:但是从C++11开始,auto关键字有了全新的用法:自动判断数据类型,原来的用法被视为非法。注意:C++11中,auto的变量必须在定义时初始化。 如上例子中,变量a将自动判断为int型,因为其被赋值为整型10。 当然,auto关键字不仅仅是为了以上的简单情况,对于上面的情况来说,auto显得有点鸡肋。对于复杂情况,例如标准模块库(STL),自动判断才更有用。如下: C++98中: std::vector<double> scores; std::vector<double> iterator pv = scores.begin(); C++11中: std::vector<double> scores; auto pv = scores.begin(); // 自动判断,简化代码
C中: register关键字最初由C语言引入,它建议编译器采用CPU的寄存器来存储指定的自动类型变量,旨在提高变量的访问速度。 C++中:其用来告诉编译器被其所修饰的局部变量使用频繁,编译器可以做特殊处理。 C++11开始:其作用只是显示指出定义的局部变量是自动存储类型,而且只能修饰原本就是自动类型的变量。这与auto关键字原来的用法完全相同。保留的唯一原因就是代码兼容。
主要用于C++中:该关键字跟constant(既C++中的const)是反义词,用来指出即使结构体或者类为const类型,其成员只要被mutable修饰,值仍然可以被修改。该关键字只用在类中或者结构体中,用来修饰单个变量是不被允许的。
默认情况下,全局变量的可见性是所有文件,但是被const修饰的全局变量却只在当前文件可见,和使用了static修饰一样。这样,在头文件中定义的const变量,在被多个文件包含时,不会再出现重复包含。
如果需要使定义的const变量为外部可见,则需要使用extern关键字覆盖。 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;例如: const int a; // a 为常量 int const a; // a 为常量,同第一种 int const *a; // a 为指向常整型的指针 const int *a; // a 为指向常整形的指针。指向的整型数是不可修改的,但指针可以,此最常见于函数的参数,当你只引用传进来指针所指向的值时应该加上const修饰符 int * const a; // a 为 指向整形的常指针。指针指向的整型数是可以修改的,但指针是不可修改的 int const * a const; // a 为指向常整形的常指针。指针指向的整型数是不可修改的,同时指针也是不可修改的 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如: const classA operator*(const classA& a1,const classA& a2); // operator*的返回结果必须是一个const对象。如果不是,下面这样的变态代码也不会编译出错: classA a, b, c; (a * b) = c; // 对a*b的结果赋值extern可以置于变量或者函数前,以标识变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。extern也可用来进行链接指定。也就是说extern有两个作用:
当它与”C”一起连用时,如: extern “C” void fun(int a, int b);或者直接用大括号包含代码块时,则告诉编译器在编译fun这个函数名或代码块时按着C的规则去翻译相应的函数名而不是C++规则。 extern “C” { … // 代码 }当extern不与”C”在一起修饰变量或函数时,如在头文件中: extern int g_Int;它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块A(编译单元)中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被其他模块引用,使用前就要在使用的模块中用extern来声明。 在定义变量的时候,这个extern可以被省略(定义时,默认均省略);在声明变量的时候,这个extern必须添加在变量前。
extern int a; // 声明一个全局变量a int a; // 定义一个全局变量a extern int a = 0 ; // 定义一个全局变量a 并给初值。 int a = 0; // 定义一个全局变量a,并给初值,等价于上面定义和声明的区别:定义要为变量分配内存空间;而声明不需要为变量分配内存空间。定义只能一次,声明可多次
由于函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体(还有以分号结尾),所以函数定义和声明时都可以将extern省略掉。关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有区别: extern int func(); 和int func(); // 等价
上例中,main函数中调用了b.c中定义的函数,只要在第一次使用之前声明了就可以。函数声明,不管在哪里(跨文件),都可以省略extern关键字。 extern与#include “*.h”的区别
如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。如果一个文件(假设文件名A)要大量引用另一个文件(假设文件名B)中定义的变量或函数,则使用头文件效率更高,程序结构也更规范。其他文件(例如文件名C、D等)要引用文件名B中定义的变量或函数,则只需用#include包含文件B对应的头文件(当然,这个头文件只有对变量或函数的声明,绝不能有定义)即可。头文件对计算机而言没什么作用,她只是在预编译时在#include的地方展开一下,没别的意义了,其实头文件主要是给别人看的。改成任意扩展名都可以。所以,头文件中放的都是声明。静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存储空间(在程序整个运行期间都不释放),也可以认为是其内存地址不变,直到整个程序运行结束(相反,动态局部变量,属于动态存储类别,占动态存储空间,函数调用结束后即释放)。静态变量虽在程序的整个执行过程中始终存在,但是在它作用域之外不能使用。 另外,属于静态存储方式的量不一定就是静态变量。 例如:外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。
所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量。静态变量可以在任何可以申请的地方申请,一旦申请成功后,它将不再接受其他的同样申请。静态变量并不是说其就不能改变值,不能改变值的量叫常量。 其拥有的值是可变的 ,而且它会保持最新的值。说其静态,是因为它不会随着函数的调用和退出而发生变化。即上次调用函数的时候,如果我们给静态变量赋予某个值的话,下次函数调用时,这个值保持不变。有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。函数内部的Static变量同自动变量(即未加 Static 声明的局部变量)一样,是某个特定函数的局部变量,即只能在定义该变量的函数内使用该变量,两者作用域相同;两者的不同在于:自动变量会随着函数被调用和退出而存在和消失,而static类局部变量不会,它不管其所在的函数是否被调用,都将一直存在;不过,尽管该变量还继续存在,但不能使用它。倘若再次调用定义它的函数时,它又可继续使用,而且保存了前次被调用后留下的值。换言之,Static类型的内部变量是一种只能在某个特定函数中使用,但一直占据存储空间的变量。 函数体内如果在定义静态变量的同时进行了初始化,则以后程序不再进行初始化操作(出现在函数内部的基本类型的静态变量初始化语句只有在第一次调用才执行)。而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。 静态局部变量的初始化表达式必须是一个常量或者常量表达式。即使局部静态变量定义时没有赋初值,系统会自动赋初值0(对数值型变量)或空字符(对字符变量);静态变量的初始值为0。而对自动变量来说,如果不赋初值则它的值将是个不确定的值。 当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。 注:局部静态变量占用内存时间较长,并且可读性差,因此,除非必要,尽量避免使用局部静态变量。
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于: 非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。
C++和C一样,不允许一个函数中定义另一个函数,所以函数默认都是静态存储的。函数的定义和声明默认情况下是extern的。默认情况下,函数在程序执行的整个过程中都是存在的。其作用域为所有文件可见,这就要求多文件中不能定义同名函数。在函数的返回类型前加上关键字static,函数就被定义成为静态函数。静态函数只是在声明他的文件当中可见,不能被其他文件所用。定义静态函数的好处:
其他文件中可以定义相同名字的函数,不会发生冲突静态函数不能被其他文件所用。静态函数覆盖外部同名函数。(C++11支持) 静态函数必须在第一次调用之前就被声明了,例如:头文件中声明没有加static,只是在定义中加了static,是没有用的! // 1.c #include "1.h" #include <stdio.h> static int g_current = 0; // 静态全局变量 void func1() { g_current++; static int count = 0; // 静态局部变量。该语句只在第一次调用时起作用,多次调用该函数时,不在起作用 count++; printf("func1: g_current = %d count = %d\n", g_current, count); count = 1; // 下一次调用该函数时,count将为 1; } // 1.h void func1(); #include <stdio.h> #include "1.h" //extern int g_current; //1.c中g_current为static,则此处将出现链接错误 int main() { func1(); func1(); // 两次调用时,输出不同 return 0; } 一个参数既可以是const还可以是 volatile。例如:一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它 一个指针可以是volatile。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。
ZC·Shou 认证博客专家 砖家 码字员 没了 进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!进步始于交流,收获源于分享!