【问题标题】:array of variable size in C. Is it ok?C中可变大小的数组。可以吗?
【发布时间】:2014-03-12 11:50:36
【问题描述】:

我的座右铭是在系统负载最小的情况下将一些虚拟数据写入文件。数据的大小是未知的,并在运行时指定。

为此,我可以想到 2 个解决方案。

1) 使用动态内存分配

reserve_size_in_file (int reserve_size, FILE *fp) 
{
    char *p
    p = malloc(reserve_size)
    fwrite(p, sizeof(*p), 1, fp);
    free(p);
}

2) 使用数组

reserve_size_in_file (int reserve_size, FILE *fp) 
{
    char arr[reserve_size];
    fwrite(arr, sizeof(arr), 1, fp);
}

我认为第二种方法不能用作声明具有可变长度的数组(ewww...),我认为是不允许的。但它正在工作。现在我的问题是,这样使用它可以吗?另外,如果有另一种更好的方法来做到这一点,请提出建议。

注意:我不能使用fallocate(),因为我只需要处理流 IO。

编辑:

我刚刚看到我可以使用 fileno(fp) 和 fallocate()。但是我没有看到很多使用 fallocate() 的应用程序。使用 fallocate() 有什么顾虑

例如:fallocate(fileno(fp), 0, 0, 100000);

【问题讨论】:

  • 在选项 1 中,您正在泄漏内存。你需要free() 分配给malloc() 的内存。
  • @GrahamBorland 已更新。谢谢。
  • 它真的需要消耗存储量还是可以接受的稀疏文件?
  • 对不起@datenwolf 我不太明白你问的问题。对不起我糟糕的英语。在我的用户可以保留任意数量的数据。我应该准备好直到 20 MB。
  • @Manty:在大多数操作系统上,文件可能是“稀疏的”,即文件的长度与实际存储在其中的字节数不同。其中可能有漏洞,当您读取它们时,它们会以 NUL 字节出现,但实际上并未存储在存储设备上。如果您希望稍后用实际数据填充文件并且不想等待操作系统实际分配存储设备上的字节,则为文件保留存储空间是有意义的。 –– 如果您不需要预先分配实际存储空间,您可以使用fseek(fp, SEEK_SET, offset); 在流中的任何位置写入数据

标签: c arrays file file-io


【解决方案1】:

Variable length arrays(VLA) 是 C99 标准功能,只要您知道您将使用的所有编译器都支持 C99,就可以使用。 C11 标准虽然使可变长度数组成为可选的。 gccclang 即使在 C99 模式之外也支持 VLA 作为扩展。另一方面,Visual Studio 直到最近才支持 C99,我认为他们还不支持 VLA。

如果您不提前知道大小,另一种选择是通过 malloc 进行动态内存分配。

正如 Jens 指出的那样,VLA 确实会进入堆栈,并且您有 limited stack space,如果您的数组大小很大,这可能是一个问题,如果是这种情况,那么您将不得不使用动态内存。

【讨论】:

  • 我刚刚编辑了我的问题。你能解释一下吗。谢谢。
【解决方案2】:

我会这样做:

reserve_size_in_file (int reserve_size, FILE *fp) 
{
    /* Usual HDDs have a block size of 512, but big
     * block storage is becoming popular. */
    char arr[4096]; 

    /* initialize to 0xff, since runs of NUL bytes might
     * be transparently replaced with sparse file holes. */
    memset(arr, 0xff, sizeof(arr));

    while(reserve_size > sizeof(arr)) {
        fwrite(arr, sizeof(arr), 1, fp);
        reserve_size -= sizeof(arr);
    }
    fwrite(arr, reserve_size, 1, fp);
}

这完全避免了动态内存分配,并且可以很好地处理块大小。

【讨论】:

  • +1 对于进化的问题,这个答案可能是更好的答案。
  • 谢谢。我愿意接受这个答案。但是您能否也评论一下 fallocate() 部分?
  • @Manty: fallocate,或者如果你想保持便携,posix_fallocate 基本上和我的代码 sn-p 一样,但是“在幕后”并且没有辅助缓冲区。大多数应用程序不使用 fallocate,但因为它对它们没有用。事实上,只有极少数情况下需要在存储设备上预先分配空间。实际上对我来说只有两个容易想到:实时视频录制数据库存储集群,甚至数据库也很少使用预分配。那么问题来了:为什么您认为需要进行预分配?
  • 基本上我正在创建一个库,该库将帮助用户根据不同的数据(日志或其他信息)创建 ELF 类型的对象。因此,如果用户想在对象中分配一些空间并稍后返回并写入一些数据,我想给予支持。这就是整个想法。希望我很清楚。
  • @Manty:那你就不需要预分配了。稀疏文件是你想要的。只需fseek 到文件中应该写入的偏移量。无需刷过中间的缝隙。我认为那些 ELF 将被链接到一个可执行文件中。别担心,这些洞没有任何伤害。可执行文件是内核映射到进程内存空间的内存,所有位于空洞上的页面都读为零;如果文件被映射为写入,写入一个洞会透明地填充存储空间。
【解决方案3】:

VLA 有两个缺点

  • 您不知道可以为他们“在堆栈上”保留多少数据
  • 分配不成功不会返回错误。

因此,除非您确定您的阵列永远不会超过一公斤的数据,否则我不会为此目的选择它们。否则,如果分配失败,您将因 stackoverflow 出现虚假错误。

【讨论】:

  • 谢谢。除了上述2之外,我还有其他选择吗?我能想到的另一种方法是每次 fseeking 开始时在 for 循环中写入 1 个字节。一般的标准方式是什么?
  • 没有真正的标准。许多人所做的就是保留一个固定大小的数组,比如 8k,然后对其进行迭代。
  • 我刚刚编辑了我的问题。你能否解释一下。谢谢。
猜你喜欢
  • 2019-12-02
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
  • 2020-08-24
  • 2021-06-02
  • 2010-10-01
  • 1970-01-01
相关资源
最近更新 更多