We have an annoying error that I cannot explain around this piece of code:
unsigned char bitmap[K_BITMAP_SIZE] = {0} ; SetBit(bitmap, K_18);
- This happens in Visual C ++ 2010.
- This happens on both 32-bit and 64-bit strings.
- This only happens in Release versions (with the setting "Maximize speed (/ O2)"
- This happens not only in Release versions with the parameter "Minimize Size (/ O1)"
- This happens in Visual C ++ 2008, only if we have the
__forceinline function (by default, VC ++ 2008 did not build this function, but VC ++ 2010) - This happens on the code snippet below, probably because the massive insert inside the loop
- This does not happen if we delete the cycle and directly set an interesting value (18)
Bonus Information:
1- BenJ commented that the problem does not appear in Visual C ++ 2012, i.e. this may be a compiler error
2- If we add cast to the unsigned char in the Test / Set / ResetBit function, the error will also disappear.
size_t TestBit(const unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) & (1 << (unsigned char)((pos) & 7))) ; } size_t SetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) |= (1 << (unsigned char)((pos) & 7))) ; } size_t ResetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) &= ~(1 << (unsigned char)((pos) & 7))) ; }
The question arises:
Does this error happen because our code depends on undefined behavior, or is there some kind of error in the VC ++ 2010 compiler?
The following source is self-contained and can be compiled as such on your favorite compiler:
#include <iostream> const size_t K_UNKNOWN = (-1) ; const size_t K_START = (0) ; const size_t K_12 = (K_START + 12) ; const size_t K_13 = (K_START + 13) ; const size_t K_15 = (K_START + 15) ; const size_t K_18 = (K_START + 18) ; const size_t K_26 = (K_START + 26) ; const size_t K_27 = (K_START + 27) ; const size_t K_107 = (K_START + 107) ; const size_t K_128 = (K_START + 128) ; const size_t K_END = (K_START + 208) ; const size_t K_BITMAP_SIZE = ((K_END/8) + 1) ; size_t TestBit(const unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) & (1 << ((pos) & 7))) ; } size_t SetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) |= (1 << ((pos) & 7))) ; } size_t ResetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) &= ~(1 << ((pos) & 7))) ; } size_t getData(size_t p_value) { size_t value = K_UNKNOWN; switch(p_value) { case K_13: value = K_12; break; case K_18: value = K_15; break; case K_107: value = K_15; break; case K_27: value = K_26; break; case K_128: value = K_12; break; default: value = p_value; break; } return value; } void testBug(const unsigned char * p_bitmap) { const size_t byte = p_bitmap[1] ; const size_t bit = 1 << 7 ; const size_t value = byte & bit ; if(value == 0) { std::cout << "ERROR : The bit 15 should NOT be 0" << std::endl ; } else { std::cout << "Ok : The bit 15 is 1" << std::endl ; } } int main(int argc, char * argv[]) { unsigned char bitmap[K_BITMAP_SIZE] = {0} ; SetBit(bitmap, K_18); for(size_t i = 0; i < K_END; ++i) { if(TestBit(bitmap, i)) { size_t i2 = getData(i); SetBit(bitmap, i2); } } testBug(bitmap) ; return 0; }
Some background information: Initially:
- The functions Test / Set / ResetBit were macros.
- constants determined
- indexes were either
long or int (they are the same size on a 32-bit version of Windows)
If necessary, I will add some additional information as soon as possible (for example, the generated assembler for both configurations, update the information on how g ++ handles the problem).
c ++ compiler-optimization undefined-behavior visual-studio visual-studio-2010
paercebal Oct 08 2018-12-12T00: 00Z
source share