C程序一直由下列部分组成:
1)正文段——CPU执行的机器指令部分;一个程序只有一个副本;只读,防止程序由于意外事故而修改自身指令;
2)初始化数据段(数据段)——在程序中所有赋了初值的全局变量,存放在这里。
3)非初始化数据段(bss段)——在程序中没有初始化的全局变量;内核将此段初始化为0。
4)栈——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。
5)堆——动态存储分。
-------------------------------------我是分割线--------------------------------
问:为什么要有关键字?
答:关键字是程序设计中代码必须包含的部分,编译器在编译C代码的时候,必然要将C代码进行断句,将代码分割成不同部分,将这些部分分别进行解析和编译。int a = 10; int是关键字,编译器看到它出现,会将它后面的字符作为整型变量名来处理。
也就是说,关键字是编译器能认识的特殊字符串符号。
关键字的数量是由编译器来决定的,关键字大小写敏感性也和编译器有关。如果关键字写错,那么在代码的解析过程中,编译器就会报错:符号不能识别或符号不能被解析。
每个关键字有着不同的意义,用来告知编译器编程者的目的。
32个关键字每个都有不同的意义,大体上根据其意义可以分为以下几类(下划线表示不同分类中有交集):
1)非常见:auto、register、volatile、goto
2)存储相关:const、extern、register、volatile、static、auto、signed、unsigned
3)数据类型:char、short、int、float、long、double、struct、union、enum、void
4)逻辑控制:if、else、for、while、do、break、continue、return、default、switch、case、goto
5)特殊用途:sizeof、typedef
---------------------------------------------我是分割线-----------------------------------------------------
auto:
在缺省的情况下,编译器默认所有变量都是auto的。而它与C语言中提供的四种存储说明符(auto,register,extern,static)中的register对应存储类别说明符的两种存储期(自动存储期和静态存储期)的自动存储期。
自动存储期---指生存时间仅仅在代码活动区间(函数调用期间)有效。
extern,static就是用来说明静态存储期的变量和函数。
指变量不在栈上,放在静态存储区。
Static:
当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。当加上static关键字修饰后,该变量或函数就会被隐藏。
此关键字可分别修饰变量和函数。
变量:
static修饰的变量和全局变量都是存储在内存的静态区。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。而static修饰的变量可分为局部变量和全局变量。这里要注意其作用域。若是静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使使用extern也不使用。准确地说,作用域是从定义之处开始到文件结尾处结束。静态局部变量。如果其在某个函数体内定义,其作用域只限于本函数,本文件中其他函数也不能使用。而且下次调用时候其值为上次改动后的值,当然没有改动的话就是原来的值。因为区存在于内存的静态区。
函数:
在函数前加上static关键字的话,此函数就成为静态函数。注意,这里的静态不同于变量,这里静态的作用只是对其他的文件此函数不可见,并不是指此函数存储在静态内存区中。所以一般又称为内部函数。这个内部函数的好处就是在不同的人编写不同的函数时,不需要担心自己定义的函数是否与其他文件中的函数同名。
存放在静态内存区的变量其初始值都为0. 在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。
------------------------------我是分割线---------------------------------
C语言函数的形参为什么不可以说明为static型变量
形参:形式上的参数,用完就扔,是系统运行中的一个临时变量,因此不能定义成static型变量
如果形参是static类型,则失去了函数的重用性。
-------------------------我是分割线--------------------------------
register:
这个关键字请求编译器尽可能地将变量存在CPU内部的寄存器中,故是否存储在寄存器中取决于编译器。
Register变量必须是能被CPU寄存器所接受的类型。这就意味着register变量必须是一个单个的值,
并且其长度应小于或等于整型的长度,而且register变量可能不存放在内存中,
所以是不能用取址运算符“&”来获取register变量的地址。
6.外部变量extern定义函数中的全局变量时,其缺省格式是外部变量类型。外部变量应该在一个头文件中声明,在当前源文件中定义。
外部变量允许其他文件引用。
const:修饰符:修饰变量,函数。修饰变量时候,不能被重复赋值,只能放在只读段中。修饰函数时候,
表明函数的返回值必须为常数。
首先需要注意的是,const修饰的是在它前面的类型,如果它前面没有类型,那它修饰的是紧跟着它的那个类型。
const int nValue; //nValue是constconst char *pContent; //*pContent是const, pContent可变const (char *) pContent; //pContent是const,*pContent可变char* const pContent; //pContent是const,*pContent可变const char* const pContent; //pContent和*pContent都是constint const nValue; //nValue是constchar const * pContent; /*pContent是const, pContent可变(char *) const pContent; //pContent是const,*pContent可变char* const pContent; //pContent是const,*pContent可变
char const* const pContent; //pContent和*pContent都是const
1.定义常量
(1)const
修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的,readonly。
2.指针使用CONST
(1)指针本身是常量不可变
char * const pContent;
const (char*) pContent;
(2)指针所指向的内容是常量不可变
const char *pContent;
char const *pContent;
(3)两者都不可变
const char* const pContent;
3.函数中使用CONST
(1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)
void function(const int Var);
b.参数指针所指内容为常量不可变
void function(const char* Var);
c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)
void function(char* const Var);
d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:
void function(const Class& Var); //引用参数在函数内不可以改变
void function(const TYPE& Var); //引用参数在函数内为常量不可变
这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用
的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,
然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,
因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,
只有引用的const传递能够捕捉到这个家伙.
(2)const 修饰函数返回值
const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
a.
const int fun1() //这个其实无意义,因为参数返回本身就是赋值。
b.
const int * fun2() //调用时
const int *pValue = fun2(); //我们可以把fun2()看作成一个变量,即指针内容不可变。
c.
int* const fun3() //调用时
int * const pValue = fun2(); //我们可以把fun2()看作成一个变量,即指针本身不可变。
volatile:(嵌入式程序员必须掌握的)volatile最初的意思是表示汽油容易挥发,在c中的作用大概有两点
(1)表示变量是易失的,易变的。
(2)强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存。