【问题标题】:Dynamically indexing an array in C在 C 中动态索引数组
【发布时间】:2019-04-30 00:29:25
【问题描述】:

是否可以像中那样根据索引创建数组

int x = 4;
int y = 5;
int someNr = 123;
int foo[x][y] = someNr;

动态/在运行中,不创建 foo[0...3][0...4]?

如果没有,是否有一种数据结构可以让我在 C 中做类似的事情?

【问题讨论】:

  • 听起来你可能想要一张地图stackoverflow.com/questions/21958247/…
  • 您需要先声明数组然后分配第二个,除非您准备好assign the values properly。根据定义,您未初始化的任何值都未初始化并包含垃圾数据,因此您可能希望全部初始化它们,而不是选择性地初始化。
  • @tadman 如果我先声明它,那么它不会是动态的
  • 如果你想要它完全动态,那么你可以使用malloc。您必须事先知道最大尺寸,否则这是不可能的。正如 bhspencer 所说,如果您不知道 xy 的这些值将落在哪里,那么您可能想要一张地图。
  • 如果只有 25% 将被占用,您必须关注这里的实际成本。 1024 * 1024 * 4 真的会破产吗?那只是4MB的内存。除非你有成千上万个这样的东西,或者你真的记忆力有限,否则这没什么大不了的。如果这很关键,您可以使用short intchar 来节省一些内存。从必须捕获的值和必须在其中操作的内存条件两个方面考虑约束。

标签: c arrays


【解决方案1】:

没有。

正如您所写的那样,您的代码完全没有意义。您需要在某处声明foo,然后您可以使用foo[x][y] = someNr; 对其进行索引。但是你不能只是让foo 出现,这就是你正在尝试做的事情。

例如创建具有正确大小的 foo(只有您可以说出它们是什么)int foo[16][16]; 或使用不同的数据结构。 在 C++ 中,您可以使用 map<pair<int, int>, int>

【讨论】:

  • 我知道这毫无意义,重点是说明我想做什么。我确实看过 C 的映射,看来我必须散列才能在映射数组中找到正确的映射,是吗?
  • 有不同的方法。散列可能是最常见的。 “稀疏数组”是一个很好的搜索词,可以进行更多研究。正如@tadman 在 cmets 中所说:1024x1024 并没有那么昂贵(也许不要把它放在堆栈上),所以您是否需要做一些其他数据类型或只是分配一个大数组真的是您的决定。
  • 我知道占用 4MB 内存通常没什么大不了的,但我正在使用操作系统,在 32 位上运行每个程序只占用 4mb。但在 64 位上,我需要 2*2^25 int 数组
  • 那么看起来像稀疏数组是要走的路。
【解决方案2】:

可变长度数组

即使xy 被替换为常量,您也无法使用所示符号初始化数组。您需要使用:

int fixed[3][4] = { someNr };

或类似的(可能是额外的大括号;可能是更多的值)。但是,您可以声明/定义可变长度数组 (VLA),但根本无法初始化它们。所以,你可以写:

int x = 4;
int y = 5;
int someNr = 123;
int foo[x][y];

for (int i = 0; i < x; i++)
{
    for (int j = 0; j < y; j++)
        foo[i][j] = someNr + i * (x + 1) + j;
}

显然,您不能使用xy 作为索引而不在数组边界之外写入(或读取)。您有责任确保堆栈上有足够的空间用于选择作为数组限制的值(在 3x4 上不会有问题;虽然它可能是 300x400,但会是 3000x4000)。您还可以使用 VLA 的动态分配来处理更大的矩阵。

VLA 支持在 C99 中是强制性的,在 C11 和 C18 中是可选的,在严格的 C90 中不存在。

稀疏数组

如果您想要的是“稀疏数组支持”,那么 C 中没有内置工具可以帮助您。您必须设计(或找到)可以为您处理的代码。当然可以; Fortran 程序员过去常常不得不这样做,当时兆字节的内存是一种奢侈,而 MIPS 意味着每秒数百万条指令,当他们的计算机可以执行两位数的 MIPS(而 Fortran 90 标准是未来几年)。

您需要设计一个结构和一组函数来处理稀疏数组。您可能需要决定是否在每一行中都有值,或者是否只在某些行中记录数据。您需要一个函数来为单元格分配值,并需要另一个函数来从单元格中检索值。当没有明确的条目时,您需要考虑值是什么。 (这个想法可能并不难。默认值通常为零,但无穷大或 NaN(不是数字)可能是合适的,具体取决于上下文。)您还需要一个函数来分配基本结构(将你指定最大尺寸?)和另一个释放它。

【讨论】:

    【解决方案3】:

    创建数组动态索引的最有效方法是创建一个与要索引的数组所保存的数据类型相同的空数组。

    为了简单起见,假设我们使用整数。然后,您可以将该概念扩展到任何其他数据类型。

    理想的索引深度将取决于要索引的数据的长度,并且会接近数据的长度。

    假设数组中有 100 万个 64 位整数要索引。

    首先,您应该对数据进行排序并消除重复项。这很容易通过使用 qsort()(快速排序 C 内置函数)和一些删除重复函数来实现,例如

    uint64_t remove_dupes(char *unord_arr, char *ord_arr, uint64_t arr_size)
    {   
        uint64_t i, j=0;
        for (i=1;i<arr_size;i++)
        {
            if ( strcmp(unord_arr[i], unord_arr[i-1]) != 0 ){
                strcpy(ord_arr[j],unord_arr[i-1]);
                j++;
            }
            if ( i == arr_size-1 ){
              strcpy(ord_arr[j],unord_arr[i]);
              j++;  
            }   
        }
        return j;
    }
    

    根据您的需要调整上面的代码,当函数完成对有序数组的排序时,您应该 free() 无序数组。上面的函数非常快,当要排序的数组包含一个元素时,它将返回零条目,但这可能是你可以忍受的。

    一旦数据有序且唯一,创建一个长度接近数据长度的索引。它不需要有一个确切的长度,虽然保证 10 的幂会让一切变得更容易,如果是整数。

    uint64_t* idx = calloc(pow(10, indexdepth), sizeof(uint64_t));
    

    这将创建一个空索引数组。 然后填充索引。遍历您的数组以索引一次,每次您检测到左侧有效数字的数量(与索引深度相同)发生变化时,添加检测到该新数字的位置。

    如果您选择 indexdepth 为 2,您的索引中将有 10² = 100 个可能的值,通常从 0 到 99。

    当您检测到某个数字以 10 (103456) 开头时,您向索引添加一个条目,假设在位置 733 检测到 103456,您的索引条目将是:

    index[10] = 733;
    

    下一个以 11 开头的条目应该添加到下一个索引槽中,假设第一个以 11 开头的数字在位置 2023 找到

    index[11] = 2023;
    

    等等。

    当您稍后需要在存储 100 万个条目的原始数组中查找某个数字时,您不必迭代整个数组,您只需检查索引中第一个数字从前两位有效数字开始的位置被储存了。条目索引[10] 告诉您以 10 开头的第一个数字的存储位置。然后,您可以向前迭代,直到找到匹配项。

    在我的示例中,我使用了一个小索引,因此您需要执行的平均迭代次数为 1000000/100 = 10000

    如果您将索引扩大到接近数据长度的某个位置,则迭代次数将趋于 1,从而使任何搜索速度都非常快。

    我喜欢做的是创建一些简单的算法,在知道要索引的数据的类型和长度后,告诉我理想的索引深度是多少。

    请注意,在我提出的示例中,64 位数字由它们的第一个索引深度有效数字索引,因此 10 和 100001 将存储在同一个索引段中。这本身不是问题,但是每个大师都有他的小秘密书。将数字视为固定长度的十六进制字符串有助于保持严格的数字顺序。

    您不必更改基数,您可以将 10 视为 0000010 以将其保留在 00 索引段中并保持基数为 10 的数字有序,但使用不同的数字基数在 C 中是微不足道的,这很棒帮助完成这项任务。

    随着索引深度变大,每个索引段的条目数量会减少

    请注意,编程,尤其是像 C 这样的低级编程,在很大程度上在于理解 CPU 周期和内存使用之间的权衡。

    创建建议的索引是一种减少定位值所需的 CPU 周期数的方法,代价是随着索引变大而使用更多内存。尽管如此,这仍然是当今要走的路,因为大量的内存很便宜。

    随着 SSD 的速度越来越接近 RAM 的速度,需要考虑使用文件存储索引。然而,现代操作系统倾向于尽可能多地加载到 RAM 中,因此从性能角度来看,使用文件最终会产生类似的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-27
      • 2011-07-24
      • 2011-01-24
      • 1970-01-01
      • 2020-01-12
      • 2018-06-24
      相关资源
      最近更新 更多