C++ 积分器下溢导致模失效

C++ 积分器下溢导致模失效,c++,undefined-behavior,modulo,underflow,C++,Undefined Behavior,Modulo,Underflow,我有4个图像要显示,因此我使用了一个无符号的short来表示当前图像的索引。当我按下A键时,索引减少1;当我按D键时,索引增加1 我使用image\u index=(image\u index-1)%4计算按键上的索引(在D键上,图像索引=(图像索引+1)%4;) 如果我循环前进(即,按D键),一切正常,但如果我在指数0处,按A键,它将下溢到无符号空头的最大值,而不是按4模得到指数3 我知道对于有符号类型,溢出/下溢是UB,但我确信对于无符号类型,它定义得很好。知道是什么导致它忽略了模吗 enu

我有4个图像要显示,因此我使用了一个
无符号的short
来表示当前图像的索引。当我按下A键时,索引减少1;当我按D键时,索引增加1

我使用
image\u index=(image\u index-1)%4计算按键上的索引(在D键上,
图像索引=(图像索引+1)%4;

如果我循环前进(即,按D键),一切正常,但如果我在指数0处,按A键,它将下溢到无符号空头的最大值,而不是按4模得到指数3

我知道对于有符号类型,溢出/下溢是UB,但我确信对于无符号类型,它定义得很好。知道是什么导致它忽略了模吗

enum class Errors : short {
  kSdlSuccess = 0,
  kSdlInitFailure = -1,
  kSdlWindowCreationError = -2,
  kSdlRendererCreationError = -3
};

int main(int argc, char** argv) {

  sdl::Context ctx;
  sdl::Window window({ .w = 600, .h = 480, .flags = 0});
  sdl::Renderer renderer(window, {0});

  bool is_running {true};
  unsigned short texture_index = 0;
  SDL_Event event {};
  while(is_running) {
    while(SDL_PollEvent(&event)) {
      if(event.type == SDL_QUIT) { is_running = false; }
      else if(event.type == SDL_KEYDOWN) {
        if(event.key.keysym.scancode == SDL_SCANCODE_A) { texture_index = (texture_index - 1) % 4; }
        else if(event.key.keysym.scancode == SDL_SCANCODE_D) { texture_index = (texture_index + 1) % 4; }
      }
    }

    printf("%d\n", texture_index);

    renderer.setDrawColor(255, 0, 0, 255);
    renderer.clearBuffer();
    renderer.setDrawColor(0,0,0,255);
    renderer.drawTexture(texture_index);
    renderer.swapBuffer();
  }
  return static_cast<int>(Errors::kSdlSuccess);
}
enum类错误:短{
kSdlSuccess=0,
kSdlInitFailure=-1,
kSdlWindowCreationError=-2,
KSDLrenderCreationError=-3
};
int main(int argc,字符**argv){
上下文ctx;
窗口窗口({.w=600,.h=480,.flags=0});
渲染器(窗口,{0});
布尔正在运行{true};
无符号短纹理索引=0;
SDL_事件{};
当(正在运行时){
while(SDL_事件和事件)){
如果(event.type==SDL_QUIT){is_running=false;}
else if(event.type==SDL\u KEYDOWN){
如果(event.key.keysym.scancode==SDL_scancode_A){texture_index=(texture_index-1)%4;}
如果(event.key.keysym.scancode==SDL\u scancode\u D){texture\u index=(texture\u index+1)%4;}
}
}
printf(“%d\n”,纹理索引);
setDrawColor(255,0,0,255);
clearBuffer();
渲染器.setDrawColor(0,0,0255);
渲染器.drawTexture(纹理索引);
swapBuffer();
}
返回静态_cast(错误::kSdlSuccess);
}
问题: 这是由于将
无符号short
类型提升为
(有符号)int
。(有关更多详细信息,请参阅)

假设您的
纹理索引最初为零

当您执行
纹理索引-1
时,您的
纹理索引将提升为
(带符号)int
。这种计算的结果是
(有符号)int
中的
-1
(-1)%4
的结果是
-1
(涉及负值的模计算可能会很棘手且不直观,有关更多详细信息,请参阅)

然后,将
-1
(a
signed int
)赋值(转换)到
texture\u index
(a
无符号短字符
)。此转换产生
65535
(或
0xFFFF
)。有关有符号到无符号转换的详细信息,请参阅。因此,问题不是被忽略的模运算,而是不需要的类型转换(或升级)


解决方案: 因此,解决方案是消除不必要的转换

在这个问题下的一条评论中,我看到了这样的情况:

纹理索引=(纹理索引-1u)%4u;
这就消除了对有符号整数的转换,非常好。它仍然会触发升级到
unsigned int
(因为
1u
4u
unsigned int
文字),但由于您的模很小,这并不重要

这在您的情况下可以正常工作,但是很脆弱

如果有一天你想要五张图片怎么办

unsigned short texture_index=0;
纹理指数=(纹理指数-1U)%5U;
断言(纹理指数=4U)//断言失败!
为什么??调试器现在说
texture\u index
0
。这是因为
0U-1U==4294967295U

绕过这个问题的一个好方法是在进行模运算之前,先将除数加到被除数中

unsigned short texture_index=0;
纹理指数=(纹理指数-1U+5U)%5U//在修改5U之前添加5U
断言(纹理指数=4U)//断言通过
问题: 这是由于将
无符号short
类型提升为
(有符号)int
。(有关更多详细信息,请参阅)

假设您的
纹理索引最初为零

当您执行
纹理索引-1
时,您的
纹理索引将提升为
(带符号)int
。这种计算的结果是
(有符号)int
中的
-1
(-1)%4
的结果是
-1
(涉及负值的模计算可能会很棘手且不直观,有关更多详细信息,请参阅)

然后,将
-1
(a
signed int
)赋值(转换)到
texture\u index
(a
无符号短字符
)。此转换产生
65535
(或
0xFFFF
)。有关有符号到无符号转换的详细信息,请参阅。因此,问题不是被忽略的模运算,而是不需要的类型转换(或升级)


解决方案: 因此,解决方案是消除不必要的转换

在这个问题下的一条评论中,我看到了这样的情况:

纹理索引=(纹理索引-1u)%4u;
这就消除了对有符号整数的转换,非常好。它仍然会触发升级到
unsigned int
(因为
1u
4u
unsigned int
文字),但由于您的模很小,这并不重要

这在您的情况下可以正常工作,但是很脆弱

如果有一天你想要五张图片怎么办

unsigned short texture_index=0;
纹理指数=(纹理指数-1U)%5U;
断言(纹理指数=4U)//断言失败!
为什么??调试器现在说
texture\u index
0
。这是因为
0U-1U==4294967295U

绕过这个问题的一个好方法是在进行模运算之前,先将除数加到被除数中

无符号短纹理