当性能对应用程序至关重要时,是否应该考虑是否在堆栈和堆上声明数组?请允许我概述一下为什么会想到这个问题。
由于 C/C++ 中的数组不是对象并且衰减为指针,因此编译器使用提供的索引来执行指针算术以访问元素。我的理解是,当超过第一个维度时,此过程不同于静态声明的数组和动态声明的数组。
如果我要在堆栈上声明一个数组,如下所示;
int array[2][3] = { 0, 1, 2, 3, 4, 5 }
//In memory { row1 } { row2 }
该数组将以Row Major格式存储在内存中,因为它存储在连续的内存块中。这意味着当我尝试访问数组中的元素时,编译器必须执行一些加法和乘法来确定正确的位置。
因此,如果我要执行以下操作
int x = array[1][2]; // x = 5
然后编译器将使用此公式,其中:
i = 行索引 j = 列索引 n = 单行的大小(这里 n = 2)
数组 = 指向第一个元素的指针
*(array + (i*n) + j)
*(array + (1*2) + 2)
这意味着如果我要遍历这个数组来访问它的每个元素,那么每次按索引访问都会执行一个额外的乘法步骤。
现在,在堆上声明的数组中,范式不同,需要多阶段解决方案。注意:我也可以在这里使用 C++ new 运算符,但我相信数据的表示方式没有区别。
int ** array;
int rowSize = 2;
// Create a 2 by 3 2d array on the heap
array = malloc(2 * sizeof(int*));
for (int i = 0; i < 2; i++) {
array[i] = malloc(3 * sizeof(int));
}
// Populating the array
int number = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0l j < 3; j++) {
array[i][j] = number++;
}
}
由于数组现在是动态的,它的表示是一维数组的一维数组。我会尝试画一张ascii图片...
int * int int int
int ** array-> [0] 0 1 2
[1] 3 4 5
这意味着不再涉及乘法,对吗?如果我要执行以下操作
int x = array[1][1];
然后这将在 array[1] 上执行间接/指针算术以访问指向第二行的指针,然后再次执行此操作以访问第二个元素。我这样说对吗?
现在有了一些上下文,回到问题。如果我正在为需要清晰性能的应用程序编写代码,例如渲染帧大约需要 0.016 秒的游戏,我是否应该三思而后行在堆栈和堆上使用数组?现在我意识到使用 malloc 或 new 运算符有一次性成本,但是在某个点(就像 Big O 分析)当数据集变大时,最好迭代动态数组以避免行主要索引?