一、基本数据类型
四种基本数据类型:整型,浮点型,指针,聚合类型(数组、结构等)。其他所有类型由这四种派生而来。
1、整型
包括:字符、短整型、长整型、整型,都分为signed和unsigned版本。
1.1、int型
长整型 >= 整型,整型 >= 短整型。short int至少16位,long int至少32位,缺省的int长度取决于编译器。
16位,32位,64位操作系统中各类型变量的长度:
16位 | 32位 | 64位 | |
---|---|---|---|
char | 8位,1个字节 | 8位,1个字节 | 8位,一个字节 |
short int | 16位,2个字节 | 16位,2个字节 | 16位,2个字节 |
int | 16位,2个字节 | 32位,4个字节 | 32位,4个字节 |
long int | 32位,4个字节 | 32位,4个字节 | 64位,8个字节 |
long long | 无 | 64位,8个字节 | 64位,8个字节 |
指针 | 16位,2个字节 | 32位,4个字节 | 64位,8个字节 |
由此发现不同的操作系统中,各数据类型的长度不一样,这样不方便代码的移植和维护。因此,使用typedef关键字,在各个机器中依据数据的实际长度,来给数据类型起别名,再使用别名来定义变量,这样方便两台不同机器对代码的统一管理。
例如:
在16位机器中定义:
typedef signed char int8;
typedef int int16;
typedef long int int32;
在32位机器中定义:
typedef signed char int8;
typedef short int int16;
typedef int int32;
这样,使用新定义的类型名来定义变量,对变量的长度进行统一的管理,就可以在不修改代码的情况下,在这两台机器间进行代码的移植,而不会出现变量冲突的问题。
1.2、char型
signed一般只用于char,字符本质是小整型值,缺省的char要么是signed,要么是unsigned char,这取决于编译器。
类型 | 值的最小范围 |
---|---|
char | 0~127 |
signed char | -127~127 |
unsigned char | 0~255 |
short int | -32767~32767 |
int | -32767~32767 |
long int | -2^31 + 1~ 2^31 - 1 |
unsigned int | 0~65535 |
unsigned short int | 0~65535 |
unsigned long int | 0~ 2^32 - 1 |
- 为了提高程序的可移植性,可将存储在char型变量中的值限制在signed char和unsigned char的交集,即0~127中;
- 当char被具体声明为signed和unsigned时,再对其进行算术运算;
- 注意库函数的参数列表是signed还是unsigned,防止char不匹配带来兼容性问题。
2、字面值(Literal)
表示常量,尾部可添加一个后缀来改变缺省规则,包括:L,UL。
一般string literal表示字符串常量,char literal表示字符常量
3、枚举类型
enum JAR_TYPE
{
CUP, PINT, QUART, HALT_CALLON, CALLON
};
/*表示声明一个枚举类型为Jar_Type;其中的符号名的值默认从0开始,之后依次加一,
可以指定缺省值,未指定缺省值的,其值为前面一个符号名的值加一。*/
enum JAR_TYPE MILK_JUG, GAS_CAN, MEDICINE_BOTTLE; //表示定义枚举类型的变量。
enum JAR_TYPE
{
CUP, PINT, QUART, HALT_CALLON, CALLON
}MILK_JUG, GAS_CAN, MEDICINE_BOTTLE; //枚举类型的声明与定义。
enum
{
CUP, PINT, QUART, HALT_CALLON, CALLON
}MILK_JUG, GAS_CAN, MEDICINE_BOTTLE; //省略枚举名称,直接定义枚举变量
/*枚举类型引用的两种方法*/
enum JAR_TYPE MILK_JUG = CUP, GAS_CAN = QUART; //法一
enum JAR_TYPE
{
CUP, PINT, QUART, HALT_CALLON, CALLON
}MILK_JUG = CUP, GAS_CAN = QUART; //法二
枚举类型实际上就是一个整数,可以进行整数的算术运算,但这么用的话,枚举类型就失去了原来的意义。
4、指针
指针指向一个地址,对指针的间接访问(indirection)可以得到指针所指向的值。
例子:
char *a = "Hello";
- 语句的执行顺序为:
1、分配内存给指针变量a;
2、分配内存给字符串常量Hello;
3、将字符串常量的首地址赋值给指针变量a。 - "Hello"为字符串常量,存储在常量区。出现在表达式中时,其代表的值为"Hello"字符串的首地址,并赋值给指针a。
char str[] = "Hello";
- 新建一个数组空间来存储"Hello",可以整体赋值,也可以单个字符依次赋值:
char str[] = {'H','e','l','l','o'};
char str[10]; str = "Hello"; // 错误语句
- 这条语句是不正确的,"Hello"在赋值语句中表示一个地址的值,而str表示数组的地址值,是一个常量,常量的值不能被改变,所以错误。但是,初始化时对数组赋值字符串是可行的,如第二条语句。
5、常量
一开始改变常量值的两种方法:1、在声明中进行初始化;2、函数声明为const的形参得到实参时
6、浮点数
浮点数的通常以一个小数和一个以某个假定数为基数的指数构成,浮点数的字面值在缺省情况下都是double类型,若加后缀L则为long double类型,若加后缀F则为float类型。
ANSI标准规定:long double至少和double一样长,double至少和float一样长,且所有的浮点数至少可以容纳10^-37 ~ 10^37之间的任何值。
二、作用域(scope)、链接属性(linkage)、存储类型(storage class)
1、作用域:
- 代码块作用域(block scope):一块花括号之间的语句。
- 文件作用域(file scope):在代码块之外的声明的标识符都具有文件作用域。
- 原型作用域(prototype scope):只适用于函数的形参,函数原型中的形参名非必须,不需要与定义中的形参名一致,只要类型一致就行,传入的实参名不需要与形参名一致。
- 函数作用域(function scope):只适用于goto语句的语句标签,只有一条规则:函数的所有语句标签唯一。
2、链接属性:与作用域有关,但两者不同
决定如何处理各个文件中的标识符。
-
external(外部):表示该标识符能在任何用extern声明该变量的源文件中使用,且表示的是同一个实体。
文件作用域中的标识符默认为外部链接属性,在任何地方用extern声明的变量都具有外部链接属性(属于第二次声明的除外),用static关键字可以修改其为内部链接属性。- 注意:
-
如果在第二次声明中使用extern关键字,则不会改变标识符的链接属性为外部,只有第一次声明时才有用。
举例: 若第一次用static声明为内部,则第二次用extern声明时仍为内部链接属性。
-
函数内部的extern声明,表示此处的全局变量是从另一个区域链接过来的,表示的是另一个区域的实体;
若没有extern关键字,则表示该变量为函数的局部变量。
-
- 注意:
-
internal(内部):某一个标识符在同一个源文件的不同声明指向同一个实体,在不同源文件中的多种声明指向不同的实体。
在全局空间中用static定义变量,或者函数时,表示其为内部链接属性;在函数体内用static定义的为none链接属性(因为不能改变其先前的none属性)
-
none(无):函数体内部的标识符默认具有none属性
用extern声明的局部变量为外部链接属性,不表示局部变量,表示的另一个位置的全局变量;用static声明的局部变量不具有链接属性。
3、存储类型(storage class)
- 静态(static)类型:全局变量或static局部变量,静态内存中
- 自动(auto)类型:局部变量,存储在堆栈中
- 寄存器(register)类型:存储于机器硬件寄存器而不是内存中,其访问效率高,将经常访问的变量声明为寄存器类型,以提高访问速度,但太多变量声明为register时,只能选取几个,剩余的为自动变量。因为寄存器数量有限。
- 外部(external)类型:只能是全局变量,不能是局部静态变量。
变量在内存中存储的位置包括:
- 堆区(程序员动态分配);
- 栈区(编译器自动分配);
- 全区去/静态区(程序结束后由操作系统释放);
- 常量区(程序员结束后由操作系统释放)。
三、静态变量与动态变量的初始化
等号的意义:
1、在表达式里,是赋值的意思。
2、在变量声明中。 不可以看作是赋值, 只是初始化,作用是告诉编译器如何初始化这个内存空间。
1、静态变量的初始化
可执行文件想要初始化的值,会放当变量使用的位置。当可执行文件载入到内存时,就已经进行了初始化,如果不显式地赋初值,初始化为0。
2、自动变量的初始化
需要更多的开销,因为当程序链接时还无法判断自动变量的存储位置。事实上,函数的局部变量在函数的每次调用中可能占据不同的位置。因此,自动变量没有缺省的初始值,而显式的初始化将在代码块的起始处插入一条隐式的赋值语句。
- 自动变量初始化的性质:
- 自动变量初始化较之赋值语句效率并无提高。除了声明为const的变量之外,在声明变量的同时进行初始化和先声明后赋值只有风格只差,并无效率之别。
- 自动变量所在块每次执行时,都要对自动变量重新赋值。这与静态变量大不相同,后者只是在程序开始执行前初始化一次。
- 由于初始化在运行时执行,因此可以使用任何表达式作为初始化值。
- 除非对自动变量显式初始化,否则当自动变量创建时,它的值为任意值,一般为上一次存储在这个位置的变量的值。