C语言零基础入门-指针-04
本节要点:
1,指向一维数组的指针。
2,指向二维数组的指针。
3,指针的输出练习。
01. 指向一维数组的指针
有意思的来了,指针指向数组的情况非常常见,同样也是一个难点。
接下来我将从一个简单的栗子来说明指针指向数组时的内存情况。
01.1 数组存储的回顾。
当我们定义如下的一维数组的时候:
int a[5] = {1,2,3,4,5};
计算机会在地址空间中寻找连续的,可以存放下长度为5的,整形变量。也就是需要找一块连续的内存,这块内存长度为:5*4=20字节。
(为什么是20字节?因为这个数组有5个元素,每个元素都是int类型,int类型占4字节啊)
存储如下图:
在讲数组之前,上面的需要多少多少的内存空间都是理论推理出来的,现在学过指针之后,我们就可以使用指针来验证一下了。
01.2 利用指针说明数组的内存空间
01.2.1 知识点
开始之前大家要知道一个小知识点:我们之前使用数组作为实参传递数据的时候,我说过可以直接把数组名字作为实参就可以了。
其实数组名还有一个重要的含义,就是:数组名其实就是这个数组存储空间的首地址。
所以,之前我们传递参数的时候,其实就是把数组的首地址传递给子函数了,当时没说指针,因此就没细说。
下边了验证一下。
验证程序:
#include<stdio.h>
void main(){
int a[5] = {1,2,3,4,5};
int *p = a;
printf("%d",*p);
}
- 这一段代码执行结束发现会输出数组中的第一个元素 1。
- 所以说,数组名就是整个数组的起始地址。
- 因此当把a的地址给指针p的时候,a前边不用加取地址符&。
另一种获取数组地址方法:
这种方法被叫做通用方法。
#include<stdio.h>
void main(){
int a[5] = {1,2,3,4,5};
int *p = &a[0];
int *p2 = &a[3];
printf("%d\n",*p);
printf("%d\n",*p2);
}
- 这里如果指定获取某一个元素的地址的时候,就需要在前边加上取地址符号&了。(因为数组名代表数组首地址是一个特殊的定义)
01.2.2 栗子
刚才我们已经学会获取到数组的首元素指针了,接下来,我将会教大家怎么使用首元素指针来遍历到数组的全部元素。
分析:
- 目前,指针p指向的就是数组第一个元素的地址。
然后在数组中想让指针往后移动一个元素的地址很简单,只用 p+1就可以了,如下图:
用程序验证一下:(这里的数组元素换了)
#include<stdio.h>
void main(){
int a[5] = {5,4,3,2,1};
int *p = a;
printf("%d,%d,%d,%d,%d\n", *p,*(p+1),*(p+2),*(p+3),*(p+4));
}
运行结果:
程序解释:
- 数组元素我们换成5,4,3,2,1了。
- 首先p指向数组a的首地址,*p获得p指向的地址中的数据。
- 于是 p+1 就是把数组指针向后移动至第二个数据的地址。
- 在使用 *(p+1) 获得p+1所指向的地址中的数据,第二个数据是4。
02. 指向二维数组的指针
02.1 二维数组内存分配
当我们定义了如下的二维数组之后:
int a[4][3] = {
{9,6,3},
{8,5,2},
{7,4,1},
{7,5,3}
};
在内存中的存储如下图:
需要注意的是:
- 二维数组在内存中存储的时候,是把二维数组按行拆开,然后每一行顺序,依次的存储在计算机中的连续位置。
02.2 二维指针的定义
这里的定义就与一维数组有些不一样了。这个时候不能用数组名代表数组的初始地址了。所以我们可以用与前边一维数组定义相同的通用方法。
#include<stdio.h>
void main(){
int a[4][3] = {
{9,6,3},
{8,5,2},
{7,4,1},
{7,5,3}
};
int *p = &a[0][0];
printf("%d\n",*p);
}
- 这里p指向了二维数组的第一个元素9。
- 此时输出的就是第一个元素9。
02.3 二维数组遍历
- 在一维数组的遍历中,我们可以将指向首地址的指针p每次+1,就可以指向数组中的每一个元素了。
但是有人说,二维数组有行与列两个维度,要怎么处理啊?我们细看二维数组的存储图,你会发现:
管他几维的数组,其实在内存中存储的时候都只有一个维度,那就是把多维数组按行拆开后,依次顺序存放在计算机啊。
- 所以:二维数组也可以用+1的方式,遍历所有元素。
栗子:
#include<stdio.h>
void main(){
int a[4][3] = {
{9,6,3},
{8,5,2},
{7,4,1},
{7,5,3}
};
int *p = &a[0][0];
printf("%d\n",*p);
printf("%d\n",*(p+3));
printf("%d\n",*(p+11));
}
结果:
分析:
- p是指向第一个元素的,*p获得第一个元素 9。
- p+3就是把地址指针向后移动3个,然后就指到数据8所对应的地址上了。
- p+11就是把地址指针向后移动11个,然后就指到数据3所对应的地址上了。
03. 指针的输出练习
03.1 一维数组的输出练习
#include<stdio.h>
void main(){
int a[9] = {2,3,4,5,6,7,8,9,0};
int *p = &a[0];
for(int i=0;i<9;i++){
printf("%d ", *(p+i) );
}
}
结果:
分析:
- p指向数组a的第一个元素的地址,也就是首地址。
- 然后p每次都+i,表示地址p每次向后移动的数据位数。
03.2 二维数组的输出练习
#include<stdio.h>
void main(){
int a[4][3] = {
{9,6,3},
{8,5,2},
{7,4,1},
{7,5,3}
};
int *p = &a[0][0];
for(int i=0;i<12;i++){
printf("%d ", *(p +i) );
}
}
结果:
分析:
- 因为二维数组在内存中也是像一维数组那样每个元素按行顺序排放,所以,每次 p往后移动一个数据的位置,就可以遍历所有的元素了。
有人说,为什么没有换行啊?
- 数据都得到了,换行还会困难吗?
03.2.1 加入换行
我们只需要每输出三个数据,就换一个行不就完了嘛。
#include<stdio.h>
void main(){
int a[4][3] = {
{9,6,3},
{8,5,2},
{7,4,1},
{7,5,3}
};
int *p = &a[0][0];
for(int i=0;i<12;i++){
printf("%d ", *(p +i) );
if((i+1)%3 ==0){
printf("\n");
}
}
}
- 当
(i+1)%3 ==0
的时候,就是意味着输出了三个元素了,所以要输出一下换行符。 - 为什么要这样写自己去分析,因为已经很简单的。(我甚至连换行的程序都不想写的,想让大家自己写出来的。)
04. 结束
大致还有一次课,就会讲完指针了。