当初学C语言,指针是最头疼的地方,难以理解,不过时间长了就自然明白了
通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 &
运算符访问的地址,它表示了在内存中的一个地址。
注意:
用register
修饰的变量是没法取到地址的,没有内存位置
指针
也就是内存地址
,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为: type *var_name;
type
是指针的基类型,它必须是一个有效的 C 数据类型var_name
是指针变量的名称。用来声明指针的星号 *
与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针
。所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
请看下面的实例,它将输出定义的变量地址:
#include <stdio.h>int main ()
{int var_runoob = 10;int *p; // 定义指针变量p = &var_runoob;printf("var_runoob 变量的地址: %pn", p);return 0;
}结果:
var_runoob 变量的地址: 0x7ffeeaae08d8
int *p=&a
是对的,但是*p=&a;
却不对?
int *p=&a;
这一句定义了一个变量p
,p
是一个指向int型
的指针
,即p
的数据类型是int*
,而&a
的数据类型也是int*
。这条语句可以分写为 int *p;p=&a;
所以这一句没问题。
*p=&a;
这一句是对(*p
)进行赋值操作,(*p
)的数据类型是int型
,而&a
的数据类型是int*
,两者类型不同,无法进行赋值,所以语句是错的。
*p=a
和p=&a
是正确的,*p=a
的意思:将a的值
赋给p指针
指向的地址的值;p=&a
的意思是:将a的地址
赋给指针p
;
区别:*p
是一个值
;p
是一个地址
;两者完全不相同,*
代表着p
指向的地址的值,简单来说就是取值;&
是取地址
符号,取的是地址
;p
是指针
,可以理解为所指向的值的地址,*p
就是取p指针
指向的地址的值,&a
就是取a的地址。
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 *
来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
#include <stdio.h>int main ()
{int var = 20; /* 实际变量的声明 */int *ip; /* 指针变量的声明 */ip = &var; /* 在指针变量中存储 var 的地址 */printf("var 变量的地址: %pn", &var ); /* 在指针变量中存储的地址 */printf("ip 变量存储的地址: %pn", ip ); /* 使用指针访问值 */printf("*ip 变量的值: %dn", *ip ); return 0;
}结果:
var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL
值是一个良好的编程习惯。
赋为 NULL
值的指针被称为空指针
。
NULL
指针是一个定义在标准库中的值为零的常量
#include <stdio.h>
int main (){int *ptr = NULL; printf("ptr 的地址是 %pn", ptr ); return 0;
}结果:
ptr 的地址是 0x0
在大多数的操作系统上,程序不允许访问地址为0
的内存,因为该内存是操作系统保留的。然而,内存地址 0
有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,可以使用 if 语句,如下所示:
if(ptr) /* 如果 p 非空,则完成 */
if(!ptr) /* 如果 p 为空,则完成 */
C 指针是一个用数值
表示的地址
。因此,可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-
整型指针:假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:ptr++
,在执行完上述的运算之后,ptr
将指向位置 1004
,因为 ptr 每增加一次,它都将指向下一个整数位置
,即 当前位置往后移 4 字节
。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。
字符型指针:如果 ptr 指向一个地址为 1000 的字符
,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
指针的每一次递增,它其实会指向下一个元素的存储单元。
指针的每一次递减,它都会指向前一个元素的存储单元。
指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度
,比如 int 就是 4 个字节。
我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。
下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
#include <stdio.h>
const int MAX = 3;
int main ()
{int var[] = {10, 100, 200};int i, *ptr; /* 指针中的数组地址 */ptr = var;for ( i = 0; i < MAX; i++){ printf("存储地址:var[%d] = %pn", i, ptr );printf("存储值:var[%d] = %dn", i, *ptr ); /* 指向下一个位置 */ptr++;}return 0;
}结果:
存储地址:var[0] = e4a298cc
存储值:var[0] = 10
存储地址:var[1] = e4a298d0
存储值:var[1] = 100
存储地址:var[2] = e4a298d4
存储值:var[2] = 200
同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:
#include <stdio.h>
const int MAX = 3;
int main ()
{int var[] = {10, 100, 200};int i, *ptr; /* 指针中最后一个元素的地址 */ptr = &var[MAX-1];for ( i = MAX; i > 0; i--){printf("存储地址:var[%d] = %pn", i-1, ptr );printf("存储值:var[%d] = %dn", i-1, *ptr );/* 指向下一个位置 */ptr--;}return 0;
}结果:
存储地址:var[2] = 518a0ae4
存储值:var[2] = 200
存储地址:var[1] = 518a0ae0
存储值:var[1] = 100
存储地址:var[0] = 518a0adc
存储值:var[0] = 10
指针可以用关系运算符进行比较,如 ==、< 和 >
。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:
#include <stdio.h>
const int MAX = 3;
int main ()
{int var[] = {10, 100, 200};int i, *ptr; /* 指针中第一个元素的地址 */ptr = var;i = 0;while ( ptr <= &var[MAX - 1] ){ printf("存储地址:var[%d] = %pn", i, ptr );printf("存储值:var[%d] = %dn", i, *ptr );/* 指向上一个位置 */ptr++;i++;}return 0;
}结果:
存储地址:var[0] = 0x7ffeee2368cc
存储值:var[0] = 10
存储地址:var[1] = 0x7ffeee2368d0
存储值:var[1] = 100
存储地址:var[2] = 0x7ffeee2368d4
存储值:var[2] = 200
先让我们来看一个实例,它用到了一个由 3 个整数组成的数组:
#include <stdio.h>
const int MAX = 3;
int main ()
{int var[] = {10, 100, 200};int i; for (i = 0; i < MAX; i++){printf("Value of var[%d] = %dn", i, var[i] );}return 0;
}结果:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
可能有一种情况,我们想要让数组存储指向 int 或 char 或其他数据类型的指针。这是一个指向整数的指针数组的声明:
int *ptr[MAX];
在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。因此是由指针构成的数组
#include <stdio.h>
const int MAX = 3;
int main ()
{int var[] = {10, 100, 200};int i, *ptr[MAX];for ( i = 0; i < MAX; i++) {ptr[i] = &var[i]; /* 赋值为整数的地址 */}for ( i = 0; i < MAX; i++){printf("Value of var[%d] = %dn", i, *ptr[i] );}return 0;
}结果:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
指针数组和数组指针的区别:
int *p[3]
指针的数组
,首先这个变量是一个数组。指针
修饰这个数组,意思是说这个数组的所有元素都是指针类型。int (*p)[3]
数组的指针
,首先这个变量是一个指针。数组
修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址在C语言中,字符串数组
可以用字符数组
来表示。字符数组
是由一系列字符组成的数组,每个字符占用一个字节的内存空间。字符串是以