作者poyenc (发箍)
看板C_and_CPP
标题Re: [问题] 二维阵列初始化问题
时间Wed Apr 3 02:33:54 2019
※ 引述《skyHuan (Huan)》之铭言:
: 在写程设矩阵乘法的题目的时候遇到一些问题
: 完整程式码在这里:https://pastebin.com/MxAUgHcY
: 这是执行结果:https://i.imgur.com/TWB7cYL.png
: 上面两区块是input,即一个2x3矩阵乘上一个3x4矩阵,最下面的区块是相乘完的结果
: 中间两个区块是测试过程,也就是我的问题所在
: 以下列出我的问题,基本的程式观念没有很好,还请前辈们多多指教
: 1. 我的作法是先宣告出要存相乘结果的矩阵并初始化,如程式第18行
: 我记得二维阵列可以用 = {0} 来把全部的内容初始化为0
: 但做完第18行後,print出新宣告的阵列结果会是测试区块的上面那块
: 出现几个很大的数字,感觉像是记忆体残值(?
: 用for回圈重新设定每个为0之後才恢复正常全部都是0
: 是我 = {0} 的使用上有什麽没注意到的吗
在 C 语言中只有阵列这个概念, 没有几维的分别. 当你用下面几种
方式来定义阵列, 概念上还是以巢状阵列为主 (array of arrays),
这个概念很重要, 尤其在算位移的时候:
int a[2];
// array of 2 ints
int b[3][4];
// array of 3 int[4]s
int c[4][5][6];
// array of 4 int[5][6]s
因此阵列元素 c[
2] 的型别为
int[5][6], 记忆体位址为
(
char*)&***c +
2 *
sizeof(
int[5][6])
^ ^ ^
第一个元素的位址 + 索引 * 每个元素的大小
另一个很重要的概念是:
阵列初始化只能初始前几个元素, 剩下未
给值的元素都将用 zero bytes 填满, 所以下面的初始化应该和你
所认知的不一样:
int a[3] = { -1 };
// { -1, 0, 0 }
一对大括号用来初始化一个阵列,
巢状阵列则需要有对应层数的大
括号来初始化:
int b[3][4] = {
{ -1 },
{ },
{ 5, 6, 7, 8 }
};
/*
b = {
{ -1, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 5, 6, 7, 8 }
}
*/
回到上一段所说的, 未给值的元素都会用 zero bytes 填满, 如果
你就想要全部值都填 0, 像初始化 b[1] 那样直接给空大括号即可
(
全都不给初始值, 所以都用 zero bytes 来填).
: 2. 宣告二维阵列大小的时候,大小是否可以用变数来表示
: 例如程式码中的第7行中的m跟k1在scan後才能决定值
: 那第8行的二维阵列那样宣告是合法的吗,还是一定要用malloc的方式才行
: 我用自己电脑的IDE (CB)跟线上compiler (C99)跑都有过
: 但同学跑一模一样的程式码compiler不给过(VS)
可以, 但这需要你的编译器支援
VLA (Variable-Length Array),
这是在
C99 才进的 feature, 因为目前 C 编译器预设使用的语言
标准大多还是 C89/C90, 所以先确定你的编译器有支援到 C99 并在
编译时启用.
https://en.wikipedia.org/wiki/C99#Implementations
另外需要注意的是 VLA 的记忆体是放在 call stack 上, 使用的时
候小心别配置超过编译器/环境允许的大小.
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 121.131.81.65
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1554230037.A.5FD.html
1F:推 skyHuan: 哇谢谢你还专程打这麽多超详细的,帮我厘清了很多观念 04/03 03:14
2F:→ adrianshum: (char*)&***c + 2 * sizeof(int[5][6]). 这段 04/03 09:21
3F:→ adrianshum: 有点不正常。首先cast 成char* 有点怪,我不熟C99 但 04/03 09:21
4F:→ adrianshum: 应该有int_8 或byte 之类?用这些比用char 比较能表达 04/03 09:21
5F:→ adrianshum: 你的目的。其次应该不用 &***c 吧?直接 c 或&c 就好 04/03 09:21
6F:→ adrianshum: 了。 04/03 09:21
7F:推 cutekid: c[2] 记忆体位址 = c + 2 04/03 13:11
有不同运算都可以算出相同的记忆体位址, 只是最後的型别不同,
语意也不一样:
型别 叙述
int(*)[4][5][6] &c
int(*)[5][6] c
(decay 後)
int(*)[5][6] c + 2
int[5][6] c[2]
int[5][6] *c
int[6] **c
int ***c
int* &***c
***c 语意上就是取得第一个
int 元素,
sizeof(
char) 的值为 1,
它扮演的角色就是你说的 byte. 先将 &***c 转成
char* 确保往後
的指标运算都是以 byte 为单位 (int8_t 只是无 padding 的连续
8 个 bits, 但无法代表一个 byte). 所以最後整串叙述的语意为:
c[
2] 的位址是从第一个元素算起, 位移为
2 个
int[5][6] 大小的地方
这边把
sizeof 放进来是想表达指标运算的背後都和元素大小相关,
而元素大小是由指标型别来决定的, 无法单纯靠索引来计算位移.
8F:推 cuteSquirrel: 推荐这篇文章 04/03 18:48
9F:→ adrianshum: 我明白你的目的,(char*)&***c 你的目的只是用一byte 04/04 14:53
10F:→ adrianshum: ptr 指向整个array 的起始位址而已。反正你都硬cast 04/04 14:53
11F:→ adrianshum: 成char* 了,用&c 或 decaying c 更能表达「整个arra 04/04 14:53
12F:→ adrianshum: y 的起始位址」的意思。 04/04 14:53
13F:推 adrianshum: 用char方面再查考之下的确较好,我一直有个错误印象 1 04/04 15:01
14F:→ adrianshum: char 未必是 1 byte 长。(怎麽标准不弄个 byte type 04/04 15:01
15F:→ adrianshum: 出来 :( ) 04/04 15:01
重点不是最後的值如何(方法有很多种), 而是过程中转换过的型别
叙述 原始型别 结果型别
&c
int(*)[4][5][6]
char*
c
int[4][5][6]
char*
&***c
int*
char*
应该不用说明也可以看出差异在哪. 与其跟新手说「请你去算出
array of array of arrays的起始位址」, 个人觉得将阵列一步步
拆解, 扁平化, 再去算位移着实简单得多.
16F:→ sarafciel: 他想表达的就是那个三层指标的原始型态啊 04/04 22:09
17F:→ sarafciel: 纯粹用&c或c的话会让人以为int[4][5][6]就是拿单指标接 04/04 22:12
18F:→ sarafciel: 新手的话更容易踩这种陷阱 04/04 22:13
呜呜, 握手 QQ
19F:→ sarafciel: 是说就是因为这样 我都不写int[4][5][6]这种型态 04/04 22:17
20F:→ sarafciel: 宁愿弄成int[4*5*6]来做事 顶多下标麻烦一点(逃) 04/04 22:19
※ 编辑: poyenc (121.131.81.65), 04/05/2019 04:18:04