第4章 指针和数组

4.1 指针

int *p

在这里插入图片描述

​ 如上图所示,把p称为指针变量,p里存储的内存地址处的内存称为p所指向的内存,指针变量p里存储的任何数据都将被当作地址来处理。

​ 一个基本的数据类型(包括结构体等自定义类型)加上*号就构成了一个指针类型的模子。这个模子的大小是一定的,与 *号前面的数据类型无关。 *号前面的数据类型只是说明指针所指向的内存里存储的数据类型。所以,在32位系统下,不管什么样的指针类型,其大小都为4byte。

4.2 数组

int a[5]

在这里插入图片描述

​ 如上图所示,当我们定义一个数组a时,编译器根据指定的元素个数和元素的类型分配确定大小(元素类型大小*元素个数)的一块内存,并把这块内存的名字命名为a。名字a一旦与这块内存匹配就不能被改变,a[0],a[1]等为a的元素,但并非元素的名字,数组的每一个元素都是没有名字的。

4.3 指针与数组之间的恩恩怨怨

​ 指针就是指针,指针变量在32位系统下,永远占4个byte,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方都能通过这个指针变量访问到。

​ 数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数,数组可以存任何类型的数据,但不能存函数。

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

  • 以指针的形式访问和以下标的形式访问指针
char *p = "abcdef"

​ 定义指针变量p,p里存储的是一块内存的首地址,这块内存在静态区,其空间大小为7个byte,对这块内存的访问完全是匿名的访问。比如需要读取字符‘e’,有两种方式:*(p+4)与p[4]。

  • 以指针的形式访问和以下标的形式访问数组
char a[] = "123456"

​ 数组a拥有7个char类型的元素,其空间大小为7。对a的元素的访问必须先根据数组的名字a找到数组首元素的首地址,然后根据偏移量找到相应的值,这是一种典型的”具名+匿名“访问。比如需要读取字符‘5’,有两种方式:*(a+4)与a[4]。

​ 偏移量的单位是元素的个数而不是byte数。

4.3.2 a和&a的区别

​ 对指针进行加1操作,得到的是下一个元素的地址,而不是原有的地址值直接加1。所以,一个类型为T的指针的移动,以sizeof(T)为移动单位。数组a代表的是数组首元素的首地址而不是数组的首地址。&a才是整个数组的首地址。

4.3.3 指针和数组的定义与声明

//定义为数组,声明为指针
char a[100];
extern char *a;
//定义为指针,声明为数组
char *p = "abcdefg";
extern char p[];

4.3.4 指针和数组的对比

指针数组
保存数据的地址,任何存入指针变量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。隐式分配和删除。
通常指向匿名数据(当然也可指向具名数据)。自身即为数组名。

4.4 指针数据和数组指针

int *p1[10]; //指针数组,存储指针的数组
int (*p2)[10]; //数组指针,指向数组的指针

4.5 多维数组与多级指针

4.5.1 二维数组

  • 假想中的二维数组布局
char a[3][4];

在这里插入图片描述

  • 内存与尺子的对比

    实际上内存不是表状的,而是线性的。

在这里插入图片描述
​ a[i] [j]的首地址为&a[i]+j*sizeof(char),再把&a[i]的值用a表示,得到a[i] [j]元素的首地址为:a+i *sizeof(char) *4+j *sizeof(char)。同样,可以换算成指针的形式表示: *( *(a+i)+j)。

4.5.2 二级指针

char **p;

​ 定义了一个二级指针变量p,它与一级指针不同的是,一级指针保存的是数据的地址,二级指针保存的是一级指针的地址。

在这里插入图片描述

4.6 数组参数与指针参数

​ 参数分为形参和实参,形参是指声明或定义函数时的参数,而实参是在调用函数时主调函数传递过来的实际值。

4.6.1 一维数组参数

​ 无法向函数传递一个数组。

void fun(char a[10])
{
    int i = sizeof(a);
    char c = a[3];
}

​ i的值为4,C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。函数本身是没有类型的,只有函数的返回值才有类型。实际传递的数组大小与函数形参指定的数组大小没有关系。

4.6.2 一级指针参数

​ 无法把指针变量本身传递给一个函数。

4.6.3 二维数组参数与二维指针参数

数组参数等效的指针参数
数组的数组:char a[3] [4]数组的指针:char (*p)[10]
指针数组:char *a[5]指针的指针:char **p

​ C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素受地址的指针之后,后面的维再也不可改写。比如:a[3] [4] [5]作为参数时可以被改写为(*p)[4] [5]。

4.7 函数指针

4.7.1 函数指针的定义

​ 函数指针就是函数的指针,它是一个指针,指向一个函数。

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

​ fun1不是函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。

4.7.2 函数指针的使用

​ 我们使用指针的时候,需要通过钥匙(*)来取其指向的内存里面的值,函数指针使用也是如此。使用函数指针的好处在于,可以将实现同一功能的多个模块统一起来标识,这样一来更容易后期的维护,系统结构更加清晰。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。

4.7.3 函数指针数组

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

​ 这是定义一个函数指针数组。它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值