《C和指针》读书笔记(第八章 数组)(下)

简介: 《C和指针》读书笔记(第八章 数组)

2. 多维数组

多维数组是二维以及二维以上的数组,其中最常用的是二维数组。需要注意的是多维数组的元素存储顺序。以及元素的访问方式等。

当出现多维数组时,若再采用指针、或者指针和下标结合的方式去访问数组元素,将是稍微有点难度的问题。

2.1 存储顺序

在C语言中,多维数组的元素存储顺序按照最右边的下标率先变化的原则,称为行主序

#include <stdio.h>
#include <stdlib.h>
#define ROW 3
#define COL 8
int main()
{
  int matrix[ROW][COL];
  int *p = &matrix[0][0];
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      matrix[i][j] = i * ROW + j;
    }
  }
  //打印输出
  printf("第一个值是%d \n", *p);
  printf("第二个值是%d \n", *++p);
  printf("第三个值是%d \n", *++p);
  system("pause");
  return 0;
}

打印输出:

从上面的例子可以看出,当指针增长的时候,指向的是数组按照最右侧率先变化的顺序的元素。而当一行扫描结束的时候,会自动指向下一行,继续访问。

2.2 数组名

一维数组名的值是一个指针常量,指向一个元素,而多维数组第一维的元素是另一个数组。例如下面的声明:

int matrix[3][10];

可以看作是一个一维数组,包含了3个元素,每个元素是包含10个整形元素的数组。

或者可以看本文后续的章节,慢慢体会就自然会明白。

2.3 下标

如果要标识一个多维数组的某个元素,必须按照与数组声明时相同的顺序为每一维都提供一个下标,而且都单独位于一对方括号内。

#include <stdio.h>
#include <stdlib.h>
#define ROW 8
#define COL 3
int main()
{
  int matrix[ROW][COL];
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      matrix[i][j] = i * COL + j;
    }
  }
  //打印输出
  printf("第一个值是:%d \n", **matrix);
  printf("第二个值是:%d \n", *(*(matrix + 1)));
  printf("第三个值是:%d \n", *(*(matrix) + 2));
  printf("第四个值是:%d \n", *(*(matrix + 1) + 2));
  system("pause");
  return 0;
}

上面这个例子可能稍微有点难,按照指针移动的方向,仔细琢磨琢磨就可以想清楚。

2.4 指向数组的指针

指向多维(二维)数组的指针,应当如何定义呢?

int matrix[ROW][COL] = {{0,1,2},{3,4,5}};
  int(*p)[COL] = matrix;

我们定义了一个指针p,指向了一个拥有COL个元素的数组。当把p与一个整数值相加时,该整数值首先根据整形值的长度进行调整,然后再执行加法。参考下面的例子。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROW 2
#define COL 3
int main()
{
  //定义二维数组
  int matrix[ROW][COL] = {{0,1,2},{3,4,5}};
  //定义指向数组的指针
  int(*p)[COL] = matrix;
  //打印输出
  printf("(*(*p))的值为:%d \n",(*(*p)));
  printf("(*(*p + 1))的值为:%d \n", *(*(p + 1)));
  printf("(*(*p) + 1的值为:%d \n", (*(*p) + 1));
  printf("(*(*p + 1) + 1)的值为:%d \n", *(*(p + 1) + 1));
  system("pause");
  return 0;
}

打印输出:

可以看到,直接利用p指针执行间接访问,肯定访问到的是第0行的第0个元素((*(*p)))。而在对p直接进行加1操作的时候,移动的是一个一维数组,然后再间接访问,所以得到的是数组第1行的第0个元素,也就是*(*(p + 1))这样的表达式;但如果间接访问一次之后在加1,则首先访问到的是二维数组的第0行,再加1自然就是第0行第一个元素,也就是上述(*(*p) + 1)表达式;最后一个表达式(*(*(p + 1) + 1))自然不言而喻了。

2.5 作为函数参数的多维数组

多维(二维)数组作为函数参数的时候,函数声明也与一维数组有所不同。有两种声明方式:

方式1:

void func1(int(*mat)[5])

方式2:

void func2(int mat[][5])

这两种声明方式,在效果上是等同的,两种都可以。从以下的程序中就可以证明这一点:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROW 3
#define COL 5
//声明方式1
void func1(int(*mat)[5])
{
  int add = 1;
  printf("在函数func1中\n");
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      mat[i][j] += 10;
      printf("数组的第%d个元素是:%d\n", add, mat[i][j]);
      add++;
    }
  }
}
//声明方式2
void func2(int mat[][5])
{
  int add = 1;
  printf("在函数func2中\n");
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      mat[i][j] += 10;
      printf("数组的第%d个元素是:%d\n", add, mat[i][j]);
      add++;
    }
  }
}
int main()
{
  //定义二维数组
  int matrix1[ROW][COL];
  int matrix2[ROW][COL];
  //定义累加变量
  int add = 1;
  //数组初始化
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      matrix1[i][j] = i * COL + j;
      matrix2[i][j] = i * COL + j;
      printf("数组1的第%d个元素是:%d \t", add, matrix1[i][j]);
      printf("数组2的第%d个元素是:%d \n", add, matrix1[i][j]);
      add++;
    }
  }
  printf("-----------------");
  //函数调用
  func1(matrix1);
  printf("-----------------");
  func2(matrix2);
  system("pause");
  return 0;
}

打印输出:

上述例子,定义了2个3*5的二维数组,进行相同的初始化,然后分别传到两个函数进行处理,这两个函数仅仅是形参的声明方式不一样。两个函数对原来数组的每个元素进行加10处理,得到了相同的结果。

所以,这两种声明方式在效果上是等同的。

2.6 初始化

多维(二维)数组的初始化有两种常见的形式。

  1. 一种是直接给每个元素赋储值
  2. 一种是用花括号隔开每个维度,并赋值

两种方式都是正确的,只是第二种有方式有两个好处:

  1. 利于阅读
  2. 方便初始化,每个子初始列表都可以省略尾部的几个值(不完整的初始化列表)。

参考下面的程序:

#include <stdio.h>
#include <stdlib.h>
#define ROW 2
#define COL 3
int main()
{
  //初始化形式1
  int matrix1[ROW][COL] = {0,1,2,3,4,5};
  //初始化形式2
  int matrix2[ROW][COL] = { {0,1,2},{3,4,5}};
  int add = 1;
  //打印输出
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      printf("matrix1的第%d个值为%d \t", add, matrix1[i][j]);
      printf("matrix2的第%d个值为%d \n", add, matrix2[i][j]);
      add++; 
    }
  }
  system("pause");
  return 0;
}

打印输出:

从上面的例子可以看出,这两种形式,从实现效果上来说,是一致的。

2.7 数组长度自动计算

在多维数组中,只有第1维才能根据初始化列表缺省地提供。剩下的几个维必须显式地写出,这样编译器就能推断出每个子数组维数的长度。例如:

#include <stdio.h>
#include <stdlib.h>
#define ROW 3
#define COL 5
int main()
{
  int matrix3[][5] = { {0,1,2},{3,4,5},{6,7,8}};
  int add = 1;
  //打印输出
  for (int i = 0; i < ROW; i++)
  {
    for (int j = 0; j < COL; j++)
    {
      printf("matrix1的第%d个值为%d \t", add, matrix3[i][j]);
      add++; 
    }
    printf("\n");
  }
  system("pause");
  return 0;
}

打印输出:

所以此时即使不写第1个维度的值,编译器在运行的时候,也可以根据初始化的值,以及花括号自动推断出来该维度的值。

3. 指针数组

指针数组很好理解,就是一个数组中的元素是指针,至于该指针指向什么样的数据,是由用户自己定义的。

比如下面这个例子,用指针数组存储了指向字符串(更严谨地说是字符数组)的指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROW 3
#define COL 5
char const *keyword[] = {"do","for"};
int main()
{
  int add = 1;
  //打印输出
  char const desired_word[] = "do";
  char const **p;
  for (p = keyword; p < keyword + 2; p++)
  {
    if (strcmp(desired_word, *p) == 0)
    {
      printf("YES");
      system("pause");
      return 0;
    }
  }
  printf("NO");
  system("pause");
  return 0;
}

打印输出如下:

上述的例子中,用数组存储了若干个字符数组的指针数组。然后实现了多个字符串的匹配功能。

或者用二维数组也可以实现,但是需要提前知道最长字符串的大小。

4. 总结

数组与指针的关系,不可能一两句话说清楚,需要在具体开发中慢慢体会,理解。

数组的元素可以通过下标指针两种方式进行访问,而指针往往更加高效。

指针数组在开发中也会比较常用,而数组元素也不仅仅只会指向字符串(字符数组),也有可能指向结构体变量等数据类型。

----------------------------------------------------------------end----------------------------------------------------------------

相关文章
|
20天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
31 3
|
20天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
30 2
|
28天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
32 1
|
1月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
1月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
19 0
|
1月前
|
编译器 C语言
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
【C语言】指针篇-深入探索数组名和指针数组- 必读指南(2/5)
|
3月前
|
搜索推荐 C语言
指针与数组
指针与数组
59 9
下一篇
无影云桌面