【问题标题】:Dangers of shifting a char out of scope将字符移出范围的危险
【发布时间】:2020-01-27 21:32:04
【问题描述】:

好的,

我写了一个函数,它从一个十六进制文件中获取一个无符号字符,然后将它向左移动以适合一个 WORD、DWORD 或 QWORD,如下所示:

retVal |= ((unsigned char)(memory[i]) << (8 * j));

(在循环内,因此变量 i 和 j)。

现在视觉工作室提醒我可能的算术溢出。

我的问题:如果我将 j 限制为不超过 8(uint64_t 的大小),我可以安全地忽略此消息吗? 我总是对警告感到有些沮丧,并试图消除它们。

据我了解,在保存值之前向左移动多少并不重要,我弄错了吗?

编辑:

这是一个例子(这是我的功能):

int getValuePNTR(const char* memory, int &start, int size)
{
    uint64_t retVal = 0;

    //now just add up array fields 
    for (int i = start + size-1,j = size-1; j >= 0; --j, i--)
    {
        //fprintf(stdout, "\ncycle: %d, memory: [%x]", j, memory[i]);

        if ((unsigned char)memory[i] == 00 && j > 0)
            retVal <<= 8;
        else
            retVal |= ((unsigned char)(memory[i]) << (8 * j));
    }
    //get the next field after this one
    start += size;
    return retVal;
}

【问题讨论】:

  • 对于j等于8的情况,原来的第7位会占据什么位?
  • 您对您的编译器警告您左移操作而不是|= 操作符的信心有多大?或者,您对您的编译器没有警告您溢出的信心有多大,因为左侧仅提升为int,这可能只是一个 32 位值,而不是您的 64 位值重新假设?
  • 我无法重现警告。你能分享minimal reproducible example吗?您确定这一行是产生警告的行吗?
  • @SamVarshavchik 如您所见,我使用的是 uint64_t ,而不是普通的 DWORD
  • 嗯,你写了那条评论答案已经贴出来了,清楚地解释了你对整数提升规则理解的错误。不,您在移位操作中没有使用 64 位值。

标签: c++ byte-shifting


【解决方案1】:

您需要将 (8 * j) 限制为小于 sizeof(int) * CHAR_BIT 以使您的代码在所有情况下都合法(假设是标准 x86-64 实现)。

首先,当您执行(unsigned char)(memory[i]) &lt;&lt; (8 * j) 整数提升时,表达式的类型是提升左侧的类型。在这种情况下,如果 sizeof(unsigned char) &lt; sizeof(int)unsigned int 否则将 unsigned char 提升为 int

那么[expr.shift]/1

如果右操作数为负数,或者大于或等于提升的左操作数的宽度,则行为未定义。

这就是(8 * j)需要小于sizeof(promoted_type) * CHAR_BIT的原因

【讨论】:

  • 好的,我有一个内置限制,我扩展了问题以包含实际代码。我有大小参数的宏,每个 makro 可以取的最大值是 8。所以,如果我理解正确,那应该没问题?
  • 我会继续接受您的回答,因为到目前为止我的代码中没有任何实际错误,我只是想 100% 确定这在将来会起作用。谢谢
  • @clockw0rk 这取决于j 将是什么。对于您的平台,8 * j 需要小于 32,否则您将有未定义的行为。
猜你喜欢
  • 2022-11-17
  • 2013-05-22
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2019-03-30
  • 2018-09-04
相关资源
最近更新 更多