数组指针(一维数组)
数组指针就是" 数组的指针 ",它是一个指向数组首地址的指针变量。
1、数组名的含义
对于一维数组,数组名就是一个指针,指向数组的首地址。
基于如下代码进行分析:
int a[5] = {1,2,3,4,5};
int* p = a;
- a是数组a[5]的数组名,在这里a是个常量,一定指向的是a[5]的首地址,不能进行" ++ "改值运算。
- p是一个指针变量,它指向了a[5]的首地址,所以它是一个数组指针。在这里p是一个变量,可以进行" ++ "改值运算
2、数组名索引与指针索引
当我们想要访问1这个值的时候,可以有a[0]、*(a+0)、*(p+0)、p[0] 这四种访问形式。
- a[0]:基本的下标法访问
- *(a+0):a可以看作首地址,偏移量为数组的类型int大小,找到地址后解引用也可访问1这个值
- *(p+0):p保存了数组首地址,偏移量为int大小,通过解引用访问地址
- p[0]:特殊,记住即可
注意:*(p+0)、p[0]这两种方法是指针偏移的方法,它实际表示的值与p指向的位置有关。比如p指向的是a[1]这个位置,那么*(p+0)、p[0]访问的不是a[0]而是a[1],而*(p-1)、p[-1]访问的才是a[0]。
3、数组名与数组元素取地址
数组名a与数组元素取地址&a[ i ]它们的值一样,偏移量也一样。但数组名表示整个数组,数组元素取地址仅仅是一个指针。下面分析" a、&a[0]区别 "、" a+1、&a[0]+1 区别"
a、&a[0]的相同点:
- 地址值相同、偏移量也相同。
- 都是常量,都不能进行++等变值操作。
a、&a[0]的不同点:
- a不仅仅指向数组首地址,还可以代表数组;
- &a[0]只是一个指针,什么都不代表。
4、&a是什么
一维数组可以看成列指针,取地址之后变为行指针。
&a的作用与二维数组名类似:a看成列指针,偏移一个数据元素,&a看成行指针偏移一行。
数组指针(二维数组)
1、一维指针遍历二维数组
由于二维数组的元素在内存中是按行序进行排列的,内存排列依旧是连续的。所以在内存方面,它与一维数组没有任何区别,具体的二维数组内存分配如下:
遍历的代码如下:
#include <stdio.h>
/* 计算二维数组总大小 */
#define ARRAY2_SIZE(array,type) sizeof(array)/sizeof(type)
/* 计算二维数组中一维数组大小(列大小) */
#define ARRAY2_COL_SIZE(array) sizeof(array[0])
/* 计算二维数组的列数 */
#define ARRAY2_COL_NUM(array,type) ARRAY2_COL_SIZE(array)/sizeof(type)
/* 计算二维数组的行数 */
#define ARRAY2_ROW_NUM(array,type) sizeof(array)/ARRAY2_COL_SIZE(array)
int main(){
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *p = NULL;
int col,row;
int i;
col = ARRAY2_COL_NUM(a,int);
row = ARRAY2_ROW_NUM(a,int);
p = &a[0][0];//注意这里的赋值,p赋值的是a[0][0]的地址,而不是直接赋值数组名a
for(i=0;i<row*col;i++){
printf("%d %d\n",*(p+i),p[i]);
}
return 0;
}
上述代码有一个注意点,p=&a[0][0]而不是p=a。在二维数组中,尽管&a[0][0]的值与a相同,都是指向数组首地址,但是它们的偏移量不同。
2、二维数组的数组指针
二维数组名是一个指向二维数组的首地址的常量。它是一个代表二维数组,偏移量以行为单位。例如:int a[2][3]它每3列为一行,假设首地址为0x00,那么a+1 = 0x00 + sizeof(int) * 3。
指向二维数组的数组指针被称为" 行指针 "。它的定义形式为:<数据类型>(*变量名)[列数],如上述的int a[2][3],它的数组指针为:int (*p)[3] = a。
3、二维数组元素分析
二维数组可以看成两部分组成:二维数组由多个一维数组组成,一维数组由多个数据组成。
所以二维数组名解引用之后得到的是一维数组名,一维数组名解引用后得到的是数据元素。
即:*a+i = a[i],&a[0] = a
a 与 &a[0] 的区别
相同点:
- 地址值一样,偏移量一样。
- 都是常量,都不能进行++等变值操作。
不同点:
- a不仅仅指向数组首地址,还可以代表数组;
- &a[0]只是一个指针,sizeof(&a[0])返回的是指针大小,而不是二维数组大小
下面是用二维数组的数组指针来遍历的代码:
#include <stdio.h>
/* 计算二维数组总大小 */
#define ARRAY2_SIZE(array,type) sizeof(array)/sizeof(type)
/* 计算二维数组中一维数组大小(列大小) */
#define ARRAY2_COL_SIZE(array) sizeof(array[0])
/* 计算二维数组的列数 */
#define ARRAY2_COL_NUM(array,type) ARRAY2_COL_SIZE(array)/sizeof(type)
/* 计算二维数组的行数 */
#define ARRAY2_ROW_NUM(array,type) sizeof(array)/ARRAY2_COL_SIZE(array)
int main(){
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*p)[4] = a;
int row,col;
int i,j;
row = ARRAY2_ROW_NUM(a,int);
col = ARRAY2_COL_NUM(a,int);
for(i=0;i<row;i++){
for(j=0;j<col;j++){
printf("%-2d ",*(*p+j));//p是二维数组指针,*p就是a[i]即一维数组的指针,偏移量为int
}
printf("\n");
p++;//p是二维数组指针,偏移量为sizeof(int)*col即按行偏移
}
return 0;
}
上述代码中也可以用一个指针来寻址:例如a[3][4],寻址a[2][1]只需访问&a[0][0]+2*4+1
练习:使用a来访问a[2][1]这个元素
首先需要偏移到第2行,之后偏移到第1列,最后取值
- 偏移到第2行:a+2
- 偏移到第1列之前需要先转换成一维数组名:*(a+2)即a[2]
- 偏移到第1列:*(a+2)+1
- 取值:*(*(a+2)+1)
4、&a是什么
二维数组的&a分析方法与一维数组的分析类似。
- 在一维数组中,我们把a看作列地址,&a看成行地址。
- 在二维数组中,我们把整个二维数组看成列地址,代表偏移量为一行。&a看成行地址,代表偏移量为一整个二维数组。
数组指针(字符串)
字符串是一维数组的一种形式,数组指针的性质与一维数组的数组指针性质完全一样。
下面分析字符串常量与字符串变量的区别:
指针直接赋值字符串,而不是字符数组名时,该指针指向的就是字符串常量。
/* p指向字符串常量,不可修改值 */
char* p = "Hello";
/* p指向字符串变量,可以修改值 */
char a[] = "hello";
char*p = a;
相同点:
- 都是存放的字符串的首地址,都可以用%s进行打印
不同点:
- 对于char* p = "Hello";因为它是一个常量,所以不允许修改,即:*p = 'a'是不合法的。
- 对于char* p = a;因为它是一个变量,所以允许修改,即:*p = 'a'是合法的。