RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1268695
Accepted
megorit
megorit
Asked:2022-04-11 16:25:41 +0000 UTC2022-04-11 16:25:41 +0000 UTC 2022-04-11 16:25:41 +0000 UTC

std::string 和 std::vector<std::uint8_t> 的兼容性

  • 772

字节和字符之间的转换存在一个小问题。我想避免潜在的 UB,但我不确定它们是否会发生。

数据被读入std::vector<std::uint8_t>,然后,就我而言,必须分成两部分:其中一个始终是文本,第二个是文本或二进制。

我使用了一个特定的分隔符,例如,像这样:

std::string delimiter{"delimiter"};

auto delim_iter = std::search(
    byte_vector.begin(), byte_vector.end(),
    delimiter.begin(),   delimiter.end()
);

delim_iter += delimiter.size();

std::string char_part{byte_vector.begin(),delim_iter};

如您所知,char根据实现,它可以是signed, 或unsigned. 因此,我不清楚如何正确地做到这一点以避免出现问题。

例如,在使用时,std::transform建议传递一个 lambda,它需要unsigned char:

std::transform(
    string.begin(), string.end(), string.begin(),
    [](unsigned char ch) { return std::tolower(ch); }
);

这里有任何潜在的问题吗?能以某种方式解决吗?谢谢你。

c++
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    wololo
    2022-04-11T23:21:13Z2022-04-11T23:21:13Z

    任何整数类型都可以隐式转换为任何其他整数类型。

    1. 如果目标类型是bool,则将空值转换为false,将任何非空值转换为true。

    2. 如果目标类型是无符号整数类型,则使用模运算将该值转换为目标类型。

    3. 如果目标类型是有符号整数类型,并且要转换的值可以由目标类型表示,那么它不会被转换改变。如果要转换的值不能由目标类型表示,则

      • 在 C++20 之前,转换的结果是实现定义的。
      • 从 C++20 开始,使用模运算将值转换为目标类型。

    从 C++20 开始,您可以确定

    T1 v1 = ...;
    T2 v2 = v1; //Поведение однозначно определено.
    T3 v3 = v1; //Поведение однозначно определено.
    
    v1 == v2; //Поведение однозначно определено, результат либо `true`, либо `false`.
    T1(T2(v1)) == v1; //Поведение однозначно определено, результат `true`.
    T3(v1) == T3(v2); //Поведение однозначно определено, результат `true`.
    std::memcmp(&v1, &v2, 1) == 0; //Поведение однозначно определено, результат `true`.
    

    其中 typesT1和 是以下列表中的任何T2类型T3

    char, signed char, unsigned char, std::int8_t, std::uint8_t.

    上述陈述源于模算术的属性和整数类型的二进制补码表示的使用。

    在 C++20 之前,没有这样的保证。在不使用二进制补码有符号整数类型来表示的实现中,肯定应该违反上述某些陈述。但是要面对这样的实现是一项非常艰巨的工作。通常,在实践中,上述陈述得到满足。
    此外,在 C++20 之前,即使仍使用附加代码,标准仍将转换的结果留给(std::int8_t)255实现的摆布。但是,通常观察到的行为与 C++20 一致。


    综上所述,我们有

    auto delim_iter = std::search(
        byte_vector.begin(), byte_vector.end(),
        delimiter.begin(),   delimiter.end()
    );
    

    此代码不会生成未定义的行为。但是因为 由于该函数std::search()使用运算符来比较值==,因此搜索结果可能取决于类型是有char符号还是无符号。示例:

    std::vector<std::uint8_t> delimiter_1 = {128};  //Имитируем беззнаковый char
    std::vector<std::int8_t>  delimiter_2 = {-128}; //Имитируем    знаковый char
            
    //Сравниваем вектора побайтово - они равны
    cout << std::memcmp(&delimiter_1[0], &delimiter_2[0], sizeof(delimiter_1[0])) << endl; //0
    
    std::vector<std::uint8_t> byte_vector = {128};
    
    auto delim_iter_1 = std::search(
        byte_vector.begin(), byte_vector.end(),
        delimiter_1.begin(),   delimiter_1.end()
    );
    auto delim_iter_2 = std::search(
        byte_vector.begin(), byte_vector.end(),
        delimiter_2.begin(),   delimiter_2.end()
    );
    
    //Поиск с помощью std::search() даёт различные результаты
    cout << (delim_iter_1 - byte_vector.begin()) << "  " << (delim_iter_2 - byte_vector.begin()) << endl; //0 1
    

    如果您需要检查精确的逐字节对应关系,那么您可以使用search()采用二进制谓词的函数版本,在该版本中实现参数到某些常见类型的显式转换。例如,像这样:

    auto is_equal {
        [](std::uint8_t a, char b) {return static_cast<unsigned char>(a) == static_cast<unsigned char>(b);}
    };
    

    从 C++20 开始,以下代码的结果是明确的:

    std::string char_part{byte_vector.begin(),delim_iter};
    

    type 的值std::uint8_t将被转换为 type char。位模式不会随着这种转换而改变,它的解释方式会改变。反向转换 ( char-> std::uint8_t) 将恢复原始值。

    在 C++20 之前,它很可能也可以工作。


    这里

    std::transform(
        string.begin(), string.end(), string.begin(),
        [](unsigned char ch) { return std::tolower(ch); }
    );
    

    当 lambda 被调用时,它将被转换char为unsigned char. 然后调用std::tolower()将转换unsigned char为int.

    该函数std::tolower()有一个类型参数int。但是,不能将类型表示的任何值int传递给函数std::tolower()。参数必须等于EOF,或者必须可以按类型表示unsigned char。否则,函数的行为是未定义的。

    因为 传递了 type 的值unsigned char,那么应该没有任何问题。尽管该标准允许实现类型值的unsigned char范围比类型的非负值范围更宽的实现int。(例如,如果unsigned char和unsigned int具有相同的表示)。那些。理论上未定义的行为在这里是可能的。为了我自己的安心,我只想添加一个编译时检查:

    static_assert(
        std::numeric_limits<unsigned char>::max() <= 
        static_cast<unsigned int>(std::numeric_limits<int>::max())
    );
    

    调用的结果std::tolower()是类型int。标准对这个函数返回的值的范围不是很清楚,但是假设它是EOF(如果它EOF作为参数传递的情况下)或者由unsigned char.

    如前所述,转换unsigned char为charC++20 以来的结果是唯一定义的。在这种情况下,它转换int为char,但int存储了一个可表示的值unsigned char,因此结果是等价的。

    最后的转换链char-> unsigned char-> int-> char。在这种特殊情况下,如果您不考虑类型unsigned char和unsigned int具有相同内部表示的实现,那么应该没有问题。

    • 1
  2. megorit
    2022-04-11T19:19:26Z2022-04-11T19:19:26Z
    auto delim_iter = std::search(
        byte_vector.begin(), byte_vector.end(),
        delimiter.begin(),   delimiter.end(),
        [](const std::uint8_t lhs, const char rhs)
            { return (lhs == static_cast<std::uint8_t>(rhs)) }
    );
    
    delim_iter += delimiter.size();
    
    std::string char_part;
    char_part.reserve(std::distance(byte_vector.begin(),delim_iter));
    
    std::transform(
        byte_vector.begin(), delim_iter, std::back_inserter(char_part),
        [](const std::uint8_t byte) { return static_cast<char>(byte); }
    );
    
    • 0

相关问题

  • 编译器和模板处理

  • 指针。找到最小数量

  • C++,关于枚举类对象初始化的问题

  • 函数中的二维数组

  • 无法使用默认构造函数创建类对象

  • C++ 和循环依赖

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5