在刷leetcode时有道关于二维数组的题目,由于工作中较少写这块代码,对部分基础理解不足,现将学习C程序设计语言一书中关于二维数组的内容进行总结。
书中以一个日期转换函数为被背景问题,针对闰年与非闰年,可以写出二维数组的形式:
static char daytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},{0,31,29,31,30,31,30,31,31,30,31,30,31}};
关于上述数组有几点需要解释:
首先将数组元素声明为char类型,是为了说明在char类型的变量中存放较小的非字符整数也是合法的。
第一个元素为0,是为了跟实际月份进行对应。
用static修饰数组则表示该数组无需修改,因为对应月份所含天数固定。

/*求某年某月某日对应的天数为一年中第几天*/
int day_of_year(int year,int month,int day)
{
    int i,leap;
    leap = (year%4==0 && year%100!=0) || year%400==0;
    
    for(i = 1;i<month;i++)
        day += daytab[leap][i];
    
    return day;
    
}

二维数组的一般形式daytab[i][j],数组元素按行存储,因此,当按存储顺序访问数组时,最右边的数组小标变化得最快。
实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的。数组与指针的关系是因为数组下标操作符[],比如元素a[2][1]相当于*(*(a+2)+1) 。
如果将二维数组作为参数传递给函数,那么函数的参数声明中必须指明数组的列数。数组的行数没有太大关系,函数调用时传递的是一个指针,它指向由行向量构成的一维数组,其中每个行向量是具有13个整型元素的一维数组,在该例子中,传递给函数的是一个指向很多对象的指针,其中每个对象是由13个整型元素构成的一维数组。因此,如果将数组daytab作为参数传递给函数f,那么f的声明应该写成如下形式:
f(int daytab[2][13]){……}
也可以写成
f(int daytab[][13]){……}
由于数组的行数无关紧要,所以该声明还可以写成:

f(int (*daytab)[13]){……}

这种声明形式表明参数是一个指针,它指向具有13个整型元素的一维数组。因为方括号[]的优先级高于*的优先级,所以上述声明中必须使用圆括号,如果去掉圆括号

int *daytab[13]

则变成声明一个数组,该数组有13个元素,其中每个元素都是一个指向整型对象的指针。一般来说,除数组的第一维可以不指定大小外,其余各维必须明确指定大小。
针对上面的理论知识可以参考下面的几个例子:

示例1

#include <stdio.h>
int main(int argc,char** argv)
{
    int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
    printf("%d\n",a[2][1]);
    printf("%d\n",a[1][4]);
    return 0;
}

总结:
上面的例子中的代码在xcode中有如下告警:

Array index 4 is past the end of the array (which contains 3 elements)

针对3x3数组,下标应该在0 1 2三者之间,由于这种分配是在栈上进行的,数组的特性能够保证所有元素的地址空间连续。

示例2

#include <stdio.h>
#include <stdlib.h>

void printarry(int **a,int m,int n)
{
    for(int i = 0;i < m;i++)
    {
        for(int j = 0;j < n;j++)
            printf("%d ",a[i][j]);
        printf("\n");
    }
}

int main(int argc,char** argv)
{
    int arr[3] ={1,2,3};
    int fun[3] ={4,5,6};
    int foo[3] ={7,8,9};
    int *a[3] = {arr,fun,foo};
    /*二维数组指针形式*/
    printarry(a,3,3);
    
    printf("%p\n",&a[0]);
    printf("%p\n",&a[1]);
    printf("%p\n",&a[2]);
    
    return 0;
}

上面例子中采用了指针数组这种形式,数组中的每个元素都是指针,由于数组arr和fun地址不连续,因此不能采用示例1中的“越界”下标形式进行访问,例如针对3x3二维数组,数组下标只能为0 1 2中的一个。

变长数组形式参数

由于C99支持变长数组,因此如下形式是正确的。
int sum_array(int n,int a[n])
需要注意的是函数参数中int n必须在int a[n]前面。