The C99 standard has added to the C programming language a Boolean type, _Bool
and the bool
alias for it. How well does this type interoperate with the Windows SDK BOOL
type? The answer is, not at all well, and here’s the complete story.
As an example, consider the following two files.
#include <stdbool.h>
bool
(void)
false_function{
return false;
}
#include <stdio.h>
#include <windows.h>
(void);
BOOL false_function
int
(int argc, char *argv)
main{
if (false_function())
("False function returned true\n");
printf}
Compiling the two files with the Microsoft C/C++ Compiler (Version 19) produces the following result when the executable program is run.
False function returned true
The reason this happens, is because most compilers (including Microsoft C, GCC, and Clang) store bool
values into a single byte, which can be returned in the low part of the x86 accumulator, al. In contrast, the Microsft Windows SDK defines (in file WTypes.h
) BOOL
as follows.
typedef long BOOL;
This Boolean value is passed around as a long, so it is returned in the register eax.
Consequently, the false_function
just sets al to zero.
_false_function PROC; Line 5
push ebp
mov ebp, esp
; Line 6
xor al, al
; Line 7
pop ebp
ret 0
_false_function ENDP
_TEXT ENDSEND
However, the if
statement in main
tests the whole accumulator eax as the return value.
call _false_function
test eax, eax
je SHORT $LN1@main
; Line 10
push OFFSET $SG88306
call _printf
add esp, 4
@main:
$LN1; Line 11
xor eax, eax
pop ebp
ret 0
Because eax is likely to have some bits set in its most significant part, the comparison yields a true result and the program prints that the false function returned true.
I discovered the problem while debugging a much more complex issue. At one point I was single-stepping through a function that was returning a false result. However, when its caller tested the function’s return value, it behaved as if the result was true. I discovered what was going on by single-stepping through the disassembled instructions. There I saw that the one function was setting al and the other was testing eax.
The problem would be avoided, if the second file included a header file with the function’s correct declaration, such as the following.
#include <stdbool.h>
bool false_function(void);
In addition, if the first file also included the header file with the function’s declaration, an incorrect declaration (such as the one with BOOL
) would be detected. You can see this in the following error message.
t.c(7): error C2371: 'false_function': redefinition; different basic types
t.h(3): note: see declaration of 'false_function'
Sometimes, debugging software at the level of machine instructions can help you unravel otherwise inexplicable mysteries. I advise this in Item 37 of the Effective Debugging book: Know how to view assembly code and raw memory.
Comments Toot! TweetLast modified: Tuesday, September 5, 2017 7:50 pm
Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.