【问题标题】:memset on float array does not fully zero out array浮点数组上的 memset 不会完全归零数组
【发布时间】:2016-05-31 21:06:31
【问题描述】:

我有一些数组,

float *large_float_array;

我调用并在循环后尝试 memset

large_float_array = (float*)calloc(NUM, sizeof(float));

// .. do stuff with float array

memset(large_float_array, 0, sizeof(large_float_array);

// .. do other stuff

但似乎large_float_array 实际上并没有归零。为什么是这样?这是由于浮点数的特殊性质吗?如果是这样,我想知道如何解决这个问题。

附: calloc 真的在这里工作吗?

【问题讨论】:

  • 我刚刚找到 this ,为我的问题提供了一些见解。
  • memset(large_float_array, 0, sizeof(large_float_array); 无法编译。总是愿意发布真实的代码。
  • C 不强制要求浮点数是 ieee754。因此,我相信标准中不能保证0.0f 按位为零。
  • large_float_array = (float*)calloc(NUM, sizeof(float)); --> large_float_array = calloc(NUM, sizeof *large_float_array);
  • 所以大家一致认为memset在浮点数组上没问题,只要我指定正确的大小?编辑:@chux 为什么要改变?

标签: c arrays dynamic


【解决方案1】:

由于large_float_array 是一个指针,您不能使用sizeof(large_float_array) 指定要清除的字节数:sizeof(large_float_array) 只是指针的大小,通常为4 或8 个字节。要清除整个数组,您必须编写:

memset(large_float_array, 0, NUM * sizeof(float));

memset(large_float_array, 0, NUM * sizeof(*large_float_array));

请注意,memset 将内存设置为所有位为零。 C 标准不保证所有位为零都是0.0 的表示,但在所有当前架构中都是如此,因此它会按预期工作。

清除数组的严格符合方法是这样的:

#include <stdlib.h>

...
for (size_t i = 0; i < NUM; i++) {
    large_float_array[i] = 0;
}

如果目标系统将0.0 表示为所有位为零,则现代编译器会将上述循环优化为对memset 的调用,这可以通过Goldbolt's Compiler Explorer 进行验证。

【讨论】:

  • 对于那些对 memset'ing a float 的合法性感兴趣的人,here's 链接。
【解决方案2】:

你不能使用memset,因为它接受int作为值类型,但是float可能以不同的方式存储,所以你应该使用常规循环

for (size_t i = 0; i < NUM; ++i) {
    large_float_array[i] = 0.;
}

【讨论】:

  • 对于i,您应该使用size_t 而不是int,因为NUM 可能大于int
  • @chqrlie C 有size_t 类型吗?
  • 是的。它在&lt;stdlib.h&gt;(以及其他地方)中定义。
  • 更好的答案。
  • 为了避免一些由无符号类型引起的错误,您可能希望更改为非 C 标准但 POSIX 标准 ssize_t,它是有符号的 size_t,并在 @987654332 中定义@.
【解决方案3】:

calloc初始化内存区的效果和使用memset函数和0值其实是一样的。换句话说,calloc 并不比memset 更“类型化”。他们都只是将内存设置为所有位为零*。

C11 (N1570) 7.22.3.2/2 calloc函数

calloc 函数为 nmemb 对象数组分配空间, 每个大小都是大小。空间初始化为所有位为零。296)

脚注 296(仅供参考):

请注意,这不必与 浮点零或空指针常量。


*) 可以想象calloc 可以返回已经用零预初始化的内存位置的地址,因此它可能比malloc + memset 组合更快。

【讨论】:

    【解决方案4】:

    帖子有多个问题。

    以下没有平衡()。不清楚 OP 的真实代码可能是什么。

    memset(large_float_array, 0, sizeof(large_float_array);
    

    假设上面的简单使用了另一个),那么代码只是用零填充了几个字节,因为sizeof(large_float_array)是一个指针的大小。

    memset(large_float_array, 0, sizeof(large_float_array));
    

    OP 可能想要这样的东西,即用零位填充分配的空间。这将使分配的内存进入与calloc() 相同的状态。

    memset(large_float_array, 0, NUM *sizeof *large_float_array);
    

    代码应该使用以下内容。一个好的编译器会将这个琐碎的循环优化成快速的代码。

    for (size_t i=0; i<NUM; i++) {
      large_float_array[i] = 0.0f;
    }
    

    使用快速memcpy() 的一种方法是将float 复制到数组的第一个元素中,然后将第一个元素复制到第二个元素,然后将前两个元素复制到元素 3 和 4,然后是第一个元素4 元素到元素 4,5,6,7 ...

    void *memset_size(void *dest, size_t n, const char *src, size_t element) {
      if (n > 0 && element > 0) {
        memcpy(dest, src, element);  // Copy first element
        n *= element;
        while (n > element) {
          size_t remaining = n - element;
          size_t n_this_time = remaining > element ? element : remaining;
          memcpy((char*)dest + element, dest, n_this_time);
          element += n_this_time;
        }
      }
      return dest;
    }
    
    float pi = M_PI;
    float a[10000];
    memset_size(a, 10000, &pi, sizeof pi);
    

    与所有优化一样,首先考虑清楚编写的代码,然后在选择的情况下采用上述候选者。

    【讨论】:

      猜你喜欢
      • 2010-11-05
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      • 2019-06-27
      • 2018-01-10
      相关资源
      最近更新 更多