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。
我正在尝试构建一个可以填充结构的通用函数 而无需任何动态内存分配。
以下代码是我正在尝试做的事情的一个简单示例。
此代码不会编译为 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。