指针的相关特性和使用

int *p;

如上图所示:

我们把 p 称为指针变量,

p里存储的内存地址处的内存称为 p 所指向的内存

指针变量 p 里存储的任何数据都将被当作地址来处理。

“*”号理解

举个例子:当你回到家门口时,你想进屋第一件事就是拿出钥匙来开锁。那你想想防盗门的锁芯是不是很像这个“*”号?你要进屋必须要用钥匙,那你去读写一块内存是不是也要一把钥匙呢?这个“*”号就是不是就是我们最好的钥匙?使用指针的时候,没有它,你是不可能读写某块内存的。

  1. char *p = “abcdef”;
  2. char a[] = “123456”;

指针的形式访问和以下标的形式访问指针:

例子 A)定义了一个指针变量 p,p 本身在栈上占 4 个 byte,p 里存储的是一块内存的首 地址。这块内存在静态区,其空间大小为 7 个 byte,这块内存也没有名字。对这块内存的访 问完全是匿名的访问。比如现在需要读取字符‘e’,

我们有两种方式:

  1. 以指针的形式:*(p+4)

先取出 p 里存储的地址值,假设为 0x0000FF00,然后加 上 4 个字符的偏移量,得到新的地址 0x0000FF04。然后取出 0x0000FF04 地址上的值。

  1. 以下标的形式:p[4]

编译器总是把以下标的形式的操作解析为以指针的形式的操 作。p[4]这个操作会被解析成:先取出 p 里存储的地址值,然后加上中括号中 4 个元素的偏 移量,计算出新的地址,然后从新的地址中取出值。

也就是说以下标的形式访问在本质上 与以指针的形式访问没有区别,只是写法上不同罢了。

指针的形式访问和以下标的形式访问数组:

例子 B)定义了一个数组 a,a 拥有 7 个 char 类型的元素,其空间大小为 7。数组 a 本身 在栈上面。对 a 的元素的访问必须先根据数组的名字 a 找到数组首元素的首地址,然后根据 偏移量找到相应的值。这是一种典型的“具名+匿名”访问。比如现在需要读取字符‘5’,

我们有两种方式:

  1. 以指针的形式:*(a+4)

a 这时候代表的是数组首元素的首地址,假设为 0x0000FF00, 然后加上 4 个字符的偏移量,得到新的地址 0x0000FF04。然后取出 0x0000FF04 地址上的 值。

  1. 以下标的形式:a[4]

编译器总是把以下标的形式的操作解析为以指针的形式的操 作。a[4]这个操作会被解析成:a 作为数组首元素的首地址,然后加上中括号中 4 个元素的 偏移量,计算出新的地址,然后从新的地址中取出值。

由上面的分析,我们可以看到,指针和数组根本就是两个完全不一样的东西。只是它们 都可以“以指针形式”或“以下标形式”进行访问。一个是完全的匿名访问,一个是典型 的具名+匿名访问。一定要注意的是这个“以 XXX 的形式的访问”这种表达方式。

另外一个需要强调的是:

上面所说的偏移量 4 代表的是 4 个元素,而不是 4 个 byte。只不过这里刚好是 char 类型数据 1 个字符的大小就为 1 个 byte。记住这个偏移量的单位是元素的个数而不是 byte 数,在计算新地址时千万别弄错了。

int a[5]={1,2,3,4,5}

a是数组首元素的首地址;也就是 a[0]的 首地址

&a是数组的首地址;

对指针进行加 1 操作,得到的是下一个元素的地址,而不是原有地址值直接加 1。

所以,一个类型为 T 的指针的移动,以 sizeof(T) 为移动单位。

&a + 1: 取数组 a 的首地址,该地址的值加上 sizeof(a) 的值,即 &a + 5*sizeof(int),

也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。

(int *)(&a+1): 把上一步计算出来的地址,强制转换为 int * 类型。

a+1 是数组下一元素的首地址,即 a[1]的首地址。

指针和数组特性

指针数组

保存数据的地址

任何存入指针变量 p 的数 据都会被当作地址来处理。

p 本身的地址由 编译器另外存储,存储在哪里,我们并不知道。

保存数据

数组名 a 代表的是数组首元素的 首地址而不是数组的首地址。

&a 才是整个数组的首地址。

a 本身的地址由编译器另外存储,存储在哪里,我们并不知道。

间接访问数据

首先取得指针变量 p 的内容,把它作为地址,然后从这个地址提取数据或向这个地址写入数据。

指针可以以指针的形 式访问*(p+i);也可以以下标的形式访问 p[i]。 但其本质都是先取 p 的内容然后加上 i*sizeof(类型)个 byte 作为数据的真正地址。

直接访问数据

数组名 a 是整个数组的名字, 数组内每个元素并没有名字。只能通过“具名+匿名”的方式来访问其某个元素,不能把 数组当一个整体来进行读写操作。

数组可以 以指针的形式访问*(a+i);也可以以下标的形式访问 a[i]。但其本质都是 a 所代表的数组首 元素的首地址加上 i*sizeof(类型)个 byte 作为 数据的真正地址。

通常用于动态数据结构

通常用于存储固定数目且数据类型相同的元 素。

相关的函数为 malloc 和 free。

隐式分配和删除

通常指向匿名数据(当然也可指向具名数据)

自身即为数组名

指针数组和数组指针

指针数组首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身 决定。它是“储存指针的数组”的简称。

数组指针首先它是一个指针,它指向一个数组。在 32 位系统下永远是占 4 个字节, 至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

A)int *p1[10];     指针数组

B)int (*p2)[10];   数组指针

二维数组

char a[3][4]

例子:

#include <stdio.h>

int main(int argc,char * argv[])

{

int a [3][2]={(0,1),(2,3),(4,5)};  // int a [3][2]={ 1, 3, 5};

int *p;

p=a [0];

printf("%d",p[0]);

}

逗号运算符确保操作数被顺序地处理:先计算左边的操作数,再计算右边的操作数。

右操作数的类型和值作为整个表达式的结果。

左操作数只是为了副作用需要而被计算,它其值会被丢弃

例子:

int a[5][5];

int (*p)[4];

p = a;

问&p[4][2] - &a[4][2]的值为多少?  -4

二级指针

char **p;

定义了一个二级指针变量 p。

p 是一个指针变量,毫无疑问在 32 位系统下占 4 个 byte。

它与一级指针不同的是,

一级指针保存的是数据的地址

二级指针保存的是一级指针的地址

数组参数和指针参数

暂时理解不是很透彻,后续补充!

函数指针

顾名思义,函数指针就是函数的指针。

它是一个指针,指向一个函数

看例子:

char * (*fun1)(char * p1,char * p2);

使用函数指针的好处在于,可以将实现同一功能的多个模块统一起来标识,这样一来更容易后期的维护,系统结构更加清晰。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。

函数指针数组

char * (*pf[3])(char * p);

函数指针数组的指针

char * (*(*pf)[3])(char * p);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值