c语言多维数组

由于简书不能很好支持 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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352