简体   繁体   English

C - 在没有 malloc 的 function 内填充通用结构

[英]C - Populate a generic struct inside a function without malloc

I'm trying to build a generic function that can populate a struct without any dynamic memory allocation .我正在尝试构建一个通用的 function 可以填充没有任何动态 memory 分配的结构。

The following code is a naive example of what I'm trying to do.下面的代码是我正在尝试做的一个天真的例子。 This code will not compile as incomplete type 'void' is not assignable .此代码不会编译,因为incomplete type 'void' is not assignable

Please note that this is a toy example to highlight my problems.请注意,这是一个突出我的问题的玩具示例。 I don't really want to convert colours;我真的不想转换颜色; I just want to highlight that the structures will be different in data types and size.我只是想强调一下,这些结构的数据类型和大小会有所不同。

#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;
}

For some additional context, I'm using C11 on an embedded system target.对于一些额外的上下文,我在嵌入式系统目标上使用 C11。

What is the best[1] way to do this?最好的[1]方法是什么? Macro?宏? Union?联盟?

Regards,问候,
Gabriel加布里埃尔

[1] I would define "best" as a good compromise between readability and safety. [1] 我将“最佳”定义为可读性和安全性之间的良好折衷。

The reason for the error is it is invalid to store via a void pointer: the compiler does not know what to store.错误的原因是通过void指针存储是无效的:编译器不知道要存储什么。 You could cast the pointer as *(rgb_t *)out = temp;您可以将指针转换为*(rgb_t *)out = temp; or *(cmyk_t *)out = temp;*(cmyk_t *)out = temp;

Alternately, you could define temp as a pointer to the appropriate structure type and initialize it directly from out , without the cast that is not needed in C:或者,您可以将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;
    }
}

Note that the cast is not needed in C:请注意,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};

So that declares a variable on the stack of type rgb_t .这样就在rgb_t类型的堆栈上声明了一个变量。 So far so good, though you don't need that 0 .到目前为止一切顺利, 尽管您不需要 0

*out = temp;

Here is your problem: in C you can only copy memory of the same type.这是您的问题:在 C 中,您只能复制相同类型的 memory。 Ever.曾经。 This has nothing to do with malloc , as your title suggests, this is just the basic language specification.正如您的标题所示,这与malloc无关,这只是基本的语言规范。 Sure, some types provide implicit casts, but void* is not one of them.当然,某些类型提供隐式转换,但void*不是其中之一。

So if you're copying a structure ( rgb_t on the right side), the destination has to be of the same type .因此,如果您要复制结构(右侧的rgb_t ),则目标必须是相同的类型 So change the line to this:因此,将行更改为:

*(rgb_t *)out = temp;

The "best" way is not to mix unrelated structures in the same function, or in the same memory area for that matter. “最好”的方法是不要在同一个 function 或同一个 memory 区域中混合不相关的结构。 That's just messy design.那只是乱七八糟的设计。

If you need to keep a unison API for two different forms of data, then a typesafe function-like macro might be one idea.如果您需要为两个不同的 forms 数据保持一致的 API,那么一个类似类型安全函数的宏可能是一个想法。 You can fake such a macro to have a syntax similar to passing the data by pointer您可以伪造这样一个宏,使其具有类似于通过指针传递数据的语法

void convert_hex_to_color(long hex, type* data)

But then use C11 _Generic to actually determine the correct type to use, rather than using dangerous void pointers.但随后使用 C11 _Generic来实际确定要使用的正确类型,而不是使用危险的 void 指针。 Since you can't pass parameters "by reference" to macros, you'd have to sneak in a variable assignment in there.由于您不能“通过引用”将参数传递给宏,因此您必须在其中潜入变量赋值。 Example:例子:

#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;
}

Output: Output:

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

Just be aware that the _Generic support for type qualifiers ( const etc) was shaky in C11 - some C11 compilers treated const rgb_t different from rgb_t , others treated them the same.请注意,对类型限定符( const等)的_Generic支持在 C11 中是不稳定的 - 一些 C11 编译器处理const rgb_trgb_t不同,而其他编译器则相同。 This was one of the "bug fixes" in C17, so use C17 if available.这是 C17 中的“错误修复”之一,因此请使用 C17(如果可用)。

Frame challenge: It seems like you want to perform a different operation depending on the type that you pass into this function.帧挑战:您似乎想要执行不同的操作,具体取决于您传递给此 function 的类型。 Instead of using an enum to tell it what type you're passing in, and branching based on that enum, make use of C11's _Generic to handle that, and you don't even need to explicitly tell it what the type is on each call:而不是使用枚举来告诉它你传入的是什么类型,并基于该枚举进行分支,而是使用 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM