C语言第十一章指针和数组
本章真正理解了,才算是理解了C语言的精华
主要内容
1、指针与一维数组间的关系
2、指针与二维数组间的关系
3、指针数组及其应用
4、动态数组,动态内存分配
11.1指针和一维数组间的关系
指针和数组的关系极为密切
数组元素的等价引用形式:a[i] <-> *(a+i)
使用数组下标方式访问数组元素本质上也是通过元素所在地址访问的,首地址+偏移量,然后解引用。
为什么一个int指针能够指向一个整型数组呢?

p指向了数组的首地址,但是这并不意味着p指向整个数组,还要依赖于数组的存储方式(连续存储的),因此,一个int指针可以指向一个整型数组。
例:演示数组元素的引用方法

通过指针变量来指向数组:用数组首地址对指针变量进行初始化

p++不是增加1个字节,而是取决于p的基类型,这里的基类型是int,因此这里增加的字节数是 1*sizeof(int)。或者说是p指向的数组元素的基类型

因p保存的是数组的首地址,所以p也可看成是数组名。
所以产生了另外一种等价形式:p[i] <-> *(p+i) 前提是p指向a的时候。循环之前一定要先将p赋值为数组首地址。

用数组作为函数形参

指针变量作为函数形参,可以不适用数组下标循环,但是这种方法更加直观一些。

主函数怎么编写?

下面再来看看指针和二维数组之间的关系

a是第0行地址 &a[0]
a+i 是第i行的地址 &a[i]
*(a+i) <-> a[i] 第i行第0列地址
*(a+i)+j <-> a[i]+j 第i行第j列地址
*(*(a+i)+j) <-> *(a[i]+j) <-> a[i][j]
若要让一个指针指向二维数组int a[2][3],则应定义为
int (*p)[3];//行指针,基类型是int[3]
p = a;
p = &a[0];//指向第0行的“int [3]型”元素
第二种方式:将二维数组看成一维数组,有6个int型元素

根据相对偏移量进行元素访问:

例:输入一个3行4列的二维数组,然后输出这个二维数组的元素值。

用指针作为函数形参有两种:一种是指向二维数组行地址的指针变量,两层解引用,一层到行,一层到列。

第二种方法:通过列指针作为函数参数

指针、数组以及其他的类型混合
基本数据类型 int、long、char、short、float、double...
数组是一种数据类型,是从其他类型派生的类型
指针也是一种数据类型,也可以看成其他类型派生的类型
任何类型都可以作为指针或者数组的基类型。
int (*p)[3];数组作为基类型,指向一个二维数组
指针数组--用指针作为数组的基类型,元素为指针类型的数组
定义形式:
数据类型 *数组名[数组长度];
例如 :char *ptr[5] 有五个元素的字符指针数组,每一个元素都可以指向一个字符串。
例:国名字符串排序-指针数组
物理排序:真正改变字符串位置。使用二维数组方式,这种方式效率低下,整个字符串都要移动。

索引排序:交换了地址值

主函数编写:

修改一下主函数,这样运行程序会崩溃,没有对字符数组进行初始化就调用。

通过上面的例子,我们知道指针数组的一个重要应用就是字符串排序,另一个应用用于表示命令行参数。


定长数组存在的问题
空间效率差
容易出错(缓冲区溢出等)
两条基本原则:
存储长度固定数据集合的时候使用定长数组。
直到运行的时候才知道数据长度的可使用动态内存分配方式。
C上的内存映像

栈区及堆区特点:

栈区不需要程序员进行控制,堆区需要程序员进行申请释放,弊端是反复的申请释放会存在很多的碎片。


空指针与无类型的指针
空指针的用途(NULL)
定义指针时进行初始化,避免对未赋值指针的引用
作为状态比较,判断函数是否调用成功,就需要将函数的返回值与NULL进行比较,如果函数返回NULL,则表示分配内存没有成功。指针只可以和NULL进行比较,和其他的非指针类型是不能进行比较的。

释放内存:

例:一维动态数组


例:二维动态数组


小结
指针与数组间的关系
掌握二维数组在内存中的存储方式,以及一个x型的指针指向x型变量的地址,是理解二维数组的行指针和列指针的关键
数组和指针不是在所有情况下都是等同的,比如sizeof ,作为形参是可以互换的。
动态内存分配,需要通过程序员通过调用函数来申请和释放
指针和数组作为函数参数是通过间接寻址方式来改变指针指向的数据,可以通过指针将大量的数组传递给函数。

const 关键字 表示指针指向的地址存储的是一个常量,无法被修改,一旦修改,编译器就会报错。同样数组前面也可以加const 关键字,数组内容也不能修改。
高效使用指针是成为编程高手的必由之路。
指针的重要应用:作为函数参数,传地址调用,向函数传大量的数据或者从函数获取被修改的数据;动态内存分配来实现动态数组;实现动态数据结构(下一章)
数组使用的原则是明确有多大,避免越界。
字符数组注意字符串结束标志有没有
指针的使用原则:指针指向了哪里,指向的内容是什么。总的原则上就是永远清楚正在操作的那一块内存,内存中数据是什么,这种操作是否非法。

浙公网安备 33010602011771号