瞎聊
本文后面的内容,可以暂时看不懂,以后如果从事这一行,慢慢会理解,但是这句话要记住:如果 piInt 是一个指向整型的指针变量,那么 *piInt 就是一个整型变量;类似的,如果pcChar是一个指向字符型的指针变量,那么 *pcChar 就是一个字符型的变量;……
其实这部分内容,也许放在 C程序设计——基本变量类型(指针0)的前面更合适,但是思来想去,还是先把指针讲个差不多,再讲这个。
我希望大家记住这句话,将来再慢慢理解:C语言把计算机所有的外设,都抽象为变量。
当你用C语言访问显示器、鼠标、键盘时,最终访问的是一个一个的变量。当然有些变量,被用函数给封装起来,看起来好像是通过调用函数,来控制屏幕显示、获取鼠标状态。
变量
当你入学报名的时候,如果你住校,学校会给你分配一个宿舍,比如你们学校的第14号楼,3层有一个编号360的宿舍刚搞还有一个床位,然后老师大笔一挥,你就住这间了。我们把你住的这个宿舍用一串数字来表示就是: 14360,14代表第14号楼,360代表3层第60号房间。在你们学校,一说14360,就能唯一的标识你们宿舍,你问任何一个同学,都可以告诉你怎么去14号楼,然后上3层找60号宿舍。
计算机的也是一样的,所有外设,包括内存、硬盘、鼠标等等,都分配了很多地址,就好像前面讲了每6个床位分配一个宿舍号,计算机也类似,按照目前的标准,每8位二进制数分配一个地址,通常这8位二进制数,被看做一个整体,叫做一个字节。
C语言下,当你定义一个变量,像下面这样:
int iInt ;
编译器生成代码的时候,会按照既定的规则(算法)给变量 iInt 分配一片4字节(32bit)连续的内存空间,前面我说了,计算机给每个字节都分配的地址,这一个整形变量占连续4个字节,理论上就有四个地址,按那个算呢?C语言规定,地址中最小的一个,作为整形变量的地址。强调一下,当你定义一个变量是,无论这个变量占多少字节,计算机分配内存时,一定是分配一片地址连续的空间,来存储这个变量。
比如计算机给上述代码中的 iInt 分配了四个字节的空间,地址分别是 4、5、6、7,那么当你给 iInt 取地址(&iInt )时,得到地址将是4。比如下面这段代码:
int main(void)
{
int iInt ;
int *piInt ; // 定义指向整形的指针变量
piInt = &iInt; // 将iInt 的地址,赋值给 piInt
iInt = 100;
printf("piInt %d\r\n", piInt); // 打印指针变量的值
printf("&iInt %d\r\n", &iInt); // 打印 iInt 的地址值
printf("iInt %d\r\n", iInt); // 打印 iInt 的值
}
编译后运行结果如下:
我们从打印结果上可以看到变量 iInt 的值是 100,变量 iInt 的地址值是: 6422036。按照前面的讲解,变量 iInt 占4个字节,每个字节都有地址,其中最小的一个是 6422036,另外三个字节的地址就是 6422037、6422038、6422039 。
指针变量,也是变量
我们还看这段代码(不用比较了,就是从上面复制、粘贴过来的)
int main(void)
{
int iInt ;
int *piInt ; // 定义指向整形的指针变量
piInt = &iInt; // 将iInt 的地址,赋值给 piInt
iInt = 100;
printf("piInt %d\r\n", piInt); // 打印指针变量的值
printf("&iInt %d\r\n", &iInt); // 打印 iInt 的地址值
printf("iInt %d\r\n", iInt); // 打印 iInt 的值
}
piInt 是一个指针变量,它首先是一个变量,所以计算机也会给它分配地址连续的几个字节(有些计算机是4个字节,有些计算机是8个字节),所以从分配空间的角度,piInt 和 iInt,没有本质区别,如果硬要找区别,那就是分配的空间不一样,可能数量也不一样(当然数量也有可能一样)。既然 piInt 是变量,那就可以给它赋值,比如下面这样:
piInt = &iInt; // 将iInt 的地址,赋值给 piInt
表示把 iInt的地址,赋值给 piInt。根据运行结果 iInt 的地址其实就是一个数字6422036 ,如果我们能未卜先知,这里直接把 6422036赋值给 piInt,效果是一样的:
piInt = 6422036; // 如果能未卜先知,就可以用 6422036 来代替&iInt
获取指针指向的值
先明确一个概念,比如我定义一个整形变量 iInt,计算机给它分配的地址是 6422036,然后给 iInt 赋值 100,我们就说地址 6422036 指向的值是100,如果我们改变了 iInt 为 200,则我们说地址 6422036 指向的值是200 。
我们如果定义了一个指针变量 piInt,然后给它赋值 为 iInt 的地址(&iInt),给 iInt 赋值 100,我们就说指针变量 piInt 指向的值是100,如果我们改变了 iInt 为 200,则我们说指针变量 piInt 指向的值是200 。
那么我们如何获取指针 piInt 指向的值呢?像下面这样:
*piInt ;
请寻找下面两行的不同:
int *piInt ; // 定义一个指针变量
*piInt ; // 获取 piInt 指向的值
*piInt 左面有类型名,就是定义指针变量。没有类型名,就是获取指向的值。
指针指向的值还可以改变,像下面这样:
*piInt = 200; // 改变 piInt 指向的值,使之为200
int main(void)
{
int iInt ;
int iValue ;
int *piInt ; // 定义指向整形的指针变量
piInt = &iInt; // 将iInt 的地址,赋值给 piInt
iValue = *piInt ; // 获取 piInt 指向的值,并赋值给 iValue
*piInt = 200; // 改变 piInt 指向的值,使之为200
}
指针变量类型的意义
我们现在知道,定义指针的时候,要声明指针指向什么类型的数据,比如下面这些:
int main(void)
{
char *pcChar ; // 指向字符型变量的指针
short int *psShort ; // 指向短整形变量的指针
int *piInt ; // 指向整形变量的指针
long int *plLong ; // 指向长正向变量的指针
long long *pllLongLong; // 指向超长整型变量的指针
unsigned char *pcChar ; // 指向无符号字符型变量的指针
unsigned short int *psShort ; // 指向无符号短整型变量的指针
unsigned int *piInt ; // 指向无符号整形变量的指针
unsigned long int *plLong ; // 指向无符号长整型变量的指针
unsigned long long *pllLongLong; // 指向无符号超长整形变量的指针
float *pFloat ; // 指向单精度浮点数的指针
double *pDouble ; // 指向双精度浮点数的指针
}
我再本节最开头说了,再强调一次:如果 piInt 是一个指向整型的指针变量,那么 *piInt 就是一个整型变量;类似的,如果pcChar是一个指向字符型的指针变量,那么 *pcChar 就是一个字符型的变量;……
因为不同类型的变量,计算机给分配的空间是不一样的,比如 char型就是1个字节,short int 就是2个字节 ……,指针变量指定的类型, 获取指针指向的值时,才能知道该取几个字节的数据。
指针变量 + 1
先看下面这行代码:
piInt = &iInt; // 将iInt 的地址,赋值给 piInt
经过前面的讲解,我们知道 piInt 中存放的地址,是 iInt 的4个字节中,地址最小的那个字节的。piInt 逻辑上,代表的是 4个字节的地址,因此 piInt + 1 ,逻辑上代表 iInt 后面的那个 整型数据,因此 piInt = piInt + 1 ,piInt的值加了4 。
同样的下面代码定义的指针中:
int main(void)
{
char *pcChar ; // 指向字符型变量的指针
short int *psShort ; // 指向短整形变量的指针
int *piInt ; // 指向整形变量的指针
long int *plLong ; // 指向长正向变量的指针
long long *pllLongLong; // 指向超长整型变量的指针
unsigned char *pcChar ; // 指向无符号字符型变量的指针
unsigned short int *psShort ; // 指向无符号短整型变量的指针
unsigned int *piInt ; // 指向无符号整形变量的指针
unsigned long int *plLong ; // 指向无符号长整型变量的指针
unsigned long long *pllLongLong; // 指向无符号超长整形变量的指针
float *pFloat ; // 指向单精度浮点数的指针
double *pDouble ; // 指向双精度浮点数的指针
}
如果 pcChar= pcChar + 1,pcChar 加了 1;
如果 psShort = psShort + 1,psShort 加了2;
……