Bootstrap

C语言中两个不同类型的结构体相互幅值

在 C 语言中,如果两个结构体的定义(字段名称、数量、顺序、类型)不一样,就不能直接使用 = 运算符进行整体赋值,需要逐个字段进行赋值或者通过其它手段进行“转换”。


1. 字段一一对应赋值

1.1 手动逐个赋值

假设有如下两个结构体:

typedef struct 
{
    int  id;
    char name[20];
    int  age;
} StuA;

typedef struct
{
    int  stuId;
    char stuName[20];
    int  birthYear;
} StuB;

如果想把 StuA 类型变量中的信息赋值给 StuB 类型变量,只能手工逐个字段对照相应含义进行赋值。例如:

void convertAtoB(const StuA *src, StuB *dst)
{
    // 1) 把 src->id 赋给 dst->stuId
    dst->stuId = src->id;
    
    // 2) 把 src->name 字符数组复制到 dst->stuName
    //    可以用 strcpy,memcpy 等,但要注意数组长度、是否有字符串结束符 '\0' 等
    strcpy(dst->stuName, src->name);
    
    // 3) 根据需求,假设 age 和 birthYear 之间存在一定关系 
    //    可能需要额外处理,如:birthYear = 当前年份 - age
    //    这里仅做简单举例
    dst->birthYear = 2025 - src->age;
}

然后在调用处:

StuA a = {1001, "Alice", 20};
StuB b;

convertAtoB(&a, &b);

// 测试打印
printf("b.stuId = %d\n", b.stuId);
printf("b.stuName = %s\n", b.stuName);
printf("b.birthYear = %d\n", b.birthYear);

特点

  • 完全可控,每个字段怎么赋值都可以自己定规则(例如做一些计算、类型转换等)。
  • 写法简单明了,但需要维护多行代码,且结构体字段越多,越繁琐。

1.2 结合结构体内不同字段做适配

有时字段名称或类型并不完全对应,需要在转换时做一些逻辑处理。上面已经演示了把 age 转成 birthYear。这种场景是没有直接通用的“整体复制”方法的,只能手写适配逻辑。


2. 如果结构体布局“完全相同”但名字不同

如果两个结构体字段布局完全一致(包括类型、数量、对齐方式等),只是结构体名字不同,在某些情况下,可以通过 memcpy 来“复制”内存内容。

举个例子:

typedef struct
{
    int  x;
    float y;
} MyStructA;

typedef struct
{
    int  x;
    float y;
} MyStructB;

此时它们的内存布局其实是一模一样的,仅名字不一样。可以用:

#include <string.h>

MyStructA a = {1, 2.5f};
MyStructB b;

memcpy(&b, &a, sizeof(b)); // 或 sizeof(a) 因为大小一样

然后 b.xb.y 分别与 a.xa.y 值相同。但需要特别注意:如果两个结构体字段类型、对齐方式不一致,或者我们需要对字段进行特殊处理,这种做法就会出问题。


3. 不同结构体之间数组相互幅值

在 C 语言中,如果只是想将两个结构体中的数组相互赋值(拷贝数据),可以使用 `memcpy` 或逐元素拷贝。下面介绍两种常见方式。


假设结构体定义

举例说明,假设我们有如下两个结构体,都含有一个长度相同的数组:

#include <stdint.h>

typedef struct
{
    uint8_t arrA[10];
    // ... 其他字段
} StructA;

typedef struct
{
    uint8_t arrB[10];
    // ... 其他字段
} StructB;

并且有对应的结构体变量:

StructA sA;
StructB sB;

现在您只想把 sA 中的数组 arrA 拷贝到 sB 中的数组 arrB


方法一:使用 memcpy 拷贝数组

如果您确定两个数组长度相同,且确实要把所有字节都复制过去,那么最简洁的做法是:

#include <string.h>  // memcpy 所在头文件

memcpy(sB.arrB, sA.arrA, sizeof(sB.arrB));
  • sB.arrB: 目标数组的首地址

  • sA.arrA: 源数组的首地址

  • sizeof(sB.arrB): 要复制的数据大小(单位:字节)

这样就完成了从 sA.arrAsB.arrB 的数据拷贝。

关键注意事项

  1. 数组长度需相同
    如果两边数组长度不一致,建议使用 sizeof(最短的数组) 或者确保不会越界访问。

  2. memcpy 与字符串
    如果数组保存的是字符串,需要您手动保证目标数组中是否需要末尾 '\0',或者使用 strcpy(适合以 '\0' 结尾的 C 字符串)。但如果只是普通的数据(字节流),memcpy 就更合适。

  3. memcpy 只做纯字节复制
    它不会考虑任何类型转换或深拷贝结构,只是一份内存到另一份内存的原样复制。


方法二:逐元素拷贝(for 循环)

如果想在复制过程中做一些额外操作、检查或转换,或者两边数组大小并不一样,我们可以使用 for 循环逐元素拷贝。例如:

void copyArray(const StructA *source, StructB *dest, uint8_t length)
{
    // 这里 length 可以是两个数组中的最小长度,以避免越界
    for (uint8_t i = 0; i < length; i++)
    {
        dest->arrB[i] = source->arrA[i];
    }
}

然后调用时:

copyArray(&sA, &sB, 10); // 假设要拷贝 10 个字节

关键注意事项

  1. 可控性
    在循环中我们可以加判定逻辑、打印、或做任何数据变换,而不必再写额外的循环代码。

  2. 不用担心结构体中其它字段
    只要针对目标数组和源数组进行处理就行,不会影响到结构体的其它部分。


总结

  1. 结构体字段不一样时,必须逐个字段赋值或使用适配函数,把源结构体的各字段对应到目标结构体的字段。
  2. 如果内存布局一模一样,只是名字不同,可以通过 memcpy 整体复制,但是要非常确定二者的字段定义、对齐方式、大小都相同。
  3. 如果两个结构体中数组大小完全相同,且只想做简单的字节复制,推荐使用 `memcpy`
  4. 如果需要额外逻辑处理,或者数组大小不一致,或想逐字节检查,可以使用 for 循环 逐元素赋值。

在大多数场景下,手动一一对应字段仍是最常见且安全的做法。

;