状态标志是如何表示的,以及如何使用位“或”来处理位标志

How are state flags represented and how bitwise OR is used to work with bit flags?

本文关键字:标志 何使用 处理 何表示 表示 状态      更新时间:2023-10-16

如果我们打开一个文件进行读取,我们可以定义一个或多个状态标志,例如:ios::outios::out | iso::app

我读过比特OR,以及它如何"合并"两个比特集,

例如:1010|0111=1111

话虽如此,但我不明白当我们使用ifstream.open(filename, stateflagA | stateflagB | stateflagC)等方法时,它是如何在"幕后"工作的。

有人能详细说明这些州旗的内部工作原理及其记忆表示吗?

编辑:为了更加强调我试图理解的内容(如果有帮助的话),我假设open方法可以接收一个或多个状态标志作为签名中的独立参数,而不是由逐位or分隔,所以我想了解在组合多个标志时,逐位or是如何对这些状态标志进行操作以产生不同的最终状态的,因此,我只能对一个状态标志或一组状态标志使用一个参数。即:

ifstream.open(filename, stateflagA | stateflagB | stateflagC)

而非

ifstream.open(filename, stateflagA , stateflagB , stateflagC)

位标志的表示方式与所有整数值的表示方式完全相同。使它们成为"标志"的是程序对其值的解释

位标志用于小型值集的紧凑表示。为每个值分配一个位索引。将该索引处的位设置为1的所有整数解释为包括相应成员的集合。

考虑一个小例子:假设我们需要表示一组三种颜色——红色、绿色和蓝色。我们给红色赋值为零,给绿色赋值为1,给蓝色赋值为2。这对应于以下表示:

BINARY DECIMAL COLOR
------ ------- -----
   001       1  Red
   010       2  Green
   100       4  Blue

请注意,每个标志都是二的幂。这是二进制数的一个特性,它的单个位设置为1。以下是它在C++中的样子:

enum Color {
    Red   = 1 << 0
,   Green = 1 << 1
,   Blue  = 1 << 2
};

CCD_ 6是构造在位置n处的单个比特被设置为1的整数的标准方式。

有了这个表示,我们可以构建具有这些颜色的任何组合的集合:

BINARY DECIMAL COLOR
------ ------- -----
   001       1  Red
   010       2  Green
   011       3  Red+Green
   100       4  Blue
   101       5  Blue+Red
   110       6  Blue+Green
   111       7  Blue+Green+Red

当位运算发挥作用时:我们可以使用它们在单个运算中构造集合和检查成员关系。

例如,我们可以用|构造一组RedBlue,如下所示:

Color purple = Red | Blue;

在幕后,所有这些都是将5分配给purple,因为4 | 1就是5。但是,由于您的程序将5解释为两种颜色的集合,因此5的含义与整数5的含义不同,后者表示一个袋子中的东西数量。

您可以通过对集合应用&来检查集合是否具有特定成员:

if (purple & Red) {
    // returns true
}
if (purple & Green) {
    // returns false
}

I/O库使用的标志以相同的方式工作。一些标志被组合以产生位掩码。它们的工作方式与单个标志相同,但不是让你找到成员身份,而是让你在一个比特操作中找到集合交集:

Color yellow = Blue | Green;
Color purple = Red | Blue;
Color common = yellow & purple; // common == Blue

如果我们采用GNU libstdc++实现,看看它们是如何实际实现的,我们会发现:

enum _Ios_Openmode 
{ 
  _S_app        = 1L << 0,
  _S_ate        = 1L << 1,
  _S_bin        = 1L << 2,
  _S_in         = 1L << 3,
  _S_out        = 1L << 4,
  _S_trunc      = 1L << 5,
  _S_ios_openmode_end = 1L << 16 
};

然后这些值如下所示:

typedef _Ios_Openmode openmode;
static const openmode app =     _S_app;
/// Open and seek to end immediately after opening.
static const openmode ate =     _S_ate;
/// Perform input and output in binary mode (as opposed to text mode).
/// This is probably not what you think it is; see
/// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch27s02.html
static const openmode binary =  _S_bin;
/// Open for input.  Default for @c ifstream and fstream.
static const openmode in =      _S_in;
/// Open for output.  Default for @c ofstream and fstream.
static const openmode out =     _S_out;
/// Open for input.  Default for @c ofstream.
static const openmode trunc =   _S_trunc;

由于这些值被选择为1 << n,它们每个恰好是一个"位",这允许我们使用|(或)以及其他类似的操作进行组合。

所以二进制文件中的app0000 0001bin0000 0100,所以如果我们将app | bin作为打开文件的模式,我们得到0000 0101。实现fstream的内部可以使用

 if (mode & bin) ... do stuff for binary file ... 

 if (mode & app) ... do stuff for appending to the file ...

其他C++库实现可以为每个标志选择一组不同的位值,但将使用类似的系统。

"幕后",在计算机的内存中,每个信息最终都被编码为一组比特。您的CPU可以对这些基本信息执行基本的二进制代数运算(AND、OR、XOR、NOT)。

C++运算符CCD_ 29、CCD_ 30和CCD_。对于标志管理,明智的做法是使用无符号整数类型,如unsigned intunsigned char

概述:

  • 诀窍是每个标志都对应一个固定的位。这通常是用2的幂常数(例如:1,2,4,8,二进制编码为000100100100和1000)来完成的
  • 常数之所以命名,是因为它比使用乱丢垃圾更清晰(例如:const unsigned FlagA=1, FlagB=2, FlagC=4;
  • 二进制AND x & y确保只有在x和y中都为1的比特保持为1。因此,这是用来通过"安定"重置标志,其中标志为0。因此x & FlagB重置除标志B以外的所有标志
  • 二进制OR x | y在x或y中为1的任何位变为1。所以它被用来设置标志。示例:x | FlagB设置标志B
  • 二进制AND也是检查是否设置了标志的快速方法:当且仅当设置了标志B时,(x & FlagB)将为真

编辑:关于ifstream::open()参数的具体问题:这是一个设计选择,方便使用。正如您所看到的,有6个标志会影响文件的处理方式(其中一些很少使用)。因此,不是每次都提供6个标志中的每一个,而是标准决定在开放模式中组合提供它们。可变数量的参数不是一种选择,因为被调用的函数必须知道您提供了多少个参数。