如何在C语言中创建24位无符号整数

如何在C语言中创建24位无符号整数,c,struct,printf,typedef,C,Struct,Printf,Typedef,我正在开发一个内存非常紧张的嵌入式应用程序。 为此,我需要创建一个24位无符号整数数据类型。我使用的是结构: typedef struct { uint32_t v : 24; } uint24_t; 但是,当我询问此类型变量的大小时,它返回“4”,即: 有没有办法强制这个变量有3个字节 起初我认为这是因为它强制数据类型与单词对齐,但我可以这样做: typedef struct { uint8_t blah[3]; } mytype; 在这种情况下,尺寸为3 好吧,你可以尝试

我正在开发一个内存非常紧张的嵌入式应用程序。 为此,我需要创建一个24位无符号整数数据类型。我使用的是结构:

typedef struct
{
    uint32_t v : 24;
} uint24_t;
但是,当我询问此类型变量的大小时,它返回“4”,即:

有没有办法强制这个变量有3个字节

起初我认为这是因为它强制数据类型与单词对齐,但我可以这样做:

typedef struct
{
    uint8_t blah[3];
} mytype;
在这种情况下,尺寸为3

好吧,你可以尝试确保结构只占用你需要的空间,比如:

#pragma pack(push, 1)
typedef struct { uint8_t byt[3]; } UInt24;
#pragma pack(pop)
// Inline suggestion used to (hopefully) reduce overhead.

inline uint32_t unpack(UInt24 x) {
    uint32_t retVal = x.byt[0];
    retval = retVal << 8 | x.byt[1];
    retval = retVal << 8 | x.byt[2];
    return retVal;
}

inline UInt24 pack(uint32_t x) {
    UInt24 retVal;
    retVal.byt[0] = (x >> 16) & 0xff;
    retVal.byt[1] = (x >> 8) & 0xff;
    retVal.byt[2] = x & 0xff;
    return retVal
}
您可能必须提供这些编译器指令(如上面的
#pragma
行)以确保没有填充,但这可能是只有八位字段(a)的结构的默认设置

然后,您可能需要将实际值打包/解包到结构中,或从结构中打包/解包,例如:

#pragma pack(push, 1)
typedef struct { uint8_t byt[3]; } UInt24;
#pragma pack(pop)
// Inline suggestion used to (hopefully) reduce overhead.

inline uint32_t unpack(UInt24 x) {
    uint32_t retVal = x.byt[0];
    retval = retVal << 8 | x.byt[1];
    retval = retVal << 8 | x.byt[2];
    return retVal;
}

inline UInt24 pack(uint32_t x) {
    UInt24 retVal;
    retVal.byt[0] = (x >> 16) & 0xff;
    retVal.byt[1] = (x >> 8) & 0xff;
    retVal.byt[2] = x & 0xff;
    return retVal
}

不过,这只是一个示例,因此,您可能需要考虑到“便携代码”,使用诸如“<代码> >”PrimaMaPa(1)之类的代码,或者放入代码来捕获可能并非如此的环境。

< P>,您可以尝试确保该结构只占用您所需的空间,例如:

#pragma pack(push, 1)
typedef struct { uint8_t byt[3]; } UInt24;
#pragma pack(pop)
// Inline suggestion used to (hopefully) reduce overhead.

inline uint32_t unpack(UInt24 x) {
    uint32_t retVal = x.byt[0];
    retval = retVal << 8 | x.byt[1];
    retval = retVal << 8 | x.byt[2];
    return retVal;
}

inline UInt24 pack(uint32_t x) {
    UInt24 retVal;
    retVal.byt[0] = (x >> 16) & 0xff;
    retVal.byt[1] = (x >> 8) & 0xff;
    retVal.byt[2] = x & 0xff;
    return retVal
}
您可能必须提供这些编译器指令(如上面的
#pragma
行)以确保没有填充,但这可能是只有八位字段(a)的结构的默认设置

然后,您可能需要将实际值打包/解包到结构中,或从结构中打包/解包,例如:

#pragma pack(push, 1)
typedef struct { uint8_t byt[3]; } UInt24;
#pragma pack(pop)
// Inline suggestion used to (hopefully) reduce overhead.

inline uint32_t unpack(UInt24 x) {
    uint32_t retVal = x.byt[0];
    retval = retVal << 8 | x.byt[1];
    retval = retVal << 8 | x.byt[2];
    return retVal;
}

inline UInt24 pack(uint32_t x) {
    UInt24 retVal;
    retVal.byt[0] = (x >> 16) & 0xff;
    retVal.byt[1] = (x >> 8) & 0xff;
    retVal.byt[2] = x & 0xff;
    return retVal
}

不过,这只是一个示例,因此,您可能需要考虑到“便携代码”,使用诸如“<代码> >”PraceMaPa包(1)之类的代码,或者放入代码来捕获可能不是这种情况的环境。

< P> ON说,您可以使用<代码>另一个选项是使用
\uuuuuuuuuuuuuu属性((打包))

这应该适用于GCC和Clang

但是,请注意,这可能会破坏对齐,除非您的处理器支持未对齐的访问。

上说您可以使用
\pragma pack
。另一个选项是使用
\uuuuuuuuuuuuuu属性((打包))

这应该适用于GCC和Clang

但是,请注意,这可能会破坏对齐,除非您的处理器支持未对齐的访问

起初我认为这是因为它迫使数据类型与单词对齐

不同的数据类型可以有不同的对齐方式。例如,参见文档

您可以使用
alignof
进行检查,但是
char
uint8\u t
具有1字节对齐(即,实际上没有)是完全正常的,但是
uint32\u t
具有4字节对齐。我不知道是否明确描述了位字段的对齐方式,但从存储类型继承它似乎足够合理

注意。具有对齐要求的原因通常是,它与底层硬件配合得更好。如果您确实使用了
#pragma pack
uuuu属性((packed))
或其他任何东西,那么编译器或内存硬件会以静默方式处理未对齐的访问,这可能会影响性能

在我看来,仅仅显式存储一个3字节数组可能更好

起初我认为这是因为它迫使数据类型与单词对齐

不同的数据类型可以有不同的对齐方式。例如,参见文档

您可以使用
alignof
进行检查,但是
char
uint8\u t
具有1字节对齐(即,实际上没有)是完全正常的,但是
uint32\u t
具有4字节对齐。我不知道是否明确描述了位字段的对齐方式,但从存储类型继承它似乎足够合理

注意。具有对齐要求的原因通常是,它与底层硬件配合得更好。如果您确实使用了
#pragma pack
uuuu属性((packed))
或其他任何东西,那么编译器或内存硬件会以静默方式处理未对齐的访问,这可能会影响性能


我认为,只显式存储3字节数组可能更好。

首先,不要使用位字段或结构。它们可以随意包含填充,位字段通常不可移植

除非您的CPU显式地得到24位算术指令(除非是某个古怪的DSP,否则这似乎不太可能),否则自定义数据类型只能实现额外的堆栈膨胀

最有可能的情况是,所有算术运算都必须使用
uint32\t
。这意味着您的24位类型在节省RAM方面可能没有多大成就。如果您发明了一些具有setter/getter(序列化/反序列化)访问权限的定制ADT,那么您可能只是在浪费RAM,因为如果函数不能内联,那么您将获得更高的堆栈峰值使用率

为了实际节省RAM,您应该修改您的程序设计


也就是说,您可以基于数组创建自定义类型:

typedef unsigned char u24_t[3];
每当您需要访问数据时,您可以
memcpy
32位类型的数据,然后在32位上执行所有算术运算:

u24_t    u24;
uint32_t u32;
...
memcpy(&u32, u24, sizeof(u24));
...
memcpy(&u24, &u32, sizeof(u24));

但请注意,这假设的是小端,因为我们只处理位0到2。对于大端系统,您必须执行
memcpy((uint8_t*)和u32+1,
以丢弃MS字节。

首先,不要使用位字段或结构。它们可以随意包含填充,并且位字段通常不可移植

除非您的CPU显式地得到24位算术指令(除非是某个古怪的DSP,否则这似乎不太可能),否则自定义数据类型只能实现额外的堆栈膨胀

最有可能的情况是,您必须对所有算术使用
uint32\u t
。这意味着您的24位类型在节省RAM方面可能没有多大成就。如果您发明了一些带有setter/ge的自定义ADT