我可以安全地在C ++ 11中使用std :: string作为二进制数据吗?

时间:2013-11-03 20:22:09

标签: c++ c++11 stdstring

互联网上有几篇帖子建议您使用std::vector<unsigned char>或类似的二进制数据。

但我更倾向于使用std::basic_string变体,因为它提供了许多方便的字符串操作函数。和AFAIK一样,自C ++ 11以来,该标准保证了每个已知的C ++ 03实现已经完成的工作:std::basic_string将其内容连续存储在内存中。

乍一看,std::basic_string<unsigned char>可能是一个不错的选择。

但是,我不想使用std::basic_string<unsigned char>,因为几乎所有操作系统函数都只接受char*,因此需要进行显式转换。此外,字符串文字是const char*,所以每次我将字符串文字分配给我的二进制字符串时,我都需要显式转换为const unsigned char*,我也想避免这种情况。此外,读取和写入文件或网络缓冲区的功能同样接受char*const char*指针。

这会留下std::string,它基本上是std::basic_string<char>的typedef。

使用std::string二进制数据的唯一潜在问题(我可以看到)是std::string使用char(可以签名)。

charsigned charunsigned char有三种不同类型,char可以是无符号或已签名。

因此,当11111111b的实际字节值作为char返回时,如果要检查其值,则其值可以是std::string:operator[](如果{{1} }}是无符号的)或者它可能是“负面的”(如果255已签名,具体取决于你的数字代表)。

同样,如果要将实际字节值char显式附加到char,只需添加11111111b可能是实现定义的(甚至可以引发信号){} { 1}}已签名,std::string(char) (255)会话导致溢出。

那么,是否有一种安全的方法可以使char二进制安全?

§3.10/ 15声明:

  

如果某个程序试图通过以下某种类型之外的 glvalue 访问对象的存储值,则行为未定义:

     
      
  • [...]
  •   
  • 与对象的动态类型对应的有符号或无符号类型的类型
  •   
  • [...]
  •   
  • char或unsigned char类型。
  •   

如果我理解正确的话,似乎允许使用int指针来访问和操作char的内容,并使明确定义。它只是重新解释位模式为std::string,没有任何变化或信息丢失,后者即因为unsigned char*std::string和{中的所有位{1}}必须用于值表示。

然后我可以使用unsigned charchar内容的解释,作为一种以明确定义和可移植的方式访问和更改signed char范围内的字节值的方法,无论unsigned char本身的签名如何。

这可以解决因可能签名的unsigned char*而引起的任何问题。

我的假设和结论是否正确?

此外,对于所有实现,相同位模式(即std::string[0, 255])的char解释是否保证相同?换句话说,标准是否保证“通过char的眼睛看”,相同的位模式总是会导致相同的数值(假设一个字节中的位数相同)?

我可以安全地(即没有任何未定义或实现定义的行为)使用unsigned char*来存储和操作C ++ 11中的二进制数据吗?

3 个答案:

答案 0 :(得分:19)

其中static_cast<char>(uc)类型为uc的转化unsigned char始终有效:根据3.9.1 [basic.fundamental] char的表示,{{ 1}},signed charunsigned char相同,与其他两种类型相同:

  

声明为字符(char)的对象应足够大,以存储实现的基本字符集的任何成员。如果此组中的字符存储在字符对象中,则该字符对象的整数值等于该字符的单个字符文字形式的值。实现定义char对象是否可以保存负值。字符可以显式声明为unsigned或signed。 Plain char,signed char和unsigned char是三种不同的类型,统称为窄字符类型。 char,signed char和unsigned char占用相同的存储空间并具有相同的对齐要求(3.11);也就是说,它们具有相同的对象表示。对于窄字符类型,对象表示的所有位都参与值表示。对于无符号窄字符类型,值表示的所有可能位模式表示数字。这些要求不适用于其他类型。在任何特定实现中,普通char对象可以采用相同的方式   值作为signed char或unsigned char;哪一个是实现定义的。

char范围之外的值转换为unsigned char当然会产生问题,并可能导致未定义的行为。也就是说,只要您不尝试将有趣的值存储到char中,您就可以了。关于位模式,您可以依靠std::string位转换为2 n 。在仔细处理时,在n中存储二进制数据应该没有问题。

那就是说,我不会买入你的前提:处理二进制数据主要需要处理使用std::string值最佳操作的字节。您需要在unsignedchar*之间进行转换的少数情况会在未明确处理的情况下创建方便的错误,同时意外地使用unsigned char*将会保持沉默!也就是说,处理char可以防止错误。我也不会购买你得到所有那些好的字符串函数的前提:首先,你通常最好不要使用算法,但二进制数据字符串数据。总结:对unsigned char的建议不仅仅是凭空而来!故意避免难以在设计中找到陷阱!

支持使用std::vector<unsigned char>的唯一温和合理的论点可能是关于字符串文字的论点,但即便如此也没有用C ++ 11中引入的用户定义的字符串文字:

char

答案 1 :(得分:1)

是的,您的假设是正确的。 将二进制数据存储为std :: string中的unsigned char序列。

答案 2 :(得分:-1)

我在使用std :: string处理Microsoft Visual Studio中的二进制数据时遇到了麻烦。我已经看到字符串被莫名其妙地截断了,所以无论标准文件说什么,我都不会这样做。