数据
在C语言中,仅有4种基本数据类型 ------- 整数、浮点型、指针和聚合类型(如数组和结构等)。
一、基本数据类型
整型家族包含字符、短整型 ,它们都分为有符号和无符号两种版本。长整型至少和整形一样长,而整型至少应该和短整型一样长。
类型 | 最小范围 |
---|---|
char | 0 到 127 |
sinfned char | -127 到 127 |
unsigned char | 0 至 255 |
short int | -32767 到 32767 |
unsigned short int | 0 到 65535 |
int | -32767 到 32767 |
unsigned int | 0 到 65535 |
long int | -2147483647 到 2147483647 |
unsigned long int | 0 到 4294967295 |
指针
指针是C语言为什么如此流行的一个重要原因。指针可以有效地实现诸如tree 和 list 这类高级数据结构。
变量地值存储在计算机的内存中,每个变量都占据一个特定的位置。每个内存都由地址位移确定并引用,就像一个街道上的房子由它们的门牌号标识一样。指针只是地址的另一个名字罢了。指针变量就是一个其值为另外一个(一些)内存地址的变量。C语言拥有一些操作符,你可以获得一个变量的地址,也可以通过一个指针变量获取它所指向的值或数据结构。
通过地址而不是名字来访问数据的想法常常会引起混淆。事实上你不该被混淆,因为在日常生活中,有很多东西都是这样的。比如用门牌号码来标识一条街上的房子就是如此,没有人会把房子的门牌号码和房子里面的东西搞混。
指针也是完全一样。你可以把计算机的内存想像成一条长街上的一间间房子,每间房子都用一个唯一的号码进行标识。每个位置包含一个值,这和它的地址是独立而显著不同的,即使它们都是数字。
指针常量
指针常量与非指针常量在本质上是不同的,因为编译器负责把变量赋值给计算机内存中的位置,程序员事先无法知道某个特定的变量将存储到内存中的那个位置。因此,你通过操作符获取一个变量的地址而不是直接把它的地址写成字面值常量的形式。列如,如果我们希望变量xyz的地址,我们无法书写一个类是0xff2044这样的字面值,因为我们不知道这是不是编译器实际存放这个变量的内存地址。事实上,当一个函数每次被调用时,它的自动变量(局部变量)可能每次分配的内存地址位置都不同。因此,把指针常量表达式为数值字面值的形式几乎没有用处,所有C语言内部并没有特地定义这个概念
字符串常量
许多人对C语言不存在字符串类型感到奇怪,不过C语言提供了字符串常量。事实上,C语言存在字符串这个概念:它就是一串以NUL字节结尾的零个或多个字符。字符串通常存储在字符数组中,这也是C语言没有显示的字符串类型的原因。由于NUL字节是用来终结字符串的,所以在字符串的内部不能有NUL字节。
二、基本声明
只知道基本的数据类型还远远不够,你还应该知道怎样声明变量。变量的声明的基本形式是:
说明符(一个或多个) 声明表达式列表
对于简单的类型,声明表达式列表就是被声明的标识符的列表。对于更为复杂的类型,声明表达式列表中的每个条目实际上式一个表达式,显示被声明的名字的可能用图。
说明符包含一些关键字,用于描述被声明的标识符的基本类型。说明符也可以用于改变标识符的缺省存储类型和作用域。
初始化
在一个声明中,你可以给一个标量变量指定一个初始化,方法是在变量名后面跟一个等号(赋值号),后面是你想要赋给变量的值。列如:
int j = 15;
这条语句声明 j 为一个整型变量,其初始值为15.
声明数组
为了声明一个一维数组,在数组名的后面要跟一对方括号,方括号里面是一个整数,指定数组元素的个数。
int values[20];
对于这个声明,显而易见的解释是:我们声明了一个整型数组,数组包含了20个整型元素。
C数组另外一个值得关注的地方是,编译器并不检查程序对数组下标的引用是否在数组的合法范围内。这种不加检查的行为有好处也有坏处。好处时不需要浪费时间对有些已知时正确的数组下标进行检查。坏处是这样做将使无效的下标引用无法被监测出来。
声明指针
声明表达式也可用于声明指针。在C语言的声明中,先给出一个基本类型,紧跟其后的式一个标识符列表,这些标识符组成表达式,用于产生基本类型的变量。列如:
int *a
三、typedef
C语言支持一种叫作typedef的机制,它允许你为各种数据类型定义新名字。typedef声明的写法和普通的声明基本相同,只是把typedef这个关键字出现在声明的前面。列如,下面这个声明:
typedef char *ptr_ro_char;
使用typedef声明类型可以减少使声明变成又抽又长的危险,尤其是那些复杂的声明。
四、常量
ANSI C允许你声明常量,常量的样子和变量完全一样,只是它们的值不能修改。你可以使用const关键字来声明变量,如下面例子所示:
int const a;
const int a;
这两条语句都把a声明为一个整数,它的值不能被修改。
当然,由于a的值无法被修改,所以你无法把任何东西赋值给它。如此一来,你怎样才能让它在一开始拥有一个值昵?有两种方法:
int const a = 15;
其次,在函数中声明为const的形参在函数被调用时会得到实参的值。
当涉及到指针变量时,情况就会变得更加有趣,因为有两样东西都有可能成为常量 – 指针变量和它所指向的实体。下面是几个声明的例子:
int *pi;
pi 是一个普通的指向整型的指针。而变量
int const *pci;
则是一个指向整型常量的指针。你可以修改指针的值,但你不能修改它所指向的值。相比之下:
int * const cpi;
则声明pci为一个指向整型的常量指针。此时指针是常量,它的值无法修改,但你可以修改它所指向的整型的值。
int const * const cpci;
最后,在cpci这个例子里,无论是指针本身还是它指向的值都是常量,不允许修改.
#define 指令是另外一种创建名字常量的机制。例如:下面这两个声明都为50这个值创建了名字常量。
#define MAX_ELEMENTS 50
int const max_eleemnts = 50;
在这种情况看下,使用#define比使用cosnt变量更好。因为只要允许使用字面值常量的地方都可以使用前者,比如声明数组的长度。const变量只能用于允许使用变量的地方.
五、作用域
当变量在程序的某个部分被声明时,它只有在程序的一定区域才能被访问。这个区域由标识符的作用域决定。标识符的作用就是程序中可以被使用的区域。列如:函数的局部变量的作用域局限于该函数的函数体。
编译器可以确定4种不同类型的作用域 – 文件作用域、函数作用域、代码块作用域和原型作用域。标识符声明的位置决定它的作用域。
代码块作用域
位于一对花括号中之间的所有语句称为一个代码块。任何在代码块的开始位置声明的标识符都具有代码块作用域,表示它们可以被这个代码块中的所有语句访问。标识为6、7、8、9、10的变量都具有代码代码块作用域。
当代码块处于嵌套状态时,声明于代码块的标识符的作用于到达该代码块的尾部便告终终止了。
六、链接属性
当组成一个程序的各个源文件分别被编译之后,所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起,形成可执行程序。然后,如果相同的标识符出现在几个不同的源文件中时,标识符的链接属性决定如何处理不同文件中出现的标识符。标识符的作用域与它的链接属性有关,但这两个属性并不相同。
链接属性一共3中,external(外部)、internal(内部)、none(无)。没有链接属性的标识符在同一个源文件内的所有声明都指向同一个实体,但位于不同源文件的多个声明则分属于不同的实体。最后,属于external链接属性的标识符无论声明多少次、位于几个源文件都表示同一个实体。
七、存储类型
变量的存储类型是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保存多久。由三个地方可以用于存储变量:不同内存、运行式堆栈、硬件寄存器。