第一部分 数据
一、基本数据类型
-
长整型至少应该和整型一样长,而整型至少应该和短整型一样长。
-
缺省的char 要么是signed char, 要么是unsigned char, 这取决于编译器。不同机器上的char 可能拥有不同范围的值。显式地把这类变量声明为signed 或unsigned, 可以提高程序的可移植性。当可移植问题比较重要时,最佳方案就是把存储于char 型变量的值限制在signed char 和unsigned char 的交集内(0-127)
-
枚举类型从0开始,如果某个符号名未显式指定一个值,那么它的值就比前面一个符号名的值大1 。
指针常量与常量指针
一、概念
1、指针常量:指针常量就是指针本身是常量,换句话说,就是指针的值(内存地址)是常量,不能改变。但是,内存地址所对应的内容是可以通过指针改变的。
2、常量指针:指向常量的指针,换句话说,就是指针指向的是常量,它指向的内容不能发生改变(注意:不能通过指针来修改它指向的内容,而可以通过原来的声明修改)。但是,指针自身不是常量,它自身的值可以改变,从而指向另一个常量。也称为指向常量的指针。
二、声明
1、指针常量:数据类型 * const 指针变量 eg:int * const p;
2、常量指针:数据类型 const * 指针变量 或者 const 数据类型 *指针变量
eg:int const * p; 或者 const int *p;
3、常量指针常量:数据类型 const * const 指针变量 或者 const 数据类型 * const 指针变量eg:int const * const p; 或者 const int * const p;
指针常量基本不会用,更多的还是常量指针
二、基本声明
-
signed 关键字一般只用千char 类型,因为其他整型类型在缺省情况下都是有符号数
-
**编译器并不检查程序对数组下标的引用是否在数组的合法范围之内!**好处是不需要浪费时间对有些已知是正确的数组下标进行检查。坏处是这样做将使无效的下标引用无法被检测出来。如果下标值是从那些已知是正确的值计算得来,那么就无需检查它的值。如果一个用作下标的值是根据某种方法从用户输入的数据产生而来的,那么在使用它之前必须进行检测,确保它们位于有效的范围之内。
-
函数如果不显式地声明返回值的类型,它就默认返回整型
三、typedef
使用typedef 而不是#define 来创建新的类型名,因为后者元法正确地处理指针类型。
#define ptr_to_char char *
ptr_to_char a, b;
正确地声明了a, 但是b 却被声明为一个字符。在定义更为复杂的类型名字时,如函数指针或指向数组的指针,使用typedef 更为合适。
四、常量
- 用名字常量定义数组的长度或限制循环的计数器能够提高程序的可维护性。eg: #define MAX_LENGTH 100
五、链接属性
当组成一个程序的各个源文件分别被编译之后,所有的目标文件以及那些从一个或多个函数库中
引用的函数链接在一起,形成可执行程序。标识符的链接属性(linkage)决定如何处理在不同文件中出现的标识符。标识符的作用域与它的链接属性有关,但这两个属性并不相同。
链接属性一共有3 种——external (外部)、internal (内部)和none (无)。
- 没有链接属性的标识符(none)总是被当作单独的个体,该标识符的多个声明被当作独立不同的实体。
- 属于internal 链接属性的标识符在同一个源文件内的所有声明中都指同一个实体,但位于不同源文件的多个声明则分属不同的实体。
- external 链接属性的标识符不论声明多少次、位千儿个源文件都表示同一个实体。
六、存储类型
变量的存储类型(storage class)是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保持多久。有三个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。
- 凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。
- 在代码块内部声明的变量的缺省存储类型是自动的(automatic) ,也就是说它存储千堆栈中, 这里的堆栈就是指栈。
- 在代码块内部声明的变量,如果加上关键字static, 可以使它的存储类型从自动变为静态
- 关键字register 可以用于自动变景的声明,提示它们应该存储于机器的硬件寄存器而不是内存中,这类变量称为寄存器变量。通常,寄存器变量比存储于内存的变量访问起来效率更高。但编译器并不一定要理睬register 关键字,如果有太多的变量被声明为register, 它只选取前几个实际存储于寄存器中,其余的就按普通自动变量处理。如果一个编译器自已具有一套寄存器优化方法,它也可能忽略register 关键字。
- 自动变量的初始化较之赋值语旬效率并无提高,即 int i = 0; 与 int i; i = 0; 效率是一样的。
七、static 关键字
- static用于函数定义时,或用于代码块之外的变量声明时,函数或变量只能在声明它们的源文件中访问。
八、内存模型
C语言的内存模型通常涉及以下几个方面:
- 栈(Stack):栈用于存储局部变量、函数参数和函数调用的上下文信息。栈是一种后进先出(LIFO)的数据结构,由编译器自动管理。在函数调用时,局部变量和函数参数会在栈上分配内存,随着函数的返回而释放。
- 堆(Heap):堆用于动态分配内存,通过函数如
malloc()
、calloc()
或realloc()
来进行手动内存分配和释放。堆上分配的内存不会在函数返回时自动释放,需要显式调用free()
来释放。 - 全局数据区(Global Data Area):全局数据区用于存储全局变量和静态变量(已初始化的)。全局数据区在程序启动时被分配,在程序结束时释放。全局变量和静态变量在全局作用域中可见,可以在程序的任何地方访问和使用。
- 静态数据区(Static Data Area):静态数据区用于存储静态变量和未初始化的全局变量。静态数据区在程序启动时分配,在程序结束时释放。未初始化的静态变量会被初始化为零或空值。
- 文字常量区(Text/Code Area):文字常量区存储字符串常量和程序的可执行代码。这个区域通常是只读的,存储着程序的指令和常量字符串,不能被修改。
- 寄存器(Register):寄存器是位于 CPU 内部的存储区域,用于存储高速访问的变量和临时数据。由于寄存器数量有限,编译器决定哪些变量应该存储在寄存器中,以提高访问速度。
九、作用域、链接属性与存储类型
int a = 0;
extern int b;
static int c;
int fun(int e) {
int f = 15;
register int b;
static int g = 20;
extern int a;
{
int e;
int a;
extern int h;
}
...
{
int x;
int e;
}
}
static int fun2() {
...
}
int main() {}
在上述代码中,各个变量的存储位置、链接属性和作用域如下:
int a = 0;
- 全局变量- 存储位置:静态数据区或全局数据区
- 链接属性:外部链接(External Linkage)
- 作用域:整个程序
extern int b;
- 外部变量- 存储位置:由其他文件中的定义所决定,但必须是定义在代码块外面的全局变量,相当于是存储在静态数据区或全局数据区了
- 链接属性:外部链接(External Linkage)
- 作用域:整个程序
static int c;
- 全局静态变量- 存储位置:静态数据区或全局数据区
- 链接属性:内部链接(Internal Linkage)
- 作用域:整个文件
int e
- 形式参数(函数参数)- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:函数
fun
的参数作用域
int f = 15;
- 局部变量- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:函数
fun
的块作用域(从定义开始到块结束)
register int b;
- 寄存器变量- 存储位置:可能存储在寄存器中
- 链接属性:无
- 作用域:函数
fun
的块作用域(从定义开始到块结束)
static int g = 20;
- 局部静态变量- 存储位置:静态数据区或全局数据区
- 链接属性:内部链接(Internal Linkage)
- 作用域:函数
fun
的块作用域(从定义开始到块结束)
extern int a;
- 外部变量- 存储位置:由其他文件中的定义所决定
- 链接属性:外部链接(External Linkage)
- 作用域:整个程序
int e
- 块作用域中的局部变量- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:块作用域内的定义(从定义开始到块结束)
int a
- 块作用域中的局部变量- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:块作用域内的定义(从定义开始到块结束)
extern int h;
- 外部变量- 存储位置:由其他文件中的定义所决定
- 链接属性:外部链接(External Linkage)
- 作用域:整个程序
int x;
- 块作用域中的局部变量- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:块作用域内的定义(从定义开始到块结束)
int e;
- 块作用域中的局部变量- 存储位置:函数调用时在栈上分配
- 链接属性:无
- 作用域:块作用域内的定义(从定义开始到块结束)
static int fun2()
- 静态函数- 存储位置:代码段或静态代码区
- 链接属性:内部链接(Internal Linkage)
- 作用域:整个文件
int main()
- 主函数- 存储位置:代码段或静态代码区
- 链接属性:内部链接(Internal Linkage)
- 作用域:整个文件
存储位置和链接属性的确切细节可能会受到编译器、操作系统和编译选项的影响,上述是一般情况下的存储位置和链接属性。
- 只要变量并非声明于代码块或函数定义内部,它在缺省情况下的链接属性即为external 。如果一个变量声明千代码块内部,在它前面添加extern 关键字将使它所引用的是全局变量而非局部变量。
- 全局变量在程序开始执行前创建,并在程序整个执行过程中始终存在。
十、编程规范
- 为了保持最佳的可移植性,把字符的值限制在有符号和无符号字符范围的交集之内,或者不
要在字符上执行算术运算。 - 用它们在使用时最自然的形式来表示字面值。
int i = 10;
int addr = 0xa8;
- 不要把整型值和枚举值混在一起使用。
- 不要依赖隐式声明。
- 在定义类型的新名字时,使用typedef 而不是#define 。
- 用const 声明其值不会修改的变量。
- 使用名字常量而不是字面值常量。
#define MAX 100
int a[MAX];
- 不要在嵌套的代码块之间使用相同的变量名。
- 在每个switch 语句中都放上一条default 子句
- 在一个没有循环体的循环中,用一个分号表示空语句,并让它独占一行。