【问题标题】:Memory leak using Malloc/Free in C在 C 中使用 Malloc/Free 的内存泄漏
【发布时间】:2021-01-18 03:37:01
【问题描述】:

我一直在阅读指针的使用以及为嵌入式项目分配内存。我必须承认,我可能并不完全理解它,因为我似乎无法弄清楚我的问题出在哪里。

我的两个函数应该采用 4 个浮点值,并返回 16 个字节,代表这些值,以便通过 SPI 传输它们。在程序崩溃并且我的 SPI 和 I2C 死机之前,它工作得很好,但只有一分钟,哈哈。

以下是函数:

/*Function that wraps a float value, by allocating memory and casting pointers. 
Returns 4 bytes that represents input float value f.*/
typedef char byte;

byte* floatToByteArray(float f)
{
    byte* ret = malloc(4 * sizeof(byte));
    unsigned int asInt = *((int*)&f);
    
    int i;
    for (i = 0; i < 4; i++) {
        ret[i] = (asInt >> 8 * i) & 0xFF;
    }

    return ret;
    memset(ret, 0, 4 * sizeof(byte)); //Clear allocated memory, to avoid taking all memory
    free(ret);
}

/*Takes a list of 4 quaternions, and wraps every quaternion in 4 bytes.
Returns a 16 element byte list for SPI transfer, that effectively contains the 4 quaternions*/
void wrap_quaternions(float Quaternion[4], int8_t *buff)
{
    uint8_t m;
    uint8_t n;
    uint8_t k = 0; 
    
    for (m = 0; m < 4; m++)
    {   
        for (n = 0; n < 4; n++)
        {
            byte* asBytes = floatToByteArray(Quaternion[m]);
            buff[n+4*k] = asBytes[n];
        }   
        k++;
    }
}

我收到的错误信息如下,在 Atmel Studio 的反汇编窗口中

Atmel studio screenshot

【问题讨论】:

  • 你用 valgrind 检查你的代码了吗?
  • 是否有一些关于无法访问/死代码的警告?
  • 您调用floatToByteArray 16 次,每次调用只使用 1 个字节。你永远不会释放内存。 (记住:return 之后的语句永远不会到达。)

标签: c memory malloc free memset


【解决方案1】:

您可能会完全放弃所有动态内存分配。

void floatToByteArray(float f, byte buf[4])
{
    memcpy(buf, &f, sizeof(f));
}

void wrap_quaternions(float Quaternion[4], int8_t *buff)
{   
    for (int i = 0; i < 4; i++)
    {   
        floatToByteArray(Quaternion[i], &buf[4*i]);
    }
}

使用这种方法,您无需担心在使用后释放分配的内存。它也更有效,因为动态内存分配相当昂贵。

【讨论】:

  • 非常感谢!我也在研究 memcpy,但发现它看起来比 malloc 和 free 更复杂。我想采取正确的方式会为我省去麻烦。
  • memcpy 不能替代 malloc。它是您循环的替代品。您也可以直接在调用函数中执行“memcpy”并删除floatToByteArray
【解决方案2】:

Gerhardh 是正确的,return 阻止内存被释放。

如果您需要返回 4 个字节,您可以检查您的环境是否可以返回 uint32_t 或类似的内容。

【讨论】:

    【解决方案3】:

    如前所述,return ret; 下面的行永远不会执行。无论如何,如果您想在函数中返回分配的内存(这很好),您不能在函数本身中释放它,但它必须在不再需要时由调用者释放。所以你的调用函数应该是这样的

    /*Takes a list of 4 quaternions, and wraps every quaternion in 4 bytes.
    Returns a 16 element byte list for SPI transfer, that effectively contains the 4 quaternions*/
    void wrap_quaternions(float Quaternion[4], int8_t *buff)
    {
        uint8_t m;
        uint8_t n;
        uint8_t k = 0; 
        
        for (m = 0; m < 4; m++)
        {   
            byte* asBytes = floatToByteArray(Quaternion[m]); // no need it to call for every n
            for (n = 0; n < 4; n++)
            {
                 buff[n+4*k] = asBytes[n];
            }
            free(asBytes);  // asBytes is no longer needed and can be free()d 
            k++; 
        }
    }
    

    【讨论】:

      【解决方案4】:

      关于:

      buff[n+4*k] = asBytes[n];
      

      这会导致:

      buff[0] << asBytes[0]  // from first call to `byte* floatToByteArray(float f)`
      buff[4] << asBytes[1]  // from second call to `byte* floatToByteArray(float f)`
      
      buff[8] << asBytes[2]  // from third call to `byte* floatToByteArray(float f)`
      
      buff[12] << asBytes[3] // from forth call to `byte* floatToByteArray(float f)`
      

      上面的大部分问题可以通过使用memcpy()将4个字节从asBytes[]复制到buff[]来解决,类似于:

      memcpy( &buff[ n*4 ], asBytes, 4 );
      

      当然,还有一个考虑:float的长度,在你的硬件/编译器上实际上是4个字节吗。

      '魔术'数字是没有基础的数字。 “神奇”数字使代码更难理解、调试等。 4. 建议使用类似:length = sizeof( float );,然后在当前使用4 的任何地方使用lengthQuaternion[] 数组中的条目数除外。对于那个“神奇”数字,强烈建议声明:#define arraySize 4 在您的代码中尽早。然后使用arraySize每次代码引用数组中的元素个数

      【讨论】:

      • 没问题!实际上,我在我的程序中使用了很多。我会在星期一改变它:-)
      猜你喜欢
      • 2011-11-28
      • 2021-03-26
      • 2019-04-28
      • 2020-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多