一、乱世枭雄:static和extern:改变数据类型在内存中的位置或者改变作用域,作用在数据类型上
1、解释:
在C的世界里,不同代码“国度”以.c文件为国界分隔开来,在单个国家里有不同函数占山为王,每个C程序世界里只有一个君主(MAIN),main通过下传指令(参数),调用各种军阀(函数),来掌控。某军阀(函数)心怀叵测,不想单纯听从于main的指挥调度,树立了自己的政权旗帜static。static不同听从于main的调度,自己做主,私藏金库(空间)。不同的国家(不同的.c文件)之间通过extern相互私通,传递消息。
2、static:1)、修饰局部变量:局部变量存放在栈区,staic修饰的话,放在静态数据区,生命周期从该语句块执行结束变到了直到持续整个程序执行为止,生命周期存储空间变 化,作用域没有变化。
2)、修饰全局变量,对于一个修饰的全局变量,它既可以在本源文件中被访问,也可以在同一个工程的其他源文件被访问。如果对其中某个源程序文件中的全局变量 前加 static,则更改作用域,由原来的整个工程可见变为本源文件存在。
3)、修饰函数:与修饰全局变量类似,更改作用域。
3、extern:指当前变量或函数不是在本源文件内声明的,它是外部变量外部函数,试图引用一个外部声明的全局变量或函数时,可以在其前面加上extern。
1)、extern变量名
2)、extern函数:函数默认是外部的(在函数体内或函数体外声明一个外部函数,extern关键字可以省略),如果不想让其他源文件链接到,在函数前面加static。
*static:继承中静态属性、静态函数,存在在整个类里而非单个对象
二、铁布衫:const:改变数据类型的只读属性,作用在数据类型上
1、修饰对象:readonly,不仅仅可以用来修辞基本类型,还可以修辞一些结构类型及其参合体,如数组、指针、指针数组、结构体数组、结构体指针数组等。
2、使用方法:将类型去掉,看const修辞谁,谁就拥有了铁布衫谁就是readonly。(不包含指针常量和常量指针的辨析)
const int n=5;
int const m=10;
上述两个变量n和m是同一个类型,都是const int(整型恒量),C++规定:const关键字放在类型或变量名之前等价的。
3、const的位置(指针):常量指针和指针常量
const int *p;
int const *b;
int* const c=p;
第一个和第二个是:常量指针:指向的内容是常量,指针本身可以修改,const int 类型指针(const直接修饰int)
第三个是:指针常量:指针本身是个常量,指针只向的内容可以修改,int类型的const指针
当const在*之前是常量指针,在*之后是指针常量
char ** p1; //指向char类型的指针的指针
const char **p2; //指向const char类型的指针的指针
char *const *p3; //指向char类型的const指针
const char *const * p4; //指向const char类型的const指针
char ** const p5; //指向char类型的指针的const指针
const char ** const p6; //指向const char类型的指针的const指针
char * const * const p7; //指向char类型const指针的const指针
const char * const * const p8; //指向const char类型的const指针的const指针
一些声明是不能编译通过的,因为需要在声明的同时进行初始化。
*变量常量const、预处理指令#define
三、隐形刺客auto:局部变量前特有的关键字,作用在变量上
解释:C程序是面向过程的,在C代码中会出现大量的函数模块,每个函数都有其生命周期(也称作用域),在函数生命周期中声明的变量通常叫做局部变量,也叫作自动变量。会在内存栈上进行分配。
四、闪电飞刀:register:改变变量从内存到寄存器上,作用在变量上
1、作用:意味着该变量会作为一个寄存器变量,让该变量的访问速度达到最快。例如,一个程序逻辑中有一个很大的循环,循环中有几个变量要频繁进行操作,这些变量可以声明为register类型。
2、寄存器变量:寄存器变量是指一个变量直接引用寄存器,也就是对变量名的操作的结果是直接对寄存器进行访问。
3、 寄存器:是CPU的亲信,CPU操作的每个操作数和操作结果,都用寄存器来暂时保存,最后才写入到内存或从内存中读出。变量的值通常保存在内存中,CPU对变量进行读取先是将变量的值从内存中读取到寄存器中,然后进行运算,运算完将结果写回内存中。
4、使用寄存器变量请注意:
1)待声明为寄存器变量类型应该时CPU寄存器所能接收的类型,意味着寄存器变量是单个变量,变量长度应该小于等于寄存器长度
2)不能对寄存器变量使用取地址符“&”,因为该变量没有地址
3)尽量在大量频繁操作时使用寄存器变量,且声明的变量个数应该尽量少。
五、专一王子:volatile:每次都是从内存读取,作用在变量上
解释:不管它的值有没有变化,每次对其值进行访问,都会从内存里,寄存器里读取,从而保证数据一致,做到表里如一。
六、typedef:
1、作用:是为一种数据类型定义一个新名字,
2、目的:一给变量一个易记且意义明确的新名字,二简化一些比较复杂的类型声明。
3、typeof与define比较:define是简单的字符串替换,而typedef则是为一个类型起新名字。
4、typedef与结构体的使用
struct tag_node
{
char *p_item;
struct tag_node *p_next;
};
typedef struct tag_node *p_node;
5、typedef与复杂变量声明
int *(*a[5])(int,char*);
typedef int *(*p_fun)(int,char*); //pFun是我们建立的一个类型别名
p_fun a [5]; //使用定义的新类型来声明对象,等价于int* (*a[5])(int,char*);
void (*b[10]) (void(*)());
typedef void (*p_fun_param)();
typedef void (*p_fun)(p_fun_param); //整体声明为一个新变量
p_fun b[10]; //使用定义的新类型来声明对象,等价于void (*b[10]) (void(*)());
double (*)() (*pa)[9];
typedef double (*p_fun)();
typedef pFun (*p_fun_param)[9]; //整体声明一个新类型
P_fun_param pa; //使用定义胡新类型来声明对象,等价于double(*)()(*pa)[9];
*给结构取别名
七、右左法则
1、解释:从最内部的括号(第一次开始阅读声明时,必须从变量名开始)开始阅读声明,向右看,然后向左看。当碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。
int * (*(*fp1) (int) )[10];
从变量名开始——fp1
往右看,什么也没有,碰到了),因此往左看,碰到一个*——一个指针。
跳出括号,碰到了(int)—— 一个带一个int参数的函数
向左看,发现一个*——(函数)返回一个指针
跳出括号,向右看碰到【10】—— 一个10元素的数组
向左看,发现一个*——指针
向左看,发现int——int类型
总结:fp1被声明为一个函数的指针,该函数返回指向指针数组的指针
int *(*(*arr[5]) ()) ();
从变量名开始——arr
往右看,发现是一个数组—— 一个5元素的数组
向左看,发现一个*——指针
跳出括号,向右看,发现()——不带参数函数
向左看,碰到*——(函数)返回一个指针
继续向左,发现int——int类型
总结:arr被声明为一个函数的数组指针,该函数返回指向函数指针的指针