【问题标题】:Structures with dynamic memory allocation in CC中具有动态内存分配的结构
【发布时间】:2011-11-13 20:51:23
【问题描述】:

我需要将数据写入一个结构,其中数据的长度取决于我要发送到设备的命令。为此,我定义了以下结构:

typedef struct {
    uint8 len;          // Command length (cmd ... crc)
    uint8 cmd;          // Command code
    uint8 data_length;  // Data length
    uint8 data[12];     // Data: max 12 Byte
    uint8 crc_h;        // CRC value MSB
    uint8 crc_l;        // CRC value LSB
}CMD_TYPE;

注意: 成员 cmd、*data_length* 和 crc 始终存在,而不是成员 data可以为空或最多包含 12 个字节。

我创建了一个函数,它根据传递给函数的参数返回一个初始化的命令:

CMD_TYPE Device::get_cmd(uint8 cmd, uint8 data_len, uint8 *data)
{
    CMD_TYPE cmd;

    cmd.len = (4 + data_len) * sizeof(uint8);
    cmd.cmd = cmd;
    cmd.data_length = data_len;
    cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));
    if(data_len > 0)    memcpy(cmd.data, data, data_len);

    add_crc16((uint8*)&cmd);

    return cmd;
}

函数get_cmd()的使用如下:

uint8 cmd_code = 0x01;
uint8 data[2] = {0xAB, 0xCD};

CMD_TYPE cmd = local_device->get_cmd(cmd_code, 2, data);
retVal = local_device->send(cmd);

当我尝试编译此代码时,我从该行的编译器中得到一个错误:

cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));

编译器错误是:

error: lvalue required as left operand of assignment

使用 realloc() 的目的是重新调整数组数据的大小或将其从我的新命令结构中完全删除。我的代码有什么问题?这是用动态内存分配初始化结构的正确方法吗?

【问题讨论】:

  • 数组不是指针——你不能重新分配数组的内存位置。

标签: c memory dynamic allocation realloc


【解决方案1】:

你要的是臭名昭著的struct hack

typedef struct
{
    uint8   len;          // Command length (cmd ... crc)
    uint8   cmd;          // Command code
    uint8   data_length;  // Data length
    uint8   crc_h;        // CRC value MSB
    uint8   crc_l;        // CRC value LSB
    uint8   data[1];      // Data: max 12 Byte
} CMD_TYPE;

诀窍是为结构的所有成员分配足够的空间,直到data[],然后为data[] 成员添加足够的字节:

CMD_TYPE * allocCmd(int dataSize)
{
    int         len;
    CMD_TYPE *  p;

    len = sizeof(CMD_TYPE) + (dataSize-1)*sizeof(uint8);
    p = (CMD_TYPE *) malloc(len);
    memset(p, 0, len);
    p->data_length = dataSize;
    return p;
}

这里,len 被计算为结构的大小,减去空的 data 成员的大小,再加上 dataSizedata 数组指定的许多元素。

要注意的是,您必须小心永远不要访问p->data[] 的任何元素,而不是实际分配在其中(结构内部)的元素。

【讨论】:

  • 是的,这样的结构会更容易......但问题是我必须完全按照结构中的顺序发送数据。
  • @imx51,您只能更改结构末尾的数据。您不能改变结构内数据的大小。您可以使 data 成为指向另一个不同长度的结构/数组的指针(因为指针本身不会改变大小)
  • 你也可以使用这个 struct hack 并认为 CRC 值是数据的最后两个字节,而不是为它们设置单独的字段。因为你在结构中有长度,所以它' d 很容易找出它们的索引......如果你打算只转储结构占用的内存来发送它。如果不是,则不需要按照它们在结构中出现的顺序发送字段。
  • 我已经使用了一段时间,并没有完全意识到它是一个正式的概念,现在是标准的一部分!哇,天才我:D
【解决方案2】:

您的CMD_TYPE.data 是一个数组,而不是一个指针。由于您希望它跟踪动态分配的内存,因此它必须是一个指针:

uint8_t * data;

只是不要忘记用malloc() 初始化它(或在realloc() 之前将其设置为零)并自己清理

顺便说一句,不要malloc()和co.的结果进行转换。

【讨论】:

  • 他为什么不应该转换malloc()的结果?它返回一个 void *,不是吗?
  • 因为在 C 中你不强制转换 - 如果你这样做了,你可能会隐藏错误,并且 C 明确允许你使用这样的 void 指针。另外,它看起来不是很丑吗?
  • 感谢您的快速回复!我已经进行了更改,现在当 data_length 为 0 时,一切似乎都是正确的。但是当我想将数据写入字段数据时,看起来数据没有正确复制。这是我的更改:cmd.data = (uint8 *)malloc(data_len * sizeof(uint8)); 注意: 如果我没有转换结果,编译器会返回错误:error: invalid conversion from 'void*' to 'uint8*'
  • @imx:你现在需要一些额外的代码来正确初始化结构并清理它。这不仅仅是一条线的变化。
  • 也许您并没有向我们坦白您使用的语言。 C(我指的是“ISO C 标准”)明确允许从 void 指针进行转换。如果您正在使用一些非标准编译器或方言(1985 年的 Borland Turbo C?MS Visual C++?),请适当地标记问题。请注意,C++ 与 C 不同,微软没有真正的 C 编译器。
【解决方案3】:

定义为 a[..] 的数组是不可变的,你不能给它们分配任何东西。相反,您应该使用指针。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 2021-10-15
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多