c语言指针
什么是指针?
在计算机中内存是以字节为单位的连续编址空间,每一个字节单元对应着一个独一的编号,这个编号被称为内存单元的地址。
在特定的CPU架构下,内存地址的大小是固定的。
32位平台下地址为32个bit,即4个字节;
64位平台下地址为64个bit,即8个字节。
地址和指针:系统的内存好比带编号的小房间,地址对应着内存中的每个字节的编号,而指针也就是内存地址,它描述了数据在内存中的位置。
指针变量:指针变量本身不存储实际数据,而存储数据在内存中的地址,口语中的指针通常指的是指针变量。
指针的作用
使程序简洁、紧凑、高效、有效表示复杂的数据结构、动态分配内存、能直接访问内存、方便处理字符串、得到多于一个的函数返回值
指针变量的一般形式
格式:**<数据类型> * <指针变量名>;,如:int * p;**
数据类型要和这个地址中保存的数据的数据类型要保持一致其中 “ * ” 表示声明该变量是一个指针变量
指针变量的初始化可以是:已定义变量的地址、已初始化的同类型指针变量、NULL(空指针)。
注意:指针的使用一定要初始化,否则指针的地址值是不确定的(造成野指针),会造成非法访问等异常问题。
指针变量的赋值和引用
指针变量和普通变量一样,使用之前不仅需要声明,而且需要赋值(必须是地址常量或指针变量),未经赋值的指针变量不能使用。
在给指针变量赋值只能赋予地址,不能赋予任何其他数据,否则会引起错误。如:int *p = 2;(这样赋值是错误的)
(1) 定义指针变量,同时进行赋值
1 | int a; // 声明变量a,开辟a变量的地址空间 |
(2) 定义指针变量后再赋值
1 | int a; |
指针的目标:指针指向的内存区域中的数据称为指针的目标
(3) 指针变量的引用
格式:* 指针变量,含义是引用指针变量所指向目标的值
注意:定义变量时的 “ * “ 和引用时的 “ * “ 不同,定义时表示是个指针,在非定义时使用是解引用
什么是野指针?
野指针是指指针指向的位置是不可知的,所以当我们使用解引用去访问野指针时可能会产生不可知的结果。
野指针的危害
- 非法访问内存发生段错误,异常退出;
- 程序错误被掩盖,指向一个可用但无意义的空间;
- 程序出现离奇错误,指向可用空间,但该空间已被其他变量使用,造成程序崩溃或数据被损坏;
如何避免野指针
- 定义指针时应该初始化为NULL;
- 在使用指针之前,赋值绑定一个可用的地址空间;
- 在指针解引用之前,先判断指针是否为NULL;
- 小心指针越界;
- 指针使用完将其赋值为空;
- 避免返回局部变量的地址;
指针的基本运算符
运算符 | 名称 | 作用 | 运算符类型 | 结合性 |
---|---|---|---|---|
& | 取地址 | 获得变量的地址 | 单目运算符 | 自右向左 |
* | 指针 | 返回指定地址内的变量值 | 单目运算符 | 自右向左 |
指针的算术运算
指针的运算是以指针所存的地址作为运算量而进行的,实质上就是地址的计算。
运算符 | 计算形式 | 意义 |
---|---|---|
+ | px + n | 指针地址大的方向移动n个数据 |
- | px - n | 指针地址小的方向移动n个数据 |
++ | px++ | 指针地址大的方向移动1个数据 |
– | px– | 指针地址小的方向移动n个数据 |
- | px - py | 两个指针直接相隔数据元素的个数 |
指针的关系运算(指向的地址位置之间的关系)
运算符 | 说明 | 例子 |
---|---|---|
> | 大于 | px > py |
< | 小于 | px < py |
>= | 大于等于 | px >= py |
<= | 小于等于 | px <= py |
!= | 不等于 | px != py |
== | 等于 | px == py |
注意:不同类型的指针不能进行比较
小端序和大端序
小端序:数据低位字节保存在内存的低地址,高位字节保存在内存的高地址
大端序:数据低位字节保存在内存的高地址,高位字节保存在内存的低地址
内存地址 | 小端序 | 大端序 |
---|---|---|
0x7ffffcf75c68 | 78 | 12 |
0x7ffffcf75c69 | 56 | 34 |
0x7ffffcf75c6a | 34 | 56 |
0x7ffffcf75c6b | 12 | 78 |
数组指针和指针数组
数组指针(行指针)
存储行地址的指针变量,叫做行指针变量。形式如下:**<数据类型> (变量名)[大小];*
1 | // 如这行语句定义了一个指向包含5个整型数组的指针 |
指向数组的指针,其本质是指针,可赋值一维数组名取地址或二维数组名。
指针数组
由若干个相同数据类型的指针变量构成的集合,其形式为:**<数据类型> 变量名[大小];*
是一维数组,常结合二维数组使用,存储每行首个元素的地址,其本质是数组,存储内容为指针。
1 | // 如这行语句定义了数组p,其存储的数据类型为int *类型 |
数组指针是指针数组在内存中的起始地址,数组名代表数组指针的起始地址
指针与数组的使用事项:因为指针变量和数组名都是地址量,一定条件下其使用的方法具有相同形式。
含义不同:指针代表存储地址的变量,而数组名代表一个数组
使用不同:指针是变量,可以存储其他地址值,也可以递增,数组名是地址常量,不能修改
长度不同:数组名的长度是整个数组的总空间、指针变量长度取决于操作系统4字节(32),8字节(64)。
本质不同:指针变量是地址变量,数组名是地址常量
const和指针
在C语言中,const可以使变量常量化,即被它修饰的变量的值是不可修改的
1 | // 以下两种写法都是合法的,其中a的值为不可修改的 |
当使用const修饰指针时,可进行以下定义:
- 常量化指针目标:该指针是可以修改的,即指针可以改变指向,但不可以通过该指针对其指向的目标进行修改
1 | const int * p; |
- 常量化指针变量:该指针是不可以修改的,即指针不可以改变指向,但可以通过该指针对其指向的目标进行修改。
1 | int * const p; |
- 常量化指针变量和指针目标:该指针既不可以修改指向,同时也不能修改该指针指向的目标值
1 | const int * const p; |
void指针
void指针是不确定数据类型的指针变量,它可以通过强制转换让该变量指向任何数据类型的变量或数组
1 | int a = 100; |
在没有进行强制类型转换之前,void型指针不能进行任何指针的算术运算,因为void *相当于类型不确定,此时若不进行强制转换的话,我们只知道该指针的首地址,而不知道其占用的字节数,因此无法知道该取多少或者偏移多少。
字符串指针
字符串指针就是存储字符变量的地址,在C语言中,通常借助字符数组来存储字符串,字符指针可以存储字符串的起始地址。
在使用字符指针指向字符数组时:
虽然数组名表示数组的首地址,但由于数组名是指针常量,其值是不能改变的,可通过指针移动来访问和修改。
使用scanf时如果指针变量不是字符数组的首地址,则需要使用取地址符&。
注意:当一个字符指针初始化为一个字符常量时,不能对字符指针变量的目标进行赋值。
1 | char *s = "Hello World!"; // 这是定义字符串常量,其目标值不可修改,相当于省略了const |