计算机的内存由数以亿万的位(bit)组成,每个位可以容纳值0和1。
但是,由于一个位能表示的值的范围太有限了,所以单独的位的用处不大!
通常将许多位合成一组作为一个单位,这样就可以存储范围较大的值。
其中每一个单位称为一个字节(byte),每个字节都包含了存储一个字节所需要的位数。
通常,在大部分机器上,每个字节包含8个位,可以存储无符号值0到255,或有符号值-128到127。
每个字节通过地址来标识。
为了存储更大的值,我们把两个或更多的字节合在一起作为一个更大的内存单位。
这个单位在许多机器上被称为字。
通常情况下,一个字由2个或4个字节组成。
如果一个字包含4个字节,那么每个字可以容纳的无符号整数范围是从0到4294967295,可以容纳的有符号范围是从-2147483648到2147483647。
注意,尽管一个字包含了4个字节,它仍然只有一个地址。
由上可知:
内存中的每个位置都由一个独一无二的地址标识。内存中的每个位置都包含一个值。如图所示,5个数都位于自己所属的字中。
显而易见,我们可以通过其地址取得这个值。
但是这种方法实在是太麻瓜了。
所以C提供了一种特性——通过名字而不是地址来访问内存的位置。当然,这些名字就是我们所称的变量。
现在我们来看看存储于这些位置的值。
int a = 112; int b = -1; float c = 3.14; int *d = &a; int *e = &c;看看变量d和e的声明。
它们都被声明为指针,并用其他变量的地址予以初始化。
指针的初始化时用&操作符来完成的,它用于产生操作数的内存地址。
区分指针变量的地址和它的内容是非常重要的。
通过一个指针访问它所指向的地址的过程称为间接访问或解引用指针。
这个用于执行间接访问的操作符是单目操作符*。
表达式值类型a112intb-1intc3.14floatd100int *e108int **d112int*e3.14float我们本意是:声明创建一个名叫a的指针变量,通过赋值语句把12存储在a所指向的内存位置。
但是问题在于:我们声明了这个变量,但从未对它进行初始化,所以我们没有办法预测12这个值将存储于什么地方。
所以,在我们队指针进行间接访问之前,必须小心,确保它们已经被初始化!
标准定义了NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。
要使一个指针变量未NULL,我们可以给它赋一个零值。
对指针进行解引用操作可以获得它所指向的值。由于NULL指针并未指向任何东西,所以对NULL指针进行解引用操作是非法的。
在对指针进行解引用之前,必须确保它并非NULL指针。
指针支持的算术运算只限于两种形式:
指针 ± 整数
这类表达式的结果类型也是指针。但是存在一个问题,即如果我们将指针加1,那么运算结果是指针指向的内存中的下一位、下一个字节、下一个字中的哪个呢?答案都是否定的。当一个指针和一个整数量执行算术运算时,整数在执行加法运算前始终会根据合适的大小进行调整。这个合适的大小就是指针所指向类型的大小,调整就是把整数值和合适的大小相乘。
标准定义这种形式只能用于指向数组中某个元素的指针,这是由于数组中的元素存储于连续的内存位置中,后面元素的地址大于前面元素的地址。因此:
对一个指针加1使它指向数组中下一个元素。
对一个指针减3使它向左移3个元素。 #include <stdio.h> #define N_VALUES 5 int main() { float value[N_VALUES] = {3.14,3.15,3.16,3.17,3.18}; for (int i = 0; i < N_VALUES; ++i) { printf("%f" , value[i]); } float *vp; for (vp = value ; vp < &value[N_VALUES] ; ++vp) *vp = 0; for (int i = 0; i < N_VALUES; ++i) { printf("%f" , value[i]); } }指针 - 指针
只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。两个指针相减的结果的类型是ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位),因为减法运算的结果将除以数组元素类型的长度。用下列关系操作符对两个指针值进行比较是可能的:
< <= > >=
前提是它们都指向同一个数组中的元素。
根据你所使用的操作符,比较表达式将告诉你哪个指针指向数组中更前或更后的元素。