C++ 我可以安全地用公共头中的未签名字符替换bool成员吗?

C++ 我可以安全地用公共头中的未签名字符替换bool成员吗?,c++,C++,我提供了一个公共头,用户根据它编译,以利用其中包含的类。我需要修复实现这些公共类之一的共享库中的一个问题,并要求现有用户在安装修补程序时无需重新编译其库。修复要求bool成员能够保存比false、true更多的信息。 这个班看起来像 class PublicAPI{ ... public: bool getSync(); void setSync(bool sync); ... private bool sync_; ... }; 我想更改bool-sync\ux;到无符号字符同步

我提供了一个公共头,用户根据它编译,以利用其中包含的类。我需要修复实现这些公共类之一的共享库中的一个问题,并要求现有用户在安装修补程序时无需重新编译其库。修复要求bool成员能够保存比false、true更多的信息。 这个班看起来像

class PublicAPI{
...
public:
  bool getSync();
  void setSync(bool sync);
...
private
  bool sync_;
...
};
我想更改
bool-sync\ux;到无符号字符同步

class PublicAPI{
...
public:
  bool getSync();
  void setSync(bool sync);
...
private
  unsigned char sync_;
...
};
注意,能手和二传手不会,我认为他们也不会改变。他们会没事的,因为用户不需要知道sync_uuu包含的附加值,我将处理在内部保留我的值。显然,这是一个丑陋的黑客行为,但我认为技术上这是可以的,因为类的大小不会改变

我担心的是,我在Linux、AIX、HPUX、Solaris和Windows上提供的平台之一上的编译器会使用我不知道的bool类型玩一些游戏,并破坏某些东西


有没有想过这有多安全或危险?

我敢肯定,从技术上讲,您会调用未定义的行为。然而,我认为在实际应用中,没有编译器会进行这种更改。

我非常确定,从技术上讲,您会调用未定义的行为。然而,我认为在实际应用中,没有编译器会加入这种更改。

如果sizeof(bool)=sizeof(char),因此类的大小将不同。如果您有一个使用旧头编译的模块,并且有一个新的二进制文件,那么这可能会给您带来麻烦。 为了避免编译问题,只需确保您可以进行强制转换,并且应该是正常的。

if sizeof(bool)=sizeof(char),因此类的大小将不同。如果您有一个使用旧头编译的模块,并且有一个新的二进制文件,那么这可能会给您带来麻烦。
为了避免编译问题,只需确保您进行了强制转换并且应该是正常的。

我认为没有一种方法可以保证安全地执行您想要的操作。对于
bool
unsigned char
,某些编译器可能具有不同的大小

在大多数编译器上,这种情况似乎不太可能发生,您可能会侥幸逃脱。当然,最好是“建议”重新编译,但允许它在不重新编译的情况下工作


这是一个完美的例子,在这个例子中,pimpl’s实现可以消除任何问题,因为内部类型将完全隐藏在库中,而不是公开。

我认为没有一种方法可以保证安全。对于
bool
unsigned char
,某些编译器可能具有不同的大小

在大多数编译器上,这种情况似乎不太可能发生,您可能会侥幸逃脱。当然,最好是“建议”重新编译,但允许它在不重新编译的情况下工作


这是一个完美的例子,在这个例子中,pimpl'ing您的实现可以消除任何问题,因为内部类型将完全隐藏在您的库中,而不是公开。

从技术上讲,它是未定义的行为,在实践中,它几乎 当然不行——函数名会被弄乱 (修饰)不同,链接器将无法找到 功能

在大多数此类情况下,解决方案是使用旧的 功能转发到新功能,例如

public:
    void setSync(bool sync);
    void setSync(int sync);
,通过
setSync(bool sync)
实现为:

void PublicAPI::setSync(bool sync)
{
    setSync(sync ? 1 : 0);
}
不要使setSync的新实现内联。(如果
旧的一个实际上是内联的,你完蛋了。)

从技术上讲,它是未定义的行为,实际上,它几乎是 当然不行——函数名会被弄乱 (修饰)不同,链接器将无法找到 功能

在大多数此类情况下,解决方案是使用旧的 功能转发到新功能,例如

public:
    void setSync(bool sync);
    void setSync(int sync);
,通过
setSync(bool sync)
实现为:

void PublicAPI::setSync(bool sync)
{
    setSync(sync ? 1 : 0);
}
不要使setSync的新实现内联。(如果
以前的一个实际上是内联的,你被搞砸了。)

我不知道GCC,但很久以前我用MSVC 6做了一些类似的攻击(大多数攻击都是用指针替换DWORD)。工作很好。 当然,正如许多其他人已经写过的,sizeof(无论你使用什么)必须等于sizeof(bool)。 不过这不应该是个问题,因为您可以随时使用

class PublicAPI{
...
public:
  bool getSync();
  void setSync(bool sync);
...
private
  unsigned char sync_[sizeof(bool)];
...
};

然后在内部使用
sync\u0]

我不知道GCC,但很久以前我用MSVC 6做过一些类似的攻击(大多数攻击都是用指针替换DWORD)。工作很好。 当然,正如许多其他人已经写过的,sizeof(无论你使用什么)必须等于sizeof(bool)。 不过这不应该是个问题,因为您可以随时使用

class PublicAPI{
...
public:
  bool getSync();
  void setSync(bool sync);
...
private
  unsigned char sync_[sizeof(bool)];
...
};

然后在内部使用
sync\u0]

最安全的方法是使用
bool
无符号字符的
联合。当
bool
大于
char
时,该并集将像bool一样对齐,但仍包含
char


bool
char
都是POD类型,所以没什么大不了的。所有访问都是通过两个非内联成员进行的(您确定它们不是内联的?即未在类中定义?),这意味着您总是通过
char
成员访问联合体。

最安全的方法是使用
bool
unsigned char
联合体。当
bool
大于
char
时,该并集将像bool一样对齐,但仍包含
char


bool
char
都是POD类型,所以没什么大不了的。所有访问都是通过两个非内联成员进行的(您确定它们不是内联的?即,未在类中定义?),这意味着您总是通过
char
成员访问union。

如果您可以这样编程,您根本不需要更改头文件:

#include <iostream>
using namespace std ;

static bool b ;
int main()
  {
  unsigned char u0 = 99 ;
  *reinterpret_cast<unsigned char*>(&b) = u0 ;
  unsigned char u1 = *reinterpret_cast<unsigned char*>(&b) ;
  cout << int(u1) << endl ;
  }