C++ 积分器下溢导致模失效
我有4个图像要显示,因此我使用了一个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
无符号的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
(asigned 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
(asigned 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
绕过这个问题的一个好方法是在进行模运算之前,先将除数加到被除数中
无符号短纹理