b97902HW 板


LINE

前言 之前在〈阵列简介 I〉我们说了阵列的基本意义与操作;在〈阵列简 介 II〉我们说了字串与字串的操作函式。在这一篇文,我们会再去 探讨阵列的其他用法诸如二维阵列、阵列大小运算子(sizeof)。 * 二(多)维阵列 先前我们介绍了一维阵列,你可以把一维阵列想像 成一条数线,在 0 的位置上放一个元素,在 1 的 位置上放另一个元素,...,在 n-1 的位置上放一 个元素。可是如果只有一维阵列,在处理一些资料 的时候就不那麽方便了。例如说,如果我们有一个 2列x3行 矩阵,我们要如何方便地表示它呢? ┌ ┐ │ 1 , 3 , 5 │ │ │ │ 3 , 5 , 7 │ └ ┘ * 阵列大小运算 有时候,为了方便,我们不会在宣告时写死阵列大 小,而是透过阵列的初始值列表(Initialization List) 让编译器去决定大小,如果我们的程式又突 然需要阵列的大小,我们该怎麽办呢? int array[] = {1, 2, 3, 4, 5}; int size = ????; * 阵列的传递 有时候我们的程式要对阵列做一些重复而锁碎的处 理我们会希望把这些程式码写成一个函式。可是我 们要怎麽样让我们的函式接送一个阵列呢?本文会 大略提到这一个问题,不过真得要详细地说要在学 指标之後才能完全的解释。 int array[7] = {1, 2, 3, 4, 5, 6, 7}; int count = count_if_greater(array, 7 /*size*/, 3 /*num*/); 二(多)维阵列 二维阵列的宣告 如果我们要宣告一个二维阵列,和一维阵列不同的部分就仅止於 [] 的数目。左边的 [] 代表的是我们要有多少列(Row),右边的 [] 代 表的是我们要有多少栏(Column),例如: int array[2][3]; 这样就会是一个二维阵列,他会有 2 列 3 栏。这一个阵列会有 6 个元素(Elememt)。这一个阵列的形状如下: 0 1 2 <= 栏索引 ┌─────┬─────┬─────┐ 0 │12 │893 │985 │ ├─────┼─────┼─────┤ 1 │187690 │346743 │-2222 │ └─────┴─────┴─────┘ ^= 列索引 当然,和一维阵列的时候一样,索引都是从 0 开始编号,所以栏索 引是 0 到 栏数-1 而列索引是 0 到 列数-1。 另外,值得一提得是在 C/C++ 之中,二维阵列是用「以列为主(Row Major)」对映到线性位址上。所谓的以列为主是指「同一列的会被放 在一起」,同一列的不同栏排完之後,才会开始排下一列。所以上面 的范例在记忆体的 Layout 大约如下: 位址(Address) ┌────┐ 0x00123344 │0C000000│ // array[0][0] ├────┤ 0x00123348 │7D030000│ // array[0][1] ├────┤ 0x00123352 │D9030000│ // array[0][2] ├────┤ 0x00123356 │2ADD0200│ // array[1][0] ├────┤ 0x00123360 │774A0500│ // array[1][1] ├────┤ 0x00123364 │52F7FFFF│ // array[1][2] └────┘ ※ 结果可能因测试环境而异,此例是在 Little Endian 与 32-bit Integer 环境之中的结果。 在正常的程式码之中,你如果没有特别注意你应该不会(也不用) 注意这一个细节,不过以後我们有几会说到指标的时候,会再提到。 初始化列表 我们可以在宣告阵列的时候,加上初始化列表(Initializer List), 让阵列元素被初始化为我们想要的数值,而非一大堆本来就在记忆体 中毫无意义的数值。我们和一维阵列的时候一样,不过这一次我们用 了二层的大括号{}int array1[2][3] = {{1, 2, 3}, {4, 5, 6}}; 0 1 2 <= 栏索引 ┌─────┬─────┬─────┐ 0 │1 │2 │3 │ ├─────┼─────┼─────┤ 1 │4 │5 │6 │ └─────┴─────┴─────┘ ^= 列索引 当然,和我们在〈阵列简介 I〉中所述的一样,最左边的维度可以省 略,让编译器帮我们算阵列的大小。例如下面的宣告,就可以帮我们 宣告一个 5 列 x 3 栏的二维阵列,每一个元素的值如下所示: int array2[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}}; 0 1 2 <= 栏索引 ┌─────┬─────┬─────┐ 0 │1 │2 │3 │ ├─────┼─────┼─────┤ 1 │4 │5 │6 │ ├─────┼─────┼─────┤ 2 │7 │8 │9 │ ├─────┼─────┼─────┤ 3 │10 │11 │12 │ ├─────┼─────┼─────┤ 4 │13 │14 │15 │ └─────┴─────┴─────┘ ^= 列索引 存取元素 当然,如果我们要存取特定元素,我们只要用二次下标运算子(Sub- script),就可以了。例如我们要写一个九九乘法表到阵列之中: int nines[10][10]; int i, j; for (i = 0; i < 10; ++i) { for (j = 0; j < 10; ++j) { nines[i][j] = i * j; } } 我们要印出来就可以用 for (i = 1; i < 10; ++i) { for (j = 1; j < 10; ++j) { printf("%d\t", nines[i][j]); } printf("\n"); } 结果就是: 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 如果我们要用 scanf 我们就可以用: scanf("%d", &(array[i][j])); 字串阵列 在学过二维阵列之後,我们可以讨论一下字串阵列,首先我们看一下: 字串(String)的本质是字元阵列,所以字串阵列可以说是字元的二维 阵列。所以我们可以用下面的方法来宣告一个字串阵列: char weeks[][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 注意:最右边的维度的大小必需可以容纳最长的字串,以上面的例子 来说,最长的字串是 Wednesday 一共需要 10 个字元。 for (i = 0; i < 7; ++i) { printf("%s\n", weeks[i]); } (还有一点就是:这一个方法会浪费若干空间,不过解决的方法我们 就不在本文讨论) 多维的阵列 多维阵列和二维阵列唯一的不同就是 [] 的数目,像下面就是一个三 维阵列的例子: int cubearray[2][2][2]; 当然,和上面的规则一样,最左边的维度在有初始化列表的时候可以 省略。 int cubearray[][2][2] = { {{1,2}, {3,4}}, {{5,6}, {7,8}}, {{9,10}, {11,12}} }; ==> cubearray[0][0][0] == 1, cubearray[0][0][1] == 2, cubearray[0][1][0] == 3, cubearray[0][1][1] == 4, cubearray[1][0][0] == 5, cubearray[1][0][1] == 6, cubearray[1][1][0] == 7, cubearray[1][1][1] == 8, cubearray[2][0][0] == 9, cubearray[2][0][1] == 10, cubearray[2][1][0] == 11, cubearray[2][1][1] == 12 和二维阵列一样,多维阵列也是用「以列为主(Row Major)」来表示 的,不过因为在多维阵列之中没有「列」的观念,所以在 C99-draft 之中,是以 Row-wise 来定义。所谓的 Row-wise 是指:最右边的维 度必需能以最快速、简单的方法来存取,我们可以把 Row-wise 想像 成最右边的维度会被排在一起。例如上面的,就会是 [0][0][0], [0][0][1], [0][1][0], [0][1][1], [1][0][0], ...。笔者就不再 赘述。 阵列大小的运算 在 C 语言中,有一个运算子专门用来计算一个型别、变数在记忆体 中的大小。这一个运算子叫做 sizeof。他可以有一个引数,它的值 就是引数在记忆体中所占有的空间(bytes)例如我们可以用它来取得每一个型别的大小(要耗用多少 bytes 的 记忆体)。例如:sizeof(char) 就是 1bytes,其他的值,可能因环 境而异,以下仅供参考: sizeof(int) == 4, sizeof(long) == 4, sizeof(short) == 2, sizeof(double) == 8, sizeof(wchar_t) == 2 我们也可以用 sizeof 来取得一个复合型别(例如:指标、阵列型别) 的大小: sizeof(void *) == 4 sizeof(char *) == 4 sizeof(int *) == 4 sizeof(int [4]) == 16 // (sizeof(int) * 4) 我们也可以用 sizeof 来取得 阵列变数 所占的空间: int array[5]; sizeof(array) == 5 * sizeof(int) == 20 所以我们可以发现如果反过来做,我们就可以知道阵列有多少元素: int array[] = {1, 2, 3, 4, 5}; int size = sizeof(array) / sizeof(int); /* size == 5 */ 而二维阵列我们可以用: int array[][5] = { {0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {2, 2, 2, 2, 2} }; int size = sizeof(array) / sizeof(int [5]); 来找出列数,在这里,我们可以把 int[5] 视为一个型别,他的大小 是 sizeof(int) * 5。 阵列在函式之间的传递 想像一下,有一天我们必需写一个程式来求出一串数字之中大於给订 数字的个数。而很不幸的,我们会在程式之中常常用到,此时大家想 必会想到用函式把这一个功能抽取出来,但是我们要怎麽传递阵列呢? 预备知识:在这一部分我无可避免得会谈到指标,对於不熟悉指标的 同学可能会有些吃力。 指标,你可以想像它是一个路标,用以储放位址,我们可以透过这一 个位址找到记忆体中的特定变数,不过这已经超出本文的范筹,有机 会再谈了。 阵列名字可以被转型为对应的指标型别。这是 C 语言中定义的合法转 型之一。不过要注意的是从阵列转型为指标,会失去一些资讯,如 sizeof 等,我们稍後会再说明。 int array[] = {1, 2, 3, 4, 5}; int *ptr = array; // 转型为指标 int array2d[][3] = {{1, 2, 3}, {2, 4, 6}}; int (*ptr)[3] = array2d; // 转型为「指向阵列的某一列」的指标, // 且同一列恰有 3 个元素。这一个我 // 现在没有办法说太多,只能说这和线 // 性定址有关,所以不能直接转为 int* 我们怎麽传递阵列? 更正确地来说,在 C 语言之中,为了效率考量,没有传递阵列这一 种事,只有传递阵列的指标。说得复杂一点,对於阵列而言,只有 Call-by-Pointer 没有 Call-by-Value。要在函式之间传递阵列, 只能透过指标。我们要透过指标间接地存取在阵列中的元素。 另外,在 C 语言之中,下标运算子(Subscript []),和「计算位址 偏移量并取值」是同一件事。所以我们可以直接对指标做下标运算。 例如我们可以宣告一个函式原型: int count_if_greater(int array[], int size, int number); 第一个参数看起来像是在传递阵列,事实上编译器会转换成以下的形 式,所以是传递指标;第二个参数是在传递阵列的大小;第三个参数 是传递我们要比较的数值。 int count_if_greater(int *array, int size, int number); 而我们的函式可以这样实作: int count_if_greater(int *array, int size, int number) { int reuslt = 0; int pos = 0; for (pos = 0; pos < size; ++pos) { if (array[pos] > number) { result++; } } return result; } 从这一个函式,我们可以发现:我们可以直接对指标做下标运算,就 像是在操作阵列一样,另外就是我们也要传入阵列的大小。 还有,如果你的函式原型如下,则中括号中的数值可写可不写,因为 被转型之後,都是指标,所以不会有问题。 int count_if_greater(int array[ ], int size, int number); int count_if_greater(int array[9], int size, int number); ==> int count_if_greater(int *array, int size, int number); 而我个人是倾向不要写,因为多写并不会保证你输入的阵列大小一定 会大於该数值。所以为了不要有所误解,我是建议你不用写。 如果是另一个例子,我们要处理二维或多维阵列,此时我们 [] 中的 值只有最左边的可以省,因为转型成指标时,为了正确的计算位址偏 移量,右边的维度仍必需相同。 int array[5][2][3][4]; int (*ptr)[2][3][4] = array; void func(int array[5][2][3][4]); void func(int array[ ][2][3][4]); ==> void func(int (*array)[2][3][4]); 还有要注意,因为所有的参数都是指标,所以上面用 sizeof 来计算 阵列大小的方法会有问题,因为 sizeof(array) 是取得指标在记忆体 之中占用的记忆体量。所以 sizeof(array)/sizeof(int) 不会是阵列 有多少元素,因此我们的函式要传入阵列的大小。 (错) 元素个数的计算 int count(int array[]) // 就算是 array[5] 也不行 { int size; size = sizeof(array)/sizeof(int); // 错! size = sizeof(array)/sizeof(*int); // 这也错! return size; } 结语 我们的阵列简介的系列文章,到此就结束了。可能还有一些内容还没 有讲,不过因为个人之水平有限,或者困难度过高不适合当做简介的 内容,又或者需要更多的预备知识,就不再赘述,有兴趣者可以寻找 相关的资料。 从三篇简介,我们可以学到基本阵列的使用、字串操作与相关函式、 二维阵列、多维阵列、sizeof、阵列(指标)的传递。希望能让各位 对阵列的使用有基本的认识。 ---------------- 想写得东西太多, 能用的时间太少, 站在机会成本的岔路上, 选择左边,我才有机会通古今之变, 选择右边,我才有机会成一家之言, 然而我一直停留在原地,究天人之际, 在左右二条道路之间踌躇不定。 LoganChien: 原 Po 真得是疯了... 11/15 00:56 --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 140.112.241.166
1F:推 rohan21:推微甲爆了心情不好(/‵Д′)/~ ╧╧ 11/15 04:10
2F:推 xflash96:推 11/15 09:03







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:BabyMother站内搜寻

TOP