我要显示4张图像,因此我使用unsigned short表示当前图像的索引。当我按A时,索引减少1;当我按D时,索引会增加1。

我正在使用image_index = (image_index - 1) % 4;在A按键上计算索引(以及在D按键image_index = (image_index + 1) % 4;上计算索引)

如果我向前循环(IE,按D),一切都按预期工作,但是如果我位于索引0并按A,它将下溢至无符号short的最大值,并且不会对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);
}

最佳答案

问题:

那是由于unsigned short类型提升为(signed) int。 (有关更多详细信息,请参见this answer)。

假设您的texture_index最初为零。

执行texture_index - 1时,您的texture_index会提升为(signed) int。这种计算的结果是-1中的(signed) int(-1) % 4的结果为-1(涉及负值的模数计算可能会比较棘手且违反直觉,有关更多详细信息,请引用this question)。

然后,您将-1(一个signed int)分配(转换)为texture_index(一个unsigned short)。此转换产生65535(或0xFFFF)。有关有符号到无符号转换的更多详细信息,请参见this answer。因此,问题不在于忽略的模运算,而在于不想要的类型转换(或提升)。

解决方案:

因此解决方案将是消除不必要的转换。

在这个问题下的一个评论中,我看到了类似的东西:

texture_index = (texture_index - 1u) % 4u;

这消除了向signed int的转换,很好。它仍然会触发unsigned int的提升(因为1u4uunsigned int文字),但是由于模数很小,所以这无关紧要。

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

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

unsigned short texture_index = 0;
texture_index = (texture_index - 1U) % 5U;
assert(texture_index == 4U);    //Assertion fails!

为什么?调试器现在说texture_index0。那是因为0U - 1U == 4294967295U

绕过此问题的一个不错的技巧是在进行模前将除数添加到被除数。

unsigned short texture_index = 0;
texture_index = (texture_index - 1U + 5U) % 5U;   //Add 5U before modding 5U
assert(texture_index == 4U); //Assertion passes

关于c++ - 整数下溢导致取模失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57455808/

10-11 22:45
查看更多