C - 在没有 malloc 的函数中填充通用结构

C - Populate a generic struct inside a function without malloc

我正在尝试构建一个可以填充结构的通用函数 而无需任何动态内存分配

以下代码是我正在尝试做的事情的一个简单示例。 此代码不会编译为 incomplete type 'void' is not assignable

请注意,这是一个用来突出我的问题的玩具示例。我真的不想转换颜色;我只想强调这些结构在数据类型和大小方面会有所不同。

#include <stdio.h>

typedef struct {
    int r;
    int g;
    int b;
} rgb_t;

typedef struct {
    float c;
    float m;
    float y;
    float k;
} cmyk_t;

typedef enum { RGB, CMYK } color_t;

void convert_hex_to_color(long hex, color_t colorType, void* const out) {
    if (colorType == RGB) {
        rgb_t temp = { 0 };
        // Insert some conversion math here....
        temp.r = 1;
        temp.g = 2;
        temp.b = 3;
        *out = temp; //< [!]
    } else
    if (colorType == CMYK) {
        cmyk_t temp = { 0 };
        // Insert some conversion math here....
        temp.c = 1.0;
        temp.m = 2.0;
        temp.y = 3.0;
        temp.k = 4.0;
        *out = temp; //< [!]
    }
}

int main(void) {
    // Given
    long hex = 348576;
    rgb_t mydata = { 0 };
    convert_hex_to_color(hex, RGB, (void*)(&mydata));

    // Then
    printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
    return 0;
}

对于一些额外的上下文,我在嵌入式系统目标上使用 C11。

最好的[1]方法是什么?宏观?联盟?

此致,
加布里埃尔

[1] 我将 "best" 定义为可读性和安全性之间的良好折衷。

错误原因是通过void指针存储无效:编译器不知道要存储什么。您可以将指针转换为 *(rgb_t *)out = temp;*(cmyk_t *)out = temp;

或者,您可以将 temp 定义为指向适当结构类型的指针,并直接从 out 初始化它,而无需 C:

中不需要的强制转换
void convert_hex_to_color(long hex, color_t colorType, void *out) {
    if (colorType == RGB) {
        rgb_t *temp = out;
        // Insert some conversion math here....
        temp->r = 1;
        temp->g = 2;
        temp->b = 3;
    } else
    if (colorType == CMYK) {
        cmyk_t *temp = out;
        // Insert some conversion math here....
        temp->c = 1.0;
        temp->m = 2.0;
        temp->y = 3.0;
        temp->k = 4.0;
    }
}

请注意,C:

中不需要强制转换
int main(void) {
    // Given
    long hex = 348576;
    rgb_t mydata = { 0 };
    convert_hex_to_color(hex, RGB, &mydata);

    // Then
    printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
    return 0;
}
rgb_t temp = {0};

这样就在rgb_t类型的栈上声明了一个变量。到目前为止一切顺利,尽管您不需要那个 0

*out = temp;

这是你的问题:在 C 中你只能复制相同类型的内存。曾经。这与malloc无关,正如你的标题所暗示的,这只是基本的语言规范。当然,某些类型提供隐式转换,但 void* 不是其中之一。

因此,如果您要复制一个结构(rgb_t 在右侧),目标 必须是同一类型 。所以将行更改为:

*(rgb_t *)out = temp;

框架挑战:您似乎想根据传递给此函数的类型执行不同的操作。与其使用枚举来告诉它你传入的是什么类型,并根据该枚举进行分支,不如使用 C11 的 _Generic 来处理它,你甚至不需要明确地告诉它什么类型在每次通话中:

#include <stdio.h>

typedef struct {
    int r;
    int g;
    int b;
} rgb_t;

typedef struct {
    float c;
    float m;
    float y;
    float k;
} cmyk_t;

inline void convert_hex_to_color_rgb(long hex, rgb_t *const out) {
    (void) hex; // or whatever you're planning to do with 'hex'
    out->r = 1;
    out->g = 2;
    out->b = 3;
}

inline void convert_hex_to_color_cmyk(long hex, cmyk_t *const out) {
    (void) hex; // or whatever you're planning to do with 'hex'
    out->c = 1.0;
    out->m = 2.0;
    out->y = 3.0;
    out->k = 4.0;
}

#define convert_hex_to_color(hex, out) _Generic((out), \
        rgb_t *: convert_hex_to_color_rgb((hex), (rgb_t *)(out)), \
        cmyk_t *: convert_hex_to_color_cmyk((hex), (cmyk_t *)(out)) \
)

int main(void) {
    // Given
    long hex = 348576;
    rgb_t mydata = { 0 };
    cmyk_t mydatac = { 0 };
    convert_hex_to_color(hex, &mydata);
    convert_hex_to_color(hex, &mydatac);

    // Then
    printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
    printf("CMYK = %f,%f,%f,%f\r\n", mydatac.c, mydatac.m, mydatac.y, mydatac.k);
    return 0;
}

"best" 方法是不要在同一个函数或同一个内存区域中混合不相关的结构。那只是乱七八糟的设计。

如果您需要对两种不同形式的数据保持一致 API,那么类型安全的类似函数的宏可能是一种想法。您可以伪造这样一个宏,使其具有类似于通过指针传递数据的语法

void convert_hex_to_color(long hex, type* data)

但随后使用 C11 _Generic 实际确定要使用的正确类型,而不是使用危险的 void 指针。由于您不能将参数 "by reference" 传递给宏,因此您必须在其中偷偷进行变量赋值。示例:

#include <stdio.h>

typedef struct {
    int r;
    int g;
    int b;
} rgb_t;

typedef struct {
    float c;
    float m;
    float y;
    float k;
} cmyk_t;



void convert_hex_to_color(long hex, void* data);
  /* 
     Pretty prototype just for code documentation purposes. 
     Never actually defined or called - the actual macro will "mock" this function. 
  */

#define convert_hex_to_color(hex, output) ( *(output) = _Generic(*(output), \
  rgb_t:  (rgb_t){ .r=1, .g=2, .b=3 }, \
  cmyk_t: (cmyk_t){ .c=1.0, .m=2.0, .y=3.0, .k=4.0 } ) )


int main(void) {
    // Given
    long hex = 348576;
    rgb_t  myrgb  = { 0 };
    cmyk_t mycmyk = { 0 };

    convert_hex_to_color(hex, &myrgb);
    convert_hex_to_color(hex, &mycmyk);

    printf("RGB  = %i,%i,%i\r\n", myrgb.r, myrgb.g, myrgb.b);
    printf("CMYK = %f,%f,%f,%f\r\n", mycmyk.c, mycmyk.m, mycmyk.y, mycmyk.k);
    return 0;
}

输出:

RGB  = 1,2,3
CMYK = 1.000000,2.000000,3.000000,4.000000

请注意 _Generic 对类型限定符(const 等)的支持在 C11 中不稳定 - 一些 C11 编译器对待 const rgb_t 不同于 rgb_t,其他对他们一视同仁。这是 C17 中的 "bug fixes" 之一,因此如果可用,请使用 C17。