美文网首页
c语言多维数组

c语言多维数组

作者: ag4kd | 来源:发表于2019-10-12 16:51 被阅读0次

由于简书不能很好支持 LaTeX,需要原文档的可以加我QQ 596928539.

或者只下载PDF版 链接:https://pan.baidu.com/s/1gqlTht0VaOaa7p9b9seHNw 密码:y7pg

遍历数组

image.png
//
// Created by binny on 2019/9/20.
//
#include <stdio.h>

int main() {
    int array1[3] = {1, 2, 3};
    int s1 = sizeof(array1) / sizeof(array1[0]);
    for (int l = 0; l < s1; ++l) {
        printf("array1[%d]= %d\n", l, array1[l]);
    }
    printf("通过指针遍历一维数组\n");
    int (*pInt1) = array1;
    for (int m = 0; m < s1; ++m) {
        printf("pInt1[%d]= %d\n", m, *pInt1++);
    }

    int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int s2 = sizeof(array2) / sizeof(array2[0][0]);
    printf("array[3][3]的大小 = %d\n\n", s2);
    printf("通过下标遍历数组\n");
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("array[%d][%d] = %d\n", i, j, array2[i][j]);
        }

    }
    printf("通过指针遍历二维数组\n");
    int (*pInt2)[3] = array2;


    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("pInt[%d][%d] = %d\n", i, j, *(*(pInt2 + i) + j));
        }
    }

    int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
                           28, 29};
    int (*pInt3)[3][3] = array3;

    int s3 = sizeof(array3) / sizeof(array3[0][0][0]);
    printf("array3[3][3][3]的大小 = %d\n\n", s3);

    printf("通过指针遍历三维数组\n");

    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 3; ++k) {
                printf("pInt[%d][%d][%d] = %d\n", i, j, k, *(*(*(pInt3 + i) + j) + k));
            }
        }
    }
    return 0;
}

一位数组

int array1[3] = {1, 2, 3};
int (*p1) = array1;

取出一维数组的元素,解指针:*p_1;

遍历:*p_1++

int main() {
    /*一维数组*/
    int array1[3] = {1, 2, 3};
    int s1 = sizeof(array1) / sizeof(array1[0]);
    for (int l = 0; l < s1; ++l) {
        printf("array1[%d]= %d\n", l, array1[l]);
    }
    printf("通过指针遍历一维数组\n");
    int (*pInt1) = array1;
    printf("pInt1= %d\n", *pInt1);
    for (int m = 0; m < s1; ++m) {
        printf("下标法 --- array1[%d]= %d\n", m, array1[m]);
        printf("指针法 --- pInt1[%d]= %d\n", m, *pInt1++);
    }

    return 0;
}

二维数组

int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*p2)[3] = array2;

取出二维数组的一个元素元素,解指针:*p_2;

遍历:*p_2++:一般化,*p_2+i,(其中,i=0,1,2,3……)

这是一个二维数组的一个元素,即一个一维数组,相当于p_1

取出一维数组的元素,解指针:*(*p_2+i);

遍历:*(*p_2+i)++:一般化,*(*p_2+i)+j,(其中,i,j=0,1,2,3……)

int main() {
    /*二维数组*/
    int array2[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    int (*pInt2)[3] = array2;

    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("下标法 --- pInt[%d][%d] = %d\n", i, j, array2[i][j]);
            printf("指针法 --- array2[%d][%d] = %d\n", i, j, *(*(pInt2 + i) + j));
        }
    }
    return 0;
}

三维数组

int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
                       28, 29};
int (*pInt3)[3][3] = array3;

同理:

遍历:*(*(*p_2++)++)++:一般化*(*(*p_2+i)+j)+k

int main() {
    /*三维数组*/
    int array3[3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
                           28, 29};
    int (*pInt3)[3][3] = array3;

    int s3 = sizeof(array3) / sizeof(array3[0][0][0]);
    printf("array3[3][3][3]的大小 = %d\n\n", s3);

    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 3; ++k) {
                printf("下标法 --- array3[%d][%d][%d] = %d\n", i, j, k, array3[i][j][k]);
                printf("指针法 --- pInt[%d][%d][%d] = %d\n", i, j, k, *(*(*(pInt3 + i) + j) + k));
            }
        }
    }
    return 0;
}

<div STYLE="page-break-after: always;"></div>

归纳

多维数组指针的定义

image.png

指针遍历多维数组

一维数组:步进,解指针。

二维数组:步进,解指针;步进,解指针。

三维数组:步进,解指针;步进,解指针;步进,解指针。

n 维数组:步进,解指针;步进,解指针;步进,解指针……步进,解指针。

步进,解指针为单位,需要进行n次。

<font color=red>可以观察出规律:解指针和步进单位关于数组指针p呈结构对称</font>。通过此规律不难写出 n 维数组通过指针遍历的通式,以四维为例:

四维数组的指针:*(*(*(*p+i)+j)+k)+l


int main() {
    /*四维数组*/
    int array4[2][2][2][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17};
    int (*pInt4)[2][2][2] = array4;

    int s3 = sizeof(array4) / sizeof(array4[0][0][0][0]);
    printf("大小 = %d\n\n", s3);

    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 2; ++j) {
            for (int k = 0; k < 2; ++k) {
                for (int l = 0; l < 2; ++l) {
                    printf("下标法 --- array4[%d][%d][%d][%d] = %d\n", i, j, k,l, (array4[i][j][k][l]));
                    printf("指针法 --- pInt4[%d][%d][%d][%d] = %d\n", i, j, k,l, *(*(*(*(pInt4 + i) + j) + k) + l));
                }
            }
        }
    }
    return 0;
}

计算数组的长度

使用关键字 sizeof

因为数组是同一类型元素的集合:所以知道数组所占的字节数,除以一个元素的字节数,就可以的鳌数组中元素的个数;利用首元素即可,或者类型。

数组长度 = sizeof(数组名)/sizeof(数组首元素)

数组长度 = sizeof(数组名)/sizeof(数组类型)

image.png

性参数组的长度,需要另一个参数来传递:void test(int array[],int size),必须显示传递长度。

为什么不能?为什么能?

1、从一个字符串常量里查找出某一个字符

一级指针为什么不能?

image.png

是真的不能实现吗?先不回答这个问题。先说一下为什么不能,再给出一个一级指针的实现方案。

/**
 * 一级指针
 * @param src
 * @param ch
 * @param ret
 * @return
 */
int find2(const char *src, char ch, char *ret) {
    char *index = (char *) src;
    while (*index) {
        if (*index == ch) {
            ret = index;//①这个地方导致不行
            return 1;
        }
        index++;
    }
    return 0;
}
image.png

绿色箭头代表调用的时候ret的指向,红色箭头代表函数执行过程中满足条件的指向;find2执行完,返回到主函数,没有对实参的产生影响。就像是见异而迁

src是被声明为const char *,其所指的内存对src来说是只读的。

retchar *,可以通过ret改变其所指内存的值。

修改后的一级指针

功能函数:对其所指向的内存数据进行写操作,前提是其所指定内存是确定的。

/**
 * 一级指针
 * @param sr
 * @param ch
 * @param ret
 * @return
 */
int find2(const char *sr, char ch, char *ret) {
    char *index = (char *) sr;
    while (*index) {
        if (*index == ch) {
            *ret = *index;//这样改就可以了
            return 1;
        }
        index++;
    }
    return 0;
}

简化后的find2代码

/**
 * 一级指针
 * @param sr 原始字符串
 * @param ch 待查找字符串
 * @param c
 * @return
 */
int findCharByP(const char *sr, char ch, char *ret) {
    while (*sr) {
        if (*sr == ch) {
            *ret = *sr;
            return 1;
        }
        sr++;
    }
    return 0;
}

主函数:

int main() {
    printf("利用二级指针去查找 \n");
    char str[] = "hello china";
    int (*find)(const char *, char, char *) = find2;
    char c;
    if (find(str, 'a', &c)) {
        printf("找到了 %c \n", c);
    } else {
        printf("没找到 \n");
    }
    return 0;
}
image.png

二级指针

这种方式的实现原理,跟上面修改后的代码是一样的。

image.png

<div STYLE="page-break-after: always;"></div>

/**
 * 利用二级指针
 * 
 * @param src 指向源字符串
 * @param ch 要查找的字符串
 * @param dist 保存查到的字符串的首地址的指针
 * @return 是否查找成功
 */
int findCharByPP(const char *src, char ch, const char **dist) {
    while (*src) {
        if (*src == ch) {
            *dist = src;
            return 1;
        }
        src++;
    }
    return 0;
}

方式一:对指针变量取地址

int main() {
    printf("利用二级指针去查找 \n");
    char str[] = "hello china";
    char *pString = NULL;
    int (*find)(const char *, char, const char **) = findCharByPP;

    if (find(str, 'c', (const char **) &pString)) {
        printf("找到了 %c \n", *pString);
    } else {
        printf("没找到 \n");
    }
    return 0;
}
image.png

在函数内部声明了一个指针变量,指向一个字符型变量的pString,同时声明了一个函数指针find

src移动到需要找的那个字符时,就会将该字符的地址0x7ffedfe8f442存到pString所在的内存中,这个时候,也就是这个指针变量指向的地址就是那个字符串的地址。

方式二:二级指针变量

int main() {
    printf("利用二级指针去查找 \n");
    char str[] = "hello china";
    int size = sizeof(str) / sizeof(char);
    for (int i = 0; i < size; ++i) {
        printf("str[%d]=%c =%p\n", i, str[i], &str[i]);
    }
    const char **pString = (const char **) (char **) malloc(1);
    int (*find)(const char *, char, const char **) = findCharByPP;

    if (find(str, 'c', (const char **) pString)) {
        printf("找到了 %c \n", **pString);
    } else {
        printf("没找到 \n");
    }
    free(pString);
    return 0;
}

归根到底,就是你想改变那块内存的值,首先先找到那块内存,然后重新写入数据,赋予新值。而不是修改形参的指向。

指针使用补充

1、一个指针变量可以指向计算机中的任何一块内存,不管该内存有没有被分配,也不管该内存有没有使用权限,只要把地址给它,它就可以指向。

2、未初始化的局部变量的值是不确定的,C语言并没有对此作出规定,不同的编译器有不同的实现,我曾警告大家不要直接使用未初始化的局部变量。上面的代码中,str 就是一个未初始化的局部变量,它的值是不确定的,究竟指向哪块内存也是未知的,大多数情况下这块内存没有被分配或者没有读写权限。

对没有初始化的指针赋值为 NULL:char *str = NULL;

其实,NULL 是在stdio.h中定义的一个宏为:

#define NULL ((void *)0)

(void *)0表示把数值 0 强制转换为void *类型,最外层的( )把宏定义的内容括起来,防止发生歧义。从整体上来看,NULL 指向了地址为 0 的内存,而不是前面说的不指向任何数据。

在进程的虚拟地址空间中,最低地址处有一段内存区域被称为保留区,这个区域不存储有效数据,也不能被用户程序访问,将 NULL 指向这块区域很容易检测到违规指针。

3、在大多数操作系统中,极小的地址通常不保存数据,也不允许程序访问,NULL 可以指向这段地址区间中的任何一个地址。

注意,C语言没有规定 NULL 的指向,只是大部分标准库约定成俗地将 NULL 指向 0,所以不要将 NULL 和 0 等同起来,例如下面的写法是不专业的:

int *p = 0;

而应该写为:

int *p = NULL;

注意 NULL 和 NUL 的区别:NULL 表示空指针,是一个宏定义,可以在代码中直接使用。而 NUL 表示字符串的结束标志 '\0',它是ASCII码表中的第 0 个字符。NUL 没有在C语言中定义,仅仅是对 '\0' 的称呼,不能在代码中直接使用。

实现一个函数回调

int add(int a, int b) {
    return a + b;
}

int mul(int a, int b) {
    return a * b;
}

int minus(int a, int b) {
    return a - b;
}

int dev(int a, int b) {
    return a - b;
}

int test2(int a, int b, int(*callback)(int, int)) {
    return callback(a, b);
}

int main() {
    int a = 100, b = 20;
    printf("%d + %d = %d\n",a,b,test2(a,b,add));
    printf("%d - %d = %d\n",a,b,test2(a,b,minus));
    printf("%d * %d = %d\n",a,b,test2(a,b,mul));
    printf("%d / %d = %d\n",a,b,test2(a,b,dev));
    return 0;
}

相关文章

  • 0基础学习C语言第七章:数组(2)

    C语言 多维数组 C 语言支持多维数组。多维数组声明的一般形式如下: type name[size1][size2...

  • [进阶]C++:数组形参

    使用表准库规范 传递多维数组 C++语言中实际上没有真正的多维数组,多为的多维数组实际上是数组的数组 等价定义 m...

  • [基础]C++:名字的作用域

    使用表准库规范 传递多维数组 C++语言中实际上没有真正的多维数组,多为的多维数组实际上是数组的数组 等价定义 m...

  • c语言多维数组

    由于简书不能很好支持 LaTeX,需要原文档的可以加我QQ 596928539. 或者只下载PDF版 链接:ht...

  • C语言10 多维数组

    C语言10 多维数组 多维数组的定义 比如一个班有5个组,每个组有9个人int arr[45] 或者 intarr...

  • 静心沉淀,厚积薄发

    今天主要是看来c语言中关于数组的知识,数组按照空间大小的不同,数组分为一维数组,二维数组,多维数组,按照元素种...

  • Shell---数组

    bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组...

  • shell变量的数据类型之数组

    bash支持一维数组(不支持多维数组),并且没有限定数组的大小。 类似于 C 语言,数组元素的下标由 0 开始编号...

  • 数组和广义表

    数组的定义和运算 C语言支持一维数组和多维数组。如果一个数组的所有元素都不是数组,那么该数组称为一维数组。 在ja...

  • 10.31学习总结

    今天代课老师讲了c#中的数组。 一维数组:声明数组,分配空间,元素赋值,引用数组元素。 多维数组(声明多维数组时,...

网友评论

      本文标题:c语言多维数组

      本文链接:https://www.haomeiwen.com/subject/hctcmctx.html