杂论C语言指针笔记
指针是 C和 C++语言编程中最重要的概念之一,也是最容易产生困惑并导致程序出错的问题之一。利用指针编程可以表示各种数据结构 , 通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯;并能像汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C和 C++语言的功能。
接下来将谈一下几个内容:
a.判断指针的类型
b.指针的运算
c.数组和指针的同异
d.指向函数的指针
e.指针与抽象
阅读C语言声明--判断指针的类型
优先级规则
声明从名字开始
优先级依次是
括号
后缀操作符
圆括号()表示函数
方括号[]表示数组
前缀操作符:星号*表示”指向...的指针“
如果const/volatile后面紧跟类型说明符(如int),则作用于其上,否则作用于左侧紧邻指针星号。
例:char * const *(*next)();
next是指向返回指向指向char类型的常量指针的指针的函数的指针
1、从语法的角度看,指针的类型是指把指针声明语句中的指针名字去掉所剩下的部分。这是指针本身所具有的类型。
2、当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么类型来看待。从语法的角度看,指针所指向的类型是指针声明语句中的指针名字和名字左边的指针声明符 *去掉所剩下的部分。
3、 指针的值或者叫指针所指向的内存区或地址,是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在 32位程序里,所有类型的指针的值都是一个 32位整数,因为 32位程序里内存地址全都是 32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为 sizeof(指针所指向的类型 )的一片内存区。以后,我们说一个指针的值是 XX,就相当于说该指针指向了以 XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在上例中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
4、 指针本身所占有的内存区是指针本身占内存的大小,这个你只要用函数 sizeof(指针的 类型 )测一下就知道了。在 32位平台里,指针本身占据了 4个字节的长度。
指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
指针的算术运算
参看:http://www.cnblogs.com/madengwei/archive/2008/02/18/1072422.html
赋值运算
int a;
int *ip=&a;
int a;
int *ip;
ip=&a; // 把整型变量 a 的地址赋予整型指针变量 ip
int a;
int *pa=&a;
int *pb;
pb=pa; // 把 a 的地址赋予指针变量 pb
由于 pa,pb均为指向整型变量的指针变量,因此可以相互赋值。
int a[5],*pa;
pa=a; // 数组名表示数组的首地址,故可赋予指向数组的指针变量 pa
也可写为:
pa=&a[0]; // 数组第一个元素的地址也是整个数组的首地址也可赋予 pa
当然也可采取初始化赋值的方法:
int a[5],*pa=a;
以上是一些基本的数组赋值方法,后面我们会详细讨论指针在数组中的使用。
char *pc;
pc="c language";
或用初始化赋值的方法写为:
char *pc=" c language ";
这里应说明的是并不是把整个字符串装入指针变量, 而是把存放该字符串的字符数组的首地址装入指针变量。
int (*pf)();
pf=f; //f 为函数名
对于指向数组的指针变量,可以加上或减去一个整数 n。设 ip是指向数组 a的指针变量,则 ip+n,ip-n,ip++,++ip,ip--,--ip 运算都是合法的。指针变量加或减一个整数 n的意义是把指针指向的当前位置 (指向某数组元素 )向前或向后移动 n个位置。应该注意,数组指针变量向前或向后移动一个位置和地址加 1或减 1 在概念上是不同的。因为数组可以有不同的类型, 各种类型的数组元素所占的字节长度是不同的。如指针变量加 1,即向后移动 1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加 1。看如下例子:
char a[20];
int*ip=a;
...
ip++;
在上例中,指针 ip的类型是 int*,它指向的类型是 int,它被初始化为指向整形变量 a。接下来的第 3句中,指针 ip被加了 1,编译器是这样处理的:它把指针 ip的值加上了 sizeof(int),在 32位程序中,是被加上了 4。由于地址是用字节做单位的,故 ip所指向的地址由原来的变量 a的地址向高地址方向增加了 4个字节。
由于 char类型的长度是一个字节,所以,原来 ptr是指向数组 a的第 0号单元开始的四个字节,此时指向了数组 a中从第 4号单元开始的四个字节。再看如下例子:
char a[20];
int*ip=a;
...
ip+=5;
在这个例子中, ip被加上了 5,编译器是这样处理的:将指针 ip的值加上 5乘 sizeof(int),在 32位程序中就是加上了 5乘 4=20。由于地址的单位是字节,故现在的 ip所指向的地址比起加 5后的 ip所指向的地址来说,向高地址方向移动了 20个字节。在这个例子中,没加 5前的 ip指向数组 a的第 0号单元开始的四个字节,加 5后, ptr已经指向了数组 a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。
如果上例中, ip是被减去 5,那么处理过程大同小异,只不过 ip的值是被减去 5乘 sizeof(int),新的 ip指向的地址将比原来的 ip所指向的地址向低地址方向移动了 20个字节。
总结一下,一个指针 ipold加上一个整数 n后,结果是一个新的指针 ipnew, ipnew的类型和 ipold的类型相同, ipnew所指向的类型和 ipold所指向的类型也相同。 ipnew的值将比 ipold的值增加了 n乘 sizeof(ipold所指向的类型 )个字节。就是说, ipnew所指向的内存区将比 ipold所指向的内存区向高地址方向移动了 n乘 sizeof(ipold所指向的类型 )个字节。
一个指针 ipold减去一个整数 n后,结果是一个新的指针 ipnew, ipnew的类型和 ipold的类型相同, ipnew所指向的类型和 ipold所指向的类型也相同。 ipnew的值将比 ipold的值减少了 n乘 sizeof(ipold所指向的类型 )个字节,就是说, ipnew所指向的内存区将比 ipold所指向的内存区向低地址方向移动了 n乘 sizeof(ipold所指向的类型 )个字节。
指向同一个数组中的不同元素的两个指针可以进行各种关系运算。例如:
ip1==ip2表示 ip1和 ip2指向同一数组元素
ip1>ip2表示 ip1处于高地址位置
ip1<ip2表示 ip2处于低地址位置
指针变量还可以与 0比较。设 ip为指针变量,则 ip==0表明 ip是空指针,它不指向任何变量; ip!=0表示 ip不是空指针。空指针是由对指针变量赋予 0值而得到的。例如:
#define NULL 0
int *ip=NULL;
对指针变量赋 0值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而指针变量赋 0值后,则可以使用,只是它不指向具体的变量而已。
取地址运算符 &是单目运算符,其结合性为自右至左,其功能是取变量的地址。
取内容运算符 *是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在 *运算符之后跟的变量必须是指针变量。需要注意的是指针运算符 *和指针变量说明中的指针说明符 * 不是一回事。在指针变量说明中,‘ *’是类型说明符,表示其后的变量是指针类型。而表达式中出现的‘ *’则是一个运算符用以表示指针变量所指的变量。如下例子:
int a=12;
int b;
int *p;
int **ptr;
p=&a; //&a 的结果是一个指针,类型是 int* ,指向的类型是 int ,指向的地址是 a 的
// 地址。
*p=24; //*p 的结果,在这里它的类型是 int ,它所占用的地址是 p 所指向的地址。
ptr=&p; //&p 的结果是个指针,该指针的类型是 p 的类型加个 * ,在这里是 int ** 。该
// 指针所指向的类型是 p 的类型,这里是 int* 。该指针所指向的地址就是指针
//p 自己的地址。
*ptr=&b;//*ptr 是个指针, &b 的结果也是个指针,且这两个指针的类型和所指向的类型 // 是一样的,所以用 &b 来给 *ptr 赋值就是毫无问题的了。
**ptr=34;//*ptr 的结果是 ptr 所指向的东西,在这里是一个指针,对这个指针再做一次 *
// 运算,结果就是一个 int 类型的变量。
在解释组合说明符时, 标识符右边的方括号和圆括号优先于标识符左边的“*”号,而方括号和圆括号以相同的优先级从左到右结合。但可以用圆括号改变约定的结合顺序。
阅读组合说明符的规则是“从里向外”。从标识符开始,先看它右边有无方括号或园括号,如有则先作出解释,再看左边有无*号。 如果在任何时候遇到了闭括号,则在继续之前必须用相同的规则处理括号内的内容。
一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表式。所以指针表达式也具有指针所具有的四个要素 :指针的类型 ,指针所指向的类型 ,指针指向的内存区 ,指针自身占据的内存。(预知后事如何,且听下回分解!!!)