目录
数组名
一般性理解
下标引用与间接访问
例外
一维数组
声明与初始化
下标引用
内存分配
长度计算
二维数组
内存分配
长度计算
声明与初始化
数组指针
引入
数组指针
一级指针
引入
一级指针
章尾问题
数组名
一般性理解
数组名是一个指向(某种)类型的指针常量,指向的是内存中数组的起始地址(也就是数组的第一个元素)。
/*数据类型*/
int a; /*标量:单一的值*/ /*整型*/
int b[10]; /*数组:值的集合*/ /*指向整型的指针常量*/
printf("%p\n", b); //0000005B459BFA88
printf("%p\n", &b[0]); //0000005B459BFA88
/*结论:数组名是一个指向(某种)类型的指针常量,指向的是内存中数组的起始地址*/
下标引用与间接访问
C的下标引用和间接访问表达式是一样的,在有下标引用的地方可以使用间接访问来代替,反之同理。
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* ap = arr + 2;
int* bp = arr;
/*下标引用与间接访问完全相等*/
printf("%d %d\n", arr[2],*(arr+2));
printf("%d %d\n", arr[3], bp[3]);
printf("%d %d\n", arr[1], ap[-1]);
应用:分别使用指针和数组,打印一个一维数组的值和地址。
#include<stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* p = arr;
int i;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
printf("%d ", p[i]);
printf("%d ", *(arr + i));
printf("%d ", *(p + i));
printf("地址:");
printf("%p", &arr[i]);
printf("%p", p+i);
putchar('\n');
}
return 0;
}
例外
当数组名作为sizeof操作符的操作数时,sizeof返回整个数组的长度,而非指针的长度。
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* bp = arr;
printf("%ud\n", sizeof(arr)); //40
printf("%ud\n", sizeof(bp)); //8
一维数组
声明与初始化
/*数组的声明*/
int arr[5];
/*数组的初始化*/
int brr[5] = { 1,2,3,4,5 };
int crr[5] = { 1,2 }; //部分初始化
int drr[] = { 1,2,3,4,5 }; //未给出数组长度
下标引用
部分初始化的数组:未初始化部分默认为0。
数组边界(下标):0 ~ N -1。( N = 数组的元素个数 )
int i = 0;
for (i = 0; i < sizeof(arr) / sizeof(int); i++)
{
printf("%d ", brr[i]);
printf("%d ", crr[i]);
printf("%d ", drr[i]);
putchar('\n');
}
注意:数组只能逐个引用,不能一次引用整个数组。 (深刻理解数组名是指针常量)
内存分配
数组的元素在内存中是连续存储的。
int arr[5] = { 1,2,3,4,5 };
int i;
printf("%ud\n", sizeof(int));
for (i = 0; i < sizeof(arr) / sizeof(int); i++)
{
printf("%d %p\n", arr[i],&arr[i]);
}
// 4
// 1 000000880813F728
// 2 000000880813F72C
// 3 000000880813F730
// 4 000000880813F734
// 5 000000880813F738
长度计算
数组元素的个数 = sizeof(数组名)/ sizeof(数组类型);
二维数组
内存分配
以arr[N][M]为例,可以把二维数组看作:N个含有M个元素的一维数组。
一维数组名是指向第一个元素的指针,那么二维数组名就是指向第一个元素(包含M个整形元素的一维数组)的指针。
在理解了内存分配的基础上,很容易就可以理解一个概念:
比如上图中的 arr[0] ,它是一个一维数组名,其中包含了3个元素。
验证:
int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
printf("%d %d", sizeof(arr[0]), sizeof(int));
/*输出结果:12 4*/
长度计算
行数:int N = sizeof(arr) / sizeof(arr[0]);
列数:int M = sizeof(arr[0]) / sizeof(int);
声明与初始化
int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };/*完全初始化*/
int brr[][3] = { {1,2,3},{4,5,6},{7,8,9} };
int crr[3][3] = { {1},{4,5},{7,8,9} }; /*部分初始化*/
数组指针
引入
结论:一维数组名是指向第一个元素的指针,那么二维数组名就是指向第一个元素(包含M个整形元素的一维数组)的指针。
验证: 数组名每 + 1 ,移动 12 个字节(也就是一个 包含了3 个整型元素的一维数组)。
#include<stdio.h>
int main()
{
int arr[3][3] = { {1,2,3},{3,4,5},{5,6,7} };
printf("%p\n", arr);
printf("%p\n", arr+1);
printf("%p\n", arr+2);
return 0;
}
//输出结果:
//00000018406FF548
//00000018406FF554
//00000018406FF560
数组指针
名词解释:数组指针、行指针、二级指针
根据以上结论:
int arr[3] = { 1,2,3 };
int* p = arr;//right:因为arr是一个指向int类型的指针。
int brr[3][3] ={{0,1,2},{3,4,5},{6,7,8}};
int* q = brr;//error:因为brr不是一个指向int类型的指针。
那么我们应该怎么样声明一个指向整型数组的指针呢?
语法:<数据类型> (*指针)[M] = 数组名;
示例: q是一个指向拥有3个整型元素的数组的指针。
int brr[3][3] ={{0,1,2},{3,4,5},{6,7,8}};
int(*q)[3] = brr;
验证:或者你也可以理解为指针+1,就移动M个数据。(因为二维数组也是连续存储的)
#include<stdio.h>
int main()
{
int arr[3][3] = { {1,2,3},{3,4,5},{5,6,7} };
int(*p)[3] = arr;//[] : 表达指针加一,移动几个数据
printf("%p %p\n", arr,p);
printf("%p %p\n", arr+1,p+1);
printf("%p %p\n", arr+2,p+2);
return 0;
}
//输出结果:
//000000D7C597F998 000000D7C597F998
//000000D7C597F9A4 000000D7C597F9A4
//000000D7C597F9B0 000000D7C597F9B0
应用: 使用二级指针遍历二维数组。
#include<stdio.h>
int main()
{
int arr[3][3] = { {1,2,3},{3,4,5},{5,6,7} };
int(*p)[3] = arr;
int i,j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d %d %d ", p[i][j], *(p[i] + j), *(*(p + i) + j));
}
putchar('\n');
}
return 0;
}
//输出结果:
//1 1 1 2 2 2 3 3 3
//3 3 3 4 4 4 5 5 5
//5 5 5 6 6 6 7 7 7
一级指针
引入
比如上图中的 arr[0] ,它是一个一维数组名,其中包含了3个元素。
一级指针
当你需要逐个访问整型元素的时候,而不是逐行在数组中移动时,该怎么做呢?
答案:
int* p = arr[0];
int* p = &arr[0][0];
应用:使用一级指针遍历二维数组。
#include<stdio.h>
int main()
{
int arr[3][2] = { {1,2},{3,4},{5,6} };
int* p = &arr[0][0];
int i;
for (i = 0; i < 6; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
//输出结果:1 2 3 4 5 6
章尾问题
指针、数组和地址之间的关系是什么?数组与指针的区别是什么?