c语言进阶——多维数组本质及指针数组的应用分析

多维数组的本质

一维数组int a[10]在做函数参数时会退化为指针int *a,但是二维指针做函数参数时却不能写成二级指针,那么二维数组的本质到底是什么,可以通过代码来验证。

    int a[3][5], i=0, j=0;//定义一个3*5的二维数组 
    int tmp = 1;

    for (i=0; i<3; i++)
    {
        for (j=0; j<5; j++)
        {
            a[i][j] = tmp++;
        }
    }
    printf("a %d , a+1:%d ", a, a+1); //输出数组首元素地址和步长 
    printf("&a %d , &a+1:%d ", &a, &a+1); //输出数组地址和步长
    //输出结果
    //数组首元素地址与数组地址相同
    //a的步长为20个字节,&a的步长为15*4 = 60个字节 

得出结论:
多维数组名的本质是数组指针,步长为多维数组一维的长度
这里写图片描述

多维数组做函数参数的技术推演

1、C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参)

int fun(char a[20])
{
   printf("%d",sizeof(a));
}
//输出结果为4字节(32位)

原因1:高效
原因2:C语言处理a[n]的时候,它没有办法知道n是几,数组发生了退化,变为了指针。

2、二维数组的退化问题
二维数组可以看做一维数组的数组,因此二维数组中的第一维可以省略。
void f(int a[5]) ====》void f(int a[]); ===》 void f(int* a);
void g(int a[3][3])====》 void g(int a[][3]); ====》 void g(int (*a)[3]);

3、数组与指针的等价关系

数组参数                        等效的指针参数

一维数组 char a[30]             指针 char*
指针数组 char *a[30]            指针的指针 char **a
二维数组 char a[10][30]         数组的指针 char(*a)[30]

指针数组的应用场景

在实际的应用开发中,指针数组主要有菜单和命令行两种应用场景

1、菜单应用

//求关键字在表中的位置
//一个入口 多个出口
int searcheKeyTable(const char* table[], const int size, const char* key, int *pos)
{
    int rv = 0;
    int i = 0;
    int inum = 0;
    if (table==NULL || key==NULL || pos==NULL)
    {
        rv = -1;
        printf("func searcheKeyTable:%d", rv);
        return rv;
    }

    //间接的证明  数组做函数参数的回退  
    //这里inum的值为1,因为数组会发生回退,编译器并不知道n是多少,当做char **table来看,属于指针类型四个字节 
    inum = (sizeof(table)/sizeof(*table));

    for(i=0; i<size; i++)
    {
        if( strcmp(key, table[i]) == 0 )
        {
            *pos = i;
            //break;
            return rv;  
        }
    }

    //没有找到返回-1
    if (i == size)
    {
        *pos = -1;
    }
    return rv;
}

#define DIM(a) (sizeof(a)/sizeof(*a))  

int main()
{
    int inum = 0;
    int pos = 0;
    int a[10];
    int i = 0;
    //指针数组
    char*   c_keyword[] = {
        "while", 
        "case",
        "static",
        "do"
    };

    searcheKeyTable( c_keyword, DIM(c_keyword),"do", &pos);
    //宏定义部分展开为 sizeof(c_keyword)/sizeof(*c_keyword),计算的是指针数组的长度 
    //*c_keyword等价于c_keyword[0]  表达式为4*4/4 = 4 
    printf("pos:%d\n", pos);
    searcheKeyTable( c_keyword, DIM(c_keyword), "static", &pos);
    printf("pos:%d\n", pos);

    system("pause");
    return ;
}

2、main函数命令行应用
我们经常发现在main函数中会存在int argc, char* argv[]这样的函数参数,那么这些函数参数时从哪里传递并分配内存的吗,答案是操作系统。如果我们使用命令行工具来运行程序,就可以传入这些参数。

int main(int argc, char* argv[], char**env)
{
    int i = 0;

    printf("******************* Begin argv *******************\n");
    for(i=0; i<argc; i++)
    {
        printf("%s\n", argv[i]);
    }
    printf("******************* End argv *******************\n");


    printf("******************* Begin env *******************\n");

    for(i=0; env[i]!=NULL; i++)  //为什么可以正常运行?
    {
        printf("%s\n", env[i]);
    }

    printf("******************* End env*******************\n");

}

这里并没有传入env的个数,但是循环依然不会发生中断,原因是main函数会自动在字符数组后面自动加上结束标志(NULL 0 ‘\0’)

猜你喜欢

转载自blog.csdn.net/daidaihema/article/details/80655427