指针是什么,C语言指针的用法(非常详细)
指针是 C语言的一个重要组成部分,是 C语言的核心、精髓所在。
用好指针,可以在 C语言开发中起到事半功倍的效果。一方面,可以提高程序的编译效率、执行速度,以及动态存储分配;另一方面,可使程序更加灵活,表示和操作各种数据结构更便捷,编写出高质量的应用程序。
通常来说,系统会按字节对每个内存单元进行编号,这些内存单元就好比是许多带有编号的小房间,要想使用内存,就需要知道房间编号。
例如,定义一个整型变量 i 需要 4 个字节,编译器就会为其分配 4 个内存单元,如下图所示:

图 1 变量在内存中的存储
1000~1003 就是对应变量 i 的 4 个内存单元地址,其内存放的数值是 0。假设又定义了一个整型变量 j,则 j在内存中的起始地址是 1004,因为 j 同样占 4 个字节,所以 1004~1007 单元中保存的是 j,其值是 1。
同理,1008~1012、1012~1016、1016~1020 也同样是 3 个整型变量对应的内存空间,如下图所示。

图 2 变量存放
可见,编译系统为每个变量都分配了一个能满足其类型大小的内存单元地址,访问该地址就能找到对应变量。
那么,程序设计中,又该如何访问变量的地址呢?前面我们学习过取地址符“&”,其作用是获取变量在内存中的地址。
例如,&i 表示变量 i 的内存起始地址 1000。通过 &i 可访问变量 i 指向的内存单元,进而读取变量 i。如下图所示,“指向”某个变量地址用箭头表示,就好像旅店住宿时某个房间号牌指向某个房间一样。

图 3 指针
为了好记忆,我们将变量的地址形象化地称为该变量的“指针”,意思是通过它能访问以它为地址的内存单元。
例如,通过地址 1000 可以访问变量 i 的地址,过它能访问以它为地址的内存单元。例如,通过地址 1000 可以访问变量 i 的地址,进而读取 4 个字节得到 i 的值。
例如,将变量 i 的地址 &i 存放到指针变量 p 中,p 就指向 i,其关系如下图所示:

图 4 地址与指针
程序代码中一般通过变量名对内存单元进行存取操作,但代码编译后,会将变量名转换为该变量在内存中的存放地址,后续对变量值的存取都是通过该地址进行的。
例如,对图 2 中的变量 i 和变量 j 进行 i+j 求和操作,计算机内部的存取流程是:根据变量名与地址的对应关系,找到变量 i 的地址 1000,然后从 1000 开始读取 4 个字节数据放到 CPU 寄存器中,再找到变量 j 的地址 1004,从 1004 开始读取 4 个字节的数据放到 CPU 的另一个寄存器中,通过 CPU 的加法中断计算出结果。
低级语言(如汇编语言)中一般直接通过地址来访问内存单元,高级语言中一般使用变量名来访问内存单元。C语言作为高级语言,仍然提供了通过地址来访问内存单元的方式。
与其他变量赋值不同,给指针变量赋值只能赋予地址,而不能是其他数据,否则将引起错误。
C语言一般用“&变量名”表示某个变量的地址,如 &a 表示变量 a 的地址,&b 表示变量 b 的地址。
给一个指针变量赋值可以有以下两种方法:
第一种,定义指针变量的同时进行赋值。例如:
第二种,先定义指针变量,之后再赋值。例如:
【实例】从键盘中输入一个数,利用指针将这个数的地址以十六进制形式输出。实现代码如下:
【实例】使用指针比较两个数的大小,实现代码如下:
例如,“p=&i;”是将变量 i 的内存地址赋给指针变量 p。“q=*p;”是将指针 p 指向的变量,即变量 i 的值赋给 q,假如变量 i 的值是 5,则 q 的值也是 5。
如果有如下语句:
“*&a”先进行“&”运算,“&a”就是取变量 a 的地址,然后执行“*”运算,“*&a”就相当于取变量 a 所在地址的值,实际就是变量 a。
【实例】定义了 3 个指针变量,使用“&*”计算 c=i+j,计算后输出变量 i,j,c 的地址值。
下面通过一个例子,读者仔细揣摩一下。
【实例】定义指针变量和整型变量,并将整型变量的地址赋值给指针变量,再进行指针自增,输出结果,比较自增前后的数值,看看能得出什么结论。代码如下:
将代码改成如下:
第一段实例代码的运行结果是因为变量 i 是基本整型,所以执行 p++ 后,p 的值增加 4(即 4 个字节);第二段实例代码的运行结果是因为 i 被定义成了短整型,所以执行 p++ 后,p 的值增加了 2(即 2 个字节)。
指针自增自减运算,是按照它所指向的数据类型的直接长度进行增或减。可以将第 1 段程序用下图来形象地表示出来。

图 5 指向整型变量的指针
用好指针,可以在 C语言开发中起到事半功倍的效果。一方面,可以提高程序的编译效率、执行速度,以及动态存储分配;另一方面,可使程序更加灵活,表示和操作各种数据结构更便捷,编写出高质量的应用程序。
C语言指针的概念
指针是 C语言的独特之处,其使用起来十分灵活。使用得当,能显著提高某些程序的效率;使用不当,则很容易造成系统错误。许多程序“挂死”往往都是错误地使用指针造成的。1) 地址与指针
要想弄明白什么是指针,就必须弄清楚数据在内存中是如何存储的,又是如何被读取的。通常来说,系统会按字节对每个内存单元进行编号,这些内存单元就好比是许多带有编号的小房间,要想使用内存,就需要知道房间编号。
例如,定义一个整型变量 i 需要 4 个字节,编译器就会为其分配 4 个内存单元,如下图所示:

图 1 变量在内存中的存储
1000~1003 就是对应变量 i 的 4 个内存单元地址,其内存放的数值是 0。假设又定义了一个整型变量 j,则 j在内存中的起始地址是 1004,因为 j 同样占 4 个字节,所以 1004~1007 单元中保存的是 j,其值是 1。
同理,1008~1012、1012~1016、1016~1020 也同样是 3 个整型变量对应的内存空间,如下图所示。

图 2 变量存放
可见,编译系统为每个变量都分配了一个能满足其类型大小的内存单元地址,访问该地址就能找到对应变量。
那么,程序设计中,又该如何访问变量的地址呢?前面我们学习过取地址符“&”,其作用是获取变量在内存中的地址。
例如,&i 表示变量 i 的内存起始地址 1000。通过 &i 可访问变量 i 指向的内存单元,进而读取变量 i。如下图所示,“指向”某个变量地址用箭头表示,就好像旅店住宿时某个房间号牌指向某个房间一样。

图 3 指针
为了好记忆,我们将变量的地址形象化地称为该变量的“指针”,意思是通过它能访问以它为地址的内存单元。
例如,通过地址 1000 可以访问变量 i 的地址,过它能访问以它为地址的内存单元。例如,通过地址 1000 可以访问变量 i 的地址,进而读取 4 个字节得到 i 的值。
2) 变量与指针
变量的地址是变量和指针之间的连接纽带。所谓“指向”,是通过地址来体现的。因为指针变量通常指向一个变量的地址,所以将一个变量的地址赋给指针变量后,这个指针变量就“指向”了该变量。例如,将变量 i 的地址 &i 存放到指针变量 p 中,p 就指向 i,其关系如下图所示:

图 4 地址与指针
程序代码中一般通过变量名对内存单元进行存取操作,但代码编译后,会将变量名转换为该变量在内存中的存放地址,后续对变量值的存取都是通过该地址进行的。
例如,对图 2 中的变量 i 和变量 j 进行 i+j 求和操作,计算机内部的存取流程是:根据变量名与地址的对应关系,找到变量 i 的地址 1000,然后从 1000 开始读取 4 个字节数据放到 CPU 寄存器中,再找到变量 j 的地址 1004,从 1004 开始读取 4 个字节的数据放到 CPU 的另一个寄存器中,通过 CPU 的加法中断计算出结果。
低级语言(如汇编语言)中一般直接通过地址来访问内存单元,高级语言中一般使用变量名来访问内存单元。C语言作为高级语言,仍然提供了通过地址来访问内存单元的方式。
C语言指针的用法
一个变量的地址称为该变量的指针。如果有一个变量专门用来存放另一个变量的地址,它就是指针变量。在 C 语言中有专门用来存放内存单元地址的变量类型,即指针类型。1) 指针变量的定义
定义指针变量的一般形式如下:类型说明 * 变量名其中,“*”表示该变量是一个指针变量,“变量名”即为定义的指针变量名,“类型说明”表示本指针变量所指向的变量的数据类型。
2) 指针变量的赋值
指针变量同普通变量一样,使用前需要先进行定义并赋予具体的值。未经赋值的指针变量不能使用。与其他变量赋值不同,给指针变量赋值只能赋予地址,而不能是其他数据,否则将引起错误。
C语言一般用“&变量名”表示某个变量的地址,如 &a 表示变量 a 的地址,&b 表示变量 b 的地址。
给一个指针变量赋值可以有以下两种方法:
第一种,定义指针变量的同时进行赋值。例如:
int a; int *p=&a;
第二种,先定义指针变量,之后再赋值。例如:
int a; int *p; p=&a;
注意这两种赋值语句的区别。如果先定义指针变量之后再赋值,赋值时指针变量前不再加“*”。
【实例】从键盘中输入一个数,利用指针将这个数的地址以十六进制形式输出。实现代码如下:
#include<stdio.h> int main() { int a; /*定义整型变量*/ int *ipointer1; /*定义指针变量*/ printf("请输入数据:\n"); scanf("%d",&a); /*输入一个数*/ ipointer1 = &a; /*将地址赋给指针变量*/ printf("转化十六进制为:%x\n",*ipointer1); /*以十六进制形式输出该数*/ return 0; }程序运行结果为:
请输入数据:
1200
转化十六进制为:4b0
3) 指针变量的引用
引用指针变量是对变量进行间接访问的一种形式。引用指针变量的形式为“*指针变量”,其含义是引用指针变量所指向的值。【实例】使用指针比较两个数的大小,实现代码如下:
#include<stdio.h> int main() { int a=10, b=11; /*定义两个整型变量并初始化*/ int *ipointer1, *ipointer2; /*定义两个整型指针变量*/ ipointer1 = &a; /*将地址赋给指针变量*/ ipointer2 = &b; if (*ipointer1 > *ipointer2) /*使用指针比较两数大小*/ printf("a 的值大\n"); else printf("b 的值大\n"); return 0; }程序运行结果为:
b 的值大
4) “&”和“*”运算符
运算符“&”和“*”都是单目运算符:- “&”是取地址运算符,用于返回一个操作数的地址;
- “*”是指针运算符,用于返回指定地址内保存的变量值。
例如,“p=&i;”是将变量 i 的内存地址赋给指针变量 p。“q=*p;”是将指针 p 指向的变量,即变量 i 的值赋给 q,假如变量 i 的值是 5,则 q 的值也是 5。
5) “&*”和“*&”的区别
下面通过两条语句来分析“&*”和“*&”运算的区别。如果有如下语句:
int a; p=&a;因为“&”和“*”的优先级相同,按自右而左的方向结合,因此“&*p”先进行“*”运算,“*p”相当于变量 a;再进行“&”运算,“&*p”就相当于取变量 a 的地址。
“*&a”先进行“&”运算,“&a”就是取变量 a 的地址,然后执行“*”运算,“*&a”就相当于取变量 a 所在地址的值,实际就是变量 a。
【实例】定义了 3 个指针变量,使用“&*”计算 c=i+j,计算后输出变量 i,j,c 的地址值。
#include<stdio.h> int main() { long i, j, c; /*定义变量*/ long *p, *q, *n; /*定义指针变量*/ printf("please input the numbers:\n"); /*输入数据*/ scanf("%ld,%ld", &i, &j); /*输入数据*/ c = i + j; /*实现两数相加*/ p = &i; /*将地址赋给指针变量*/ q = &j; n = &c; printf("%ld\n", &*p); /*利用“&*”输出变量 i 的地址*/ printf("%ld\n", &*q); /*利用“&*”输出变量 j 的地址*/ printf("%ld\n", &*n); /*利用“&*”输出变量 c 的地址*/ return 0; /*程序结束*/ }程序运行结果为:
please input the numbers:
5,3
6487556
6487552
6487548
C语言指针的自增、自减运算
指针的自增自减运算不同于普通变量的自增自减运算,也就是说,并非简单地加 1 减 1,而是按照它所指向的数据类型的直接长度进行增减。下面通过一个例子,读者仔细揣摩一下。
【实例】定义指针变量和整型变量,并将整型变量的地址赋值给指针变量,再进行指针自增,输出结果,比较自增前后的数值,看看能得出什么结论。代码如下:
#include<stdio.h> int main() { int i; int *p; printf("please input the number:\n"); scanf("%d", &i); /*输入一个整型数*/ p = &i; /*将整型变量 i 的地址赋给指针变量*/ printf("the result1 is: %d\n", p); p++; printf("the result2 is: %d\n", p); }程序运行结果为:
please input the number:
23
the result1 is: 6487572
the result2 is: 6487576
将代码改成如下:
#include<stdio.h> main() { short i; short *p; printf("please input the number:\n"); scanf("%d", &i); /*输入一个短整型数*/ p = &i; /*将短整型变量 i 的地址赋给指针变量*/ printf("the result1 is: %d\n", p); p++; printf("the result2 is: %d\n", p); }程序运行结果为:
please input the number:
23
the result1 is: 6487574
the result2 is: 6487576
第一段实例代码的运行结果是因为变量 i 是基本整型,所以执行 p++ 后,p 的值增加 4(即 4 个字节);第二段实例代码的运行结果是因为 i 被定义成了短整型,所以执行 p++ 后,p 的值增加了 2(即 2 个字节)。
指针自增自减运算,是按照它所指向的数据类型的直接长度进行增或减。可以将第 1 段程序用下图来形象地表示出来。

图 5 指向整型变量的指针