将结构体复制到缓冲区

5

我有一个结构:

struct data{
 int num1;
 int num2;
 int num3;
 int num4;
}

现在,我可以转换单个元素并将它们放入缓冲区中,但我想将整个结构体复制到一个缓冲区中(char buffer[sizeof(data)])。

有没有一种方法可以复制整个结构体?


3
考虑到大多数C编译器中int类型的大小为4个字节,你要将该结构体放入8个字节的缓冲区中可能会遇到麻烦! - Mitch Wheat
如何看待:char buffer[sizeof(struct data)]; - Richard Schneider
在右括号后面需要加上分号。此外,你所定义的只是一个类型,而不是该类型的值。sizeof(data) 是无效的,除非你已经定义了一个名为 data 的变量,或者有一个 typedef struct data data - Jim Balter
5个回答

9

memcpy 可以完成这个操作

微软系统可以使用memcpy_s 对大小进行自动检查

你还需要注意 struct data 内部的指针。

struct data { 
    int num1;
    char* name;
}

char* name指针所指向的内存内容将不会被memcpy函数复制。您需要一些更加复杂的序列化方式。

以下是一些有效的代码示例。它可以将数据复制到另一个位置,然后显示结果:

struct data{
  int num1;
  int num2;
  int num3;
};

int main(int argc, char** argv)
{
   data my_data;
   my_data.num1 = 64;
   my_data.num2 = 65;
   my_data.num3 = 66;

   char buffer[20];
   memcpy(buffer, &my_data, sizeof(data));

   data copy_data;
   memcpy(&copy_data, buffer, sizeof(data));

   printf("the numbers : %d - %d - %d \n", copy_data.num1, copy_data.num2, copy_data.mum3);



   return 0;
}
       

1
在这种情况下,由于目标缓冲区的大小可能比结构体的大小小,因此memcpy(buffer, var_data, sizeof(buffer))可能是一个更好的选择。 - Ben
@ughoavgfhw data 是结构体的名称,而不是变量的名称,因此您不需要使用 &data - Ben
所以,memcpy(buffer, &data, sizeof(data)); 不起作用。缓冲区中没有任何内容。 - Punit
@Ben 如果目标缓冲区比复制到其中的内容更小,那么这将是一个严重的错误,实际上问题已经被编辑以解决这个问题。 - Jim Balter
@Punit 缓冲区是目标--如果您定义了struct data data = {...},则memcpy(buffer &data, sizeof(data))肯定有效。无论如何,给出的答案是错误的,需要进行编辑。 - Jim Balter
请编辑第一行 memcpy(buffer, data, sizeof(data)),这个语句只有在data是char[]类型时才正确,但在这里它不是。 - Jim Balter

7
在您对此答案的评论中,您透露了一个XY问题——您正在询问如何实现解决方案的想法,而不是如何解决问题。您实际上并不想将结构体复制到缓冲区,而是想使用需要char *参数的API将结构体中的数据写入Flash。
其他人已经指出memcpy可以实现您想要的功能。但它们都涉及在RAM中为该缓冲区分配内存。使用这些方法将起作用,但会浪费时间和内存。假设您的Flash API有以下函数:
int WriteBlock(int address, char *data, int length);

如果您已经将结构体存储在名为data_var的变量中,您可以像使用memcpy一样使用API:
int success = WriteBlock(my_address, (char *) &data_var, sizeof(struct data)

为了更加清晰避免出现异常,您可以将其封装在一个联合体中:
union {
    struct data{
        int num1;
        int num2;
        int num3;
        int num4;
    };
    char data_arr[sizeof(struct data)];
};

这将允许您以更传统的语法调用它。
请注意,如果您有一个非连续的结构体,那么这两种方法(以及memcpy!)都可能失败。 例如,您的结构可能类似于:
struct data{
 int   num1;
 char  num2;
 long  num3;
 short num4;
}

如果你的系统不是8位系统,这样的结构可能会包含间隙(取决于你的架构)。例如,假设num1为0x12345678,num2为0x9A,num3为0xBCDEF0123456789A,num4为0xBCDE,那么你可能有以下任何一种情况(假设你的内存已经初始化为零,并且为清晰起见,使用大端字节序):
    /* 8位 */
    0x1234 5678 9ABC DEF0 1234 5678 9ABC DE00
    /* 16位 */
    0x1234 5678 009A BCDE F012 3456 789A BCDE
    /* 32位 */
    0x1234 5678 0000 0000 0000 009A BCDE F012 3456 789A 0000 0000 0000 BCDE
在这种情况下,你必须使用更丑陋的东西,比如下面的函数:
int fillDataBuffer(struct data d, char *buffer, int len) 
{
  int i, j = 0;

  for (i = sizeof(d.num1) - 1; i >= 0 && j < len; i--, j++) {
    buffer[j] = (char) (d.num1 >> i); 
  }
  for (i = sizeof(d.num2) - 1; i >= 0 && j < len; i--, j++) {
    buffer[j] = (char) (d.num2 >> i); 
  }
  for (i = sizeof(d.num3) - 1; i >= 0 && j < len; i--, j++) {
    buffer[j] = (char) (d.num3 >> i); 
  }
  for (i = sizeof(d.num4) - 1; i >= 0 && j < len; i--, j++) {
    buffer[j] = (char) (d.num4 >> i); 
  }

  if (j >= len) {
    /* Error!  The buffer wasn't big enough. */
    return 0;
  } else {
    return 1;
  }
}

或者使用#pragma pack()宏,但这可能会使使用结构体的计算变慢,您可能更愿意在进行缓冲时进行打包。

感谢您提供问题的名称!我经常在SO和其他地方看到这种情况...因此我在我的答案中说我质疑OP真正想要做什么,这导致了您对实际需求的回答。 :-) - Jim Balter
提到一下可能有帮助的是,对于从闪存API直接读取地址范围,该地址范围必须被锁定在内存中,或者该地址范围不可分页。 - lsk

1
首先,在右括号后需要加上分号。其次,你应该使用typedef。第三,你应该大写用户类型(有些人建议在类型后面加上_t,但实际上这违反了C标准,因为这样的名称是保留的)。第四,你需要一个该类型的值来复制。考虑到所有这些:
typedef struct{
 int num1;
 int num2;
 int num3;
 int num4;
} Data;

Data foo = { 1, 2, 3, 4};
char buffer[sizeof foo]; // at least

memcpy(buffer, &foo, sizeof foo);

然而,我会质疑你为什么要将一个结构体复制到一个字符缓冲区中。你应该说明你实际想要实现什么,因为很有可能有更好的方法来解决问题。


1
我正在尝试将这个结构体复制到闪存中。而闪存读写API传递的是一个字符串。 - Punit
@Punit - 那么你正在解决错误的问题!你不是真正想要将数据复制到RAM缓冲区,然后再复制到Flash中,而是想通过字符串API传递结构中的数据。我在我的答案中发布了一些实现此目的的选项。 - Kevin Vermeer

1

使用memcpy函数可以将整个结构体复制到缓冲区中。 例如,在以下代码中,'ob1'的内容被复制到'buffer'中:

 #include <stdio.h>
 #include <stdlib.h>      
 #include <string.h>

    struct data{
        int num1;
        int num2;
        int num3;
        int num4;
    };
    int main()
    {
        struct data ob1;
        struct data *buffer = (struct data *)malloc(sizeof(struct data));
        if(buffer == NULL)
            {
               printf("Memory allocation failed\n");
               return;
            }

        ob1.num1 = 1;
        ob1.num2 = 2;
        ob1.num3 = 3;
        ob1.num4 = 4;

        memcpy(buffer, &ob1, sizeof(struct data));

    }

我理解这个工作原理,但是在结构数据中有一个char名称组件的情况下,我们该如何处理呢?将其复制到缓冲区,然后能够从缓冲区中提取信息并还原到结构体中吗?我创建的缓冲区是char类型的,而不是struct类型的缓冲区。谢谢。 - mmm

0
上面的答案只有在结构体元素的大小都相同时才能起作用(假设使用32位CPU和编译器)。因此,int每个将是32位,但如果您的结构体包含char等int以外的元素,则编译器可能会将char填充到32位以进行数据对齐。
您可以尝试在结构体的每个元素上使用sprintf,并在将每个元素加载到char缓冲区时将每个元素强制转换为正确的大小。
或者,您可以通过增加索引来逐个加载缓冲区中的元素,并在每次写入缓冲区时按每个元素的正确字节数增加索引。虽然冗长,但如果资源稀缺且数据庞大,则可以说更有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接