我没有足够的声望来发表评论,但是...
我有些同意Skizz的答案,即"最快速完成任务的方法通常是不去做它",但有一个非常重要的警告。如果你要像这样过滤访问,你真的需要考虑每个周期的浪费。
例如,Skizz提到:
void ClearArray ()
{
array_is_empty = true;
}
int ReadValue (int x, int y)
{
return array_is_empty ? 0 : array [x][y];
}
我想指出条件运算符“?:”是一个不必要的分支。为了消除这个问题,您可以使用一个整数
array_is_empty,其值为
0或
1,然后...
int ReadValue (int x, int y)
{
return array_is_empty * array [x][y];
}
这将去除条件,但添加了一个不必要的乘法,编译器不会优化掉它,因为它不知道array_is_empty是否会有除0或1之外的值。
因此,我建议使用全1的AND掩码。这可以是有符号类型中的“-1”表示,也可以是任何简单类型中的~0(零,位翻转)。
int ReadValue (int x, int y)
{
return array_is_empty & array [x][y];
}
这将产生一个更干净、更快的解决方案,没有不必要的分支预测和缓慢的乘法操作。AND非常快,如果您对数组进行大量查找并执行此过滤,则这一点非常重要。
我要补充的最后一个警告是相当严重的:
当大多数人将数组清零时,他们希望每个元素都为零。使用“单标志”方法,设置单个元素将删除过滤器并公开所有未初始化的数据...这几乎从来不是用户所期望的。这可能会在以后引起很多麻烦。
另一种选择(如Skizz所示)是在第一次写入时将数组清零。这会撤消该方法的所有潜在好处,只留下缺点和性能损失。
因此,我只会在完全初始化或根本不初始化的数组上使用它。而且,说实话,这严重地限制了它的用途。
它的用处在于数组在块中使用时,例如行……特别是当稀疏占用时。如果每个行或块都有这样的标志,那么它就成为了一种非常有效的方法。第一次写入时的单个通用“memset”(请参见Skizz的示例)会撤销任何潜在的好处,并且是一个大警报,表明您应该在构建时将数组清零;)
此外,虽然是小事,但我个人会避免像“ClearArray”这样的方法名称……我谦虚地建议,“MarkUninitialised”或“MarkUnused”可能看起来很丑陋,但可以避免混淆。
尽管如此,还是要感谢Skizz的回答。如果有意义,那么值得做。避免不必要的工作就像优化重构一样好;)
对于让这成为答案,我表示歉意。考虑到数组访问期间额外工作的迅速增加以及memset-on-first-write的问题,我认为这很值得指出。
memset
上,因为你提到仅清零一行时也会崩溃。 - Blindyint d0=10, d1=20; int arr[d0][d1]
,并使用memset(arr, 0, sizeof arr);
进行了测试(使用-std=c99 -Wall
标志进行编译,gcc版本为3.4.6),结果符合预期。我知道“在我的机器上能运行”这个说法并不靠谱,但是memset(arr, 0, sizeof arr);
应该 能正常工作。sizeof arr
应该 返回整个数组所占用的字节数(即 d0 * d1 * sizeof(int))。使用sizeof array[0] * m * n
无法得出正确的数组大小。 - John Bodeint array[][10]
,那么sizeof(array) == sizeof(int*)
,因为第一维的大小未知。OP没有指定数组是如何获取的。 - James McNellis