什么是二维数组?
E.g. int array[2][3]={{1,2,3},{4,5,6}}
示例数组是一个初始化的 二维数组。定义一个 二维数组和一个普通的一维数组类似,同样包含三要素: 数据类型, 数组名,数组大小。大部分人会说两者的区别在于第三要素 —— 数组大小方面多了一个方括号“[]”,使得二维数组能自由的定义 行数 与 列数 的数量多少。但在笔者看来,矩阵(Matrix)—— 这一数学概念可以完美的阐述两者的区别:
一维数组(int array[]) —— A 1 × n A_{1 \times n} A1×n
二维数组(int array[][]) —— A m × n A_{m \times n} Am×n
二维数组和一维数组的表述形式虽然不同,但两个数组在计算机内存里的存储方式是一致的,即 所有数据都是按顺序存储在连续的内存空间内。
二维数组的使用有诸多细节,这里博主再补充一点:参数传递。运用二维数组进行函数传递参数时 行数可以不用填写,但列数必须填写。
演示代码:array_double_print.c
/* array_double_print.c */
#include <stdio.h>
void printArrayDouble(int array[][3])
{
int i, j;
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++){
printf("%d ", array[i][j]);
}
putchar('\n');
}
}
int main()
{
int array[2][3] = {{1,2,3}, {4,5,6}};
printArrayDouble(array);
return 0;
}
点击此处,阅读笔者关于“一维数组”的博文。
二维数组的取值技巧总结
演示代码:value_array_double.c
/* value_array_double.c */
#include <stdio.h>
int main()
{
int a[2][3] = {{1,2,3},{4,5,6}};
int i,j;
printf("a[2][3]: \n");
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++){
printf("%d ", a[i][j]);
}
putchar('\n');
}
putchar('\n');
printf("a[0][0] : %d\n", a[0][0]);
printf("*(*(a+0)) : %d\n", *(*(a+0)));
printf("**a : %d\n", **a);
printf("p of a[0][0]: %p\n", a);
printf("a[1][1] : %d\n", a[1][1]);
printf("*(a[1]+1) : %d\n", *(a[1]+1));
printf("*(*(a+1)+1)): %d\n", *(*(a+1)+1));
printf("p of a[1][2]: %p\n", *(a+2));
printf("a[1][0] : %d\n", a[1][0]);
printf("a[0]+1 : %d\n", *(*(a+1)));
printf("p of a[1][0]: %p\n", a+1);
return 0;
}
运行结果:
a[0][0] : 1
*(*(a+0)) : 1
**a : 1
p of a[0][0]: 0x7ffcbd065d40
a[1][1] : 5
*(a[1]+1) : 5
*(*(a+1)+1)): 5
p of a[1][2]: 0x7ffcbd065d58
a[1][0] : 4
a[0]+1 : 4
p of a[1][0]: 0x7ffcbd065d4c
二维数组取值形式与含义总结:
表现形式 | 含义 |
---|---|
a | 二维数组名,指向一维数组a[0],即第零行首地址。 |
a[0] , ∗ \ast ∗(a+0), ∗ \ast ∗a | 第零行第零列元素首地址。 |
a[0][0], ∗ \ast ∗( ∗ \ast ∗(a+0)), ∗ ∗ \ast\ast ∗∗a | 第零行第零列元素的数值。 |
a+1,&a[1] | 第一行首地址。 |
a[1], ∗ \ast ∗(a+1) | 第一行第零列元素a[1][0]元素的地址。 |
a[1]+2, ∗ \ast ∗(a+1)+2,&a[1][2] | 第一行第二列元素a[1][2]的地址。 |
∗ \ast ∗(a[1]+2), ∗ \ast ∗( ∗ \ast ∗(a+1)+2,a[1][2] | 第一行第二列元素a[1][2]的值。 |
∗ \ast ∗注意:二维数组元素的取值方式相比一维数组虽然略有繁琐,但以数学形式理解无非都是存储数据的矩阵而已。配合以上代码理解将事半功倍。
二维数组的遍历
|运用指针遍历二维数组
指针遍历的代码:double_array_pointer.c
/* double_array.c */
#include <stdio.h>
int main()
{
int array[2][3] = {{1,2,3},{4,5,6}};
int i, j;
int *p = NULL;
p = &array[0][0];
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
printf("%d ", *p++);
}
putchar('\n');
}
return 0;
}
|数组指针遍历二维数组
演示代码:arrayDouble_arrayPointer.c
/* arrayDouble_arrayPointer.c */
#include <stdio.h>
int main()
{
int i,j;
int array[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = NULL;
p = array;
printf("for循环遍历:\n");
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++){
printf("%d ", array[i][j]);
}
putchar('\n');
}
putchar('\n');
printf("数组指针遍历:\n");
for(i = 0; i < 2; i++){
for(j = 0; j < 3; j++){
//printf("%d ", p[i][j]);
printf("%d ", *(*(p+i)+j));
}
putchar('\n');
}
return 0;
}
∗ \ast ∗注:对于优先级混乱的读者,只需牢牢记住三个字符的优先级为 () > [] > ∗ \ast ∗,从大到小逐次递减。
二维数组的典型场景:两次(多次)抽取纸牌
问题:如何从一副扑克牌中随机抽取纸牌?如何避免两次(多次抽取时)抽到同一张牌呢?
分析:
- 随机抽取纸牌。使用 srand() 函数初始化随机数生成器,利用时间戳 time() 函数避免程序在运行时发同样的牌;
- 限定范围。使用 rand() 函数产生随机数,使用运算符 % 让 rand() 函数的返回值缩限制在 花色 与 等级 范围内;
- 以名为 in_hand() 的二维数组记录选择过的纸牌,避免在抽两次或者两次以上抽取时拿到同样的纸牌;
- 设置两个字符数组(一个用纸纸牌的等级,一个用于纸牌的花色),利用等级与花色对数组取下标。这两个数组在程序执行期间不会发生改变,将它们声明为 const 。
演示代码: deal.c
/* deal.c 2022.12.7 20:12 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h> /* C99 */
#define NUM_SUITS 4
#define NUM_RANKS 13
int main(){
bool in_hand[NUM_SUITS][NUM_RANKS] = {false};
int num_cards, rank, suit;
const char rank_code[] = {'2','3','4','5','6','7','8','9','t','j','q','k','a'};
//club(♣️),diamond(♦️),heart(♥️),spade(♠️).
const char suit_code[] = {'c','d','h','s'};
srand((unsigned)time(NULL));
printf("Enter number of cards in hand: ");
scanf("%d", &num_cards);
printf("Your hand:");
while(num_cards > 0){
suit = rand() % NUM_SUITS;
rank = rand() % NUM_RANKS;
if(!in_hand[suit][rank]){
in_hand[suit][rank] = true;
num_cards--;
printf(" %c%c", rank_code[rank], suit_code[suit]);
}
}
putchar('\n');
return 0;
}
运行结果:
拓展:函数指针数组
演示代码:fun_pointer_array.c
/* fun_poniter_array.c 2022.6.18 17:52 */
#include <stdio.h>
#include <stdlib.h>
int getMax(int data1, int data2)
{
return data1 > data2 ? data1:data2;
}
int getMin(int data1, int data2)
{
return data1 < data2 ? data1:data2;
}
int getSum(int data1, int data2)
{
return data1 + data2;
}
int main()
{
int a = 10, b = 20;
int ret;
int (*pfunc[3])(int /*data1*/, int /*data2*/) = {getMax, getMin, getSum};
for(int i=0; i<3; i++){
ret = (*pfunc[i])(a, b);
printf("ret = %d\n", ret);
}
return 0;
}
∗ \ast ∗注:该段代码来自陈立臣教师新版C语言教学视频。
参考资料
- 陈立臣老师新版C语言教学视频。
- 书籍《C语言程序设计现代方法》P119-122。
文章更新记录
- 文章整体框架搭好。「2022.11.2 11:27」
- “什么是二维数组?”一节完成。「2022.11.2 16:40」
- “二维数组的取值技巧总结”一节完成。「2022.11.3 10:30」
- 第一节“参数传递”补充完毕。「2022.11.3 16:23」
- “函数指针数组”一节更新完毕。「2022.11.3 16:56」
- 修改了三处:一、
value_array_double.c
代码错误改正,二、技巧总结列表一处错误改正,三、所有“ ∗ \ast ∗注意”文字加粗并且大小 size=1 。「2022.11.5. 9:46」 - 多增加新的一节内容“二维数组的典型场景:两次(多次)抽取纸牌”。「2022.12.7 21:01」
- 增加了一张二维数组元素存储的图片,《C程序语言设计现代方法》P119。「2022.12.7 21:07」