c和指针详解------指针

本篇博客内容参考《c和指针》的内容,并从第六章指针开始记录。

六、指针

6.1 内存和地址

首先我先说明两点:
1.内存中的每个位置由一个独一无二的地址标识
2.内存中的每个位置都包含一个值

如果你记住了一个值的存储地址,你以后可以根据这个地址取得这个值,但是这样太笨拙雷,所以高级语言通过名字而不是地址来访问内存的位置。

所以名字就是我们所称的变量。这是有编译器给我们的方便之门,硬件仍是通过地址访问内存位置

6.2值和类型

int a = 112, b = -1;
float   c = 3.14;
int *d =&a;
float   *e = &c;

在实际存储中c内存所存储的值是一个很大的整数。那么到底它是哪个呢?。这引出了一个重要的结论:不能简单地通过检查一个值的位来判断它的类型,具体的是看使用方式。

考虑下面这个以二进制形式表示的32位值:
01100111011011000110111101100010
下面是这些为可能被解释的许多结果中的几种。

类型
1个32位整数 1735159650
2个16为整数 26476和28514
4个字符 glob
浮点数 1.116533×10e24
机器指令 beg.+110和ble.102

6.3 指针变量的内容

请仔细考虑,a,b,c,d,e的值分别是什么呢?
前三个很简单:a是112,b是-1,c是3.14,指针变量其实也很容易,d的值是100,e的值是108。变量的值就是分配给该变量的内存位置所存储的数值,即使是指针变量也不例外。

6.4 间接访问操作符

通过指针访问它所指向的地址的过程称为间接访问(indirection)或解引用指针(dereferencing)。

6.5 未初始化和非法的指针

下面这段代码说明了一个极为常见的错误

int *a;
*a = 12;

这个声明创建了一个名叫a的指针变量,后面那条赋值语句把12存储在a所指向的内存位置。
警告:
但是究竟a指向哪儿呢?我们并不知道。如果你运气好,编译器会提示你a的初始值是个非法地址,这样赋值语句将会出错,从而程序终止。在unix系统上,这个错误被称为“段违例(segmentation violation)”和“内存错误(memory fault)”。
一个更为严重的情况是:这个指针偶尔可能包含了一个合法的地址。那么接下来位于那个位置的值被修改,虽然你无意修改。像这样的类型的错误非常难以捕捉。所以在你对指针进行间接访问之前,必须非常小心,确保他们已被初始化。

6.6 MULL指针

标准定义雷NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。要是一个指针变量为NULL,你可以给它赋一个零值。
NULL指针的概念是非常有用,因为他给了你一种方法,表示某个特定的指针目前并未指向任何东西。
在对指针进行解引用操作之前,你首先必须确保它并非NULL指针。
警告:
如果对一个NULL指针进行间接访问会发生什么情况呢?一般情况下都会引发错误,并终止程序。
提示:
对所有的指针变量进行显示的初始化是种好做法。

6.7 指针、间接访问和左值

指针变量可以作为左值,并不是因为它们是个指针,而是因为它们是变量。下面两条语句中

*d = 10 - *d;
d = 10 - *d;   ????

第1条语句包含了两个间接访问操作,右边的值是d所指向的位置所存储的值,左边是d所指向的位置把赋值符右侧的表达式计算的结果作为它的新值。
第2条语句是非法的,因为它表示把一个整型数量(10 - *d)存储于一个指针变量中。

6.8 指针、间接访问和变量

来看看下面的表达式

*&a = 25 ;

如果你的答案是把值25赋值给变量a,恭喜!答对了。&操作符产生变量a的地址,他是一个指针常量。接着,*操作符访问其操作数所表示的地址。不过这样做太复杂,是程序效率变的底下,所以没人会这么做。

6.9 指针常量

假定a的存储于位置100

*100 = 25;

这条语句是非法的,因为字面值100的类型是整型,而间接访问操作只能作用于指针类型表达式,如果你确实想把25存储于位置100,你必须使用强制类型转换。

*(int *)100 = 25 ;

6.10 指针的指针

int a = 12 ;
int *b = &a ;
int **c = &b;

唯一一个新面孔是 c = &b ; 这是合法的吗?是的,指针变量和其他变量一样,占据内存中某个特定的位置,所以用&操作符取地址是合法的(声明为register的变量例外)。

6.11 指针表达式

现在让我们观察各种不同的指针表达式。

char ch = 'a';
char *cp = &ch;
表达式 右值 左值
&ch 变量ch地址 非法
cp cp的值 cp所处的内容
&cp 指向字符的指针的指针 非法
*cp ch的值 ch的地址
*cp+1 ch的值+1 非法
*(cp+1) cp+1所指向的值 cp+1所指向的地址
++cp 值增加 非法
cp++ 先返回cp值的拷贝然后增加 非法
*++cp ch后的值 ch后的地址
*cp++ 先得到ch 然后cp增加 先得到cp的值,然后cp增加
++*cp ch的值增加 非法
(*cp)++ ch的值增加 非法

6.13 指针运算

指针加上一个整数的结果是另外一个指针,如果你将一个字符指针加1,运算结果会产生的指针指向内存中的下一个字符。float占据的内存空间不止1字节,如果将一个指向float的指针加1,将会发生什么呢?
答案是指针值加上1个float大小的值。换句话说,如果p是一个指向char的指针,那么表达式p+1就是指向下一个char。如果p是一个指向float的指针,那么p+1就指向下一个float。

6.13.1 算数运算

c的指针算数运算只限于两种形式。
第一种是:指针 +- 整数
让指针指向数组最后一个元素后面的哪个位置是合法的,但对这个指针执行间接访问可能会失败。
第二种是:指针 - 指针
只有当两个指针指向同一个数组中的元素时,才允许从一个指针减去另一个指针。
两个指针相减的结果的类型是 ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位),因为减法运算的结果将除以数组元素类型的长度。

6.13.2 关系运算

对指针执行关系运算也是有限制的。用下列关系操作符对两个指针值进行比较是可能的:<、<=、>、>=
不过前提是它们都指向用一个数组中的元素。

6.15 警告的总结

1.错误地对一个未初始化的指针变量进行解引用;
2.错误的对一个NULL指针进行解引用;
3.向函数错误的传递NULL指针;
4.未检测到指针表达是的错误,从而导致不可预料的错误;
5.对一个指针进行减法运算,使它非法的指向了数组第一个元素的前面的内存位置;

6.16 编程提示的总结

1.一个值应该只具有一种意思
2.如果指针并不指向任何有意的东西,就把它设置为NULL。

你可能感兴趣的:(c和指针详解------指针)