两种方案,其他答主都已经提到过了,一种是位运算,辅之以一系列宏;还有一种是使用位段。

使用位运算 + 宏的方式,代码比较难读,但是对于某些操作,使用位运算代码可以很简洁。位段则是 C 语言提供的语法糖,本质上还是位运算,只不过编译器帮你写好表达式。使用位段的话,取值和赋值都比位运算的方式明了的多。但是题主又不想使用有点号的二级访问方式,下面有题主回答了,用 C++ 里的类封装,不过这样就是一个 bit 的变数,占一个 byte 了,那既然这样为什么不直接用原生的 bool 类型?

无论上面哪种方式,归根到底还是把 1 byte 拆成 8 bit 用。这样做是为了效率。支持主流机器架构的主流语言都是这样搞的,你看 C++ 和 Java 的布尔类型,都是只用到 1 个 bit 就够了,但却还是占了 1 byte 的存储空间。我猜 ST 语言里,它所谓支持定义 1bit 的变数只是语法糖而已,本质上还是几个 bit 变数挤一挤,再用位运算来操作每个 bit。如果不是这样的话,这个语言给我的感觉就很非主流。

如果题主需要的是 C 语言和 ST 语言进行底层交互的话,那么每个 bit 肯定得要固定在某个位置的,那么用位运算去自己手动控制对齐。如果题主只是想把一种语言的代码翻译成另一种语言的话,直接用 bool 就好


原来的回答误读了题主的问题。

题主要把下面这段代码翻译成C代码

VAR_GLOBAL

gvar AT %mb0.1 : bool;

gvar1 AT %mb0.2 : bool;

END_VAR

我来试一试。

union{

uint32_t mb0;

struct{

int gvar0:1;

int gvar1:1;

};

}gBitVar;

#define GLB_BIT_VAL(bName) gBitVar.bName

int main(void)

{

GLB_BIT_VAL(gvar0) = 1;

GLB_BIT_VAL(gvar1) = 0;

… …

… …

}

看上去友好一些了

在再理解一下位域结构的全局变数问题。

位域结构在全局变数中,如果没有明确的结构变数名,是不支持的。全局变数必须要能产生确定的地址,在编译时才能通过。而局部变数在编译时,能以寄存器变数方式存在,所以会支持那种方式的位域结构。

2019.7.18

下面是原来的回答。

======================================

在ARM的Cortex-M3/M4上底层支持,而且是真正的实现bit位原子操作。

STM32F3/F4或是TM4C,MSP432都支持bit-banding。它将32MB的SRAM内存按Bit映射到一块1MB的内存区域。

这意味著,你可以通过指定变数地址,来定义一个「bit型「的变数。虽然这个变数的类型应该是32位整型,但这个变数的地址的确只代表一个bit。

例如:

uint32_t bitVar __attribute__((at(0x22000000)));

//该变数bitVar只表示地址在0x2000000内存的bit-0位。

uint32_t bitArr[10] __attribute__((at(0x22000000)));

//定义了10个bit的数组。

bit位原子操作对I/O密集型应用很有好处。


一般编译器都不支持定义仅占一个位的变数,因为内存的最小存取单元也不能达到1bit的粒度,如果让每个1bit的变数都占一组内存对其的位元组数,那这种设计就太不「优雅」了,也没有什么意义。因为如果你真想为每1bit赋与不同的「意义」,你可以使用位的与或非等操作来达到,而且这样做还可以很「优雅」。如下面这段Linux内核中的代码:

/*
* Structure for FS_IOC_FSGETXATTR[A] and FS_IOC_FSSETXATTR.
*/
struct fsxattr {
__u32 fsx_xflags; /* xflags field value (get/set) */
....
....
};

/*
* Flags for the fsx_xflags field
*/
#define FS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
#define FS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
#define FS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
#define FS_XFLAG_APPEND 0x00000010 /* all writes append */
#define FS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
#define FS_XFLAG_NOATIME 0x00000040 /* do not update access time */
#define FS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
#define FS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
#define FS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
#define FS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
#define FS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */
#define FS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */
#define FS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */
#define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
#define FS_XFLAG_DAX 0x00008000 /* use DAX for IO */
#define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */
#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */

/* Transfer xflags flags to internal */
static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
{
unsigned long iflags = 0;

if (xflags FS_XFLAG_SYNC)
iflags |= EXT4_SYNC_FL;
if (xflags FS_XFLAG_IMMUTABLE)
iflags |= EXT4_IMMUTABLE_FL;
if (xflags FS_XFLAG_APPEND)
iflags |= EXT4_APPEND_FL;
if (xflags FS_XFLAG_NODUMP)
iflags |= EXT4_NODUMP_FL;
if (xflags FS_XFLAG_NOATIME)
iflags |= EXT4_NOATIME_FL;
....
....

对于位的使用其实很简单,一般是:

  • 一般是先有一个变数,用于存储每个位。如

unsigned int flags = 0;

  • 然后定义上面变数中每个位的意思(不一定要用上所有位)。如(并不拘泥与此定义):

#define FLAG_A (1&

  • 接著,使用位操作写/读每个位。如:

赋值/清除一个位:

flags |= FLAG_A; # set bit
flags = ~FLAG_A; # clear bit

赋值/清除多个位:

flags |= (FLAG_C | FLAG_E | FLAG_F); # set bits
flags = ~(FLAG_C | FLAG_E | FLAG_F); # clear bits

读(判断)一个位:

if (flags FLAG_A)
do_something();

读(判断)多个位中至少有一个位被置位:

if (flags (FLAG_A | FLAG_E))
do_something();

当然还有很多其它用法。在使用位操作时,一定要注意等号两边的变数/常量类型和边界,以及可能有的符号位,避免在项目过大后出现难以调试的问题。

最后附上一个例子:

#include &

#define FLAG_A (1&

编译执行:

# gcc -o mytest mytest.c -Wall
# ./mytest
0x1
0x0
0x34
Theres not FLAG A


更多内容请参阅:

醉卧沙场:README - 专业性文章及回答总索引


我的理解是你希望只定义一个变数,并且该变数只占用1bit的内存空间,这是没有办法做到的,内存访问地址最少也是8bit的整数倍,如果考虑内存对齐,在不同位宽的机器上整存整取造成的空间浪费也不少了。毕竟容量使用和间访效率也是需要权衡的。

但是如果你有多个位变数,例如你有32个flag,而你希望只用4个byte(理论上4个byte刚好利用所有的bit),C语言支持位操作,你确实可以把多个位变数通过位操作merge到一个位元组长或者字长的变数中去。定义一个宏,把位先移位,然后或或者与就可以了。代码很简单,也可以写得很规整,很多底层通讯协议就是这么写的,x86架构里有不少专用寄存器,像Flags寄存器,也就是PSW,也是这么操作的。底层通讯协议这么做是因为通讯成本高,处理成本低,充分利用各个位承载数据可以加速数据传输。

但是你要是知道的是,即便你用位操作实现了一个变数只占1bit的空间内存,但是背后的访问操作依然不是bit的级别的,更改或者获取它的值比处理位元组变数开销更大,因为你需要事先进行位运算。这就是所谓的处理效率和内存空间的权衡。


不同的平台不一样 。

在 大部分的8bit单片机中, 支持这样的变数定义。

但 标准C/C++中没有, 只能 按结构体这种方式定义

或者是按位操作。


补充: 好多人说的 STM32的 位操作,严格来说不算是可定义变数,这些都是连接到不同的寄存器进行操作,不能自己使用

而8位机中,可以直接定义这样的变数来使用,一般当做boolen。

例如 PIC单片机 中 ,可以用 bit a = 1 , b = 0;


推荐阅读:
相关文章