我要显示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
的提升(因为1u
和4u
是unsigned int
文字),但是由于模数很小,所以这无关紧要。这在您的情况下可以正常工作,但是是脆弱的。
如果有一天您想要五张图片怎么办?
unsigned short texture_index = 0;
texture_index = (texture_index - 1U) % 5U;
assert(texture_index == 4U); //Assertion fails!
为什么?调试器现在说
texture_index
是0
。那是因为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/