Can an uninitialized bool crash a program?












150














I know that "undefined behaviour" in C++ can pretty much allow the compiler to do anything it wants. However, I had a crash that surprised me, as I would have assumed the code looked safe enough. In this case, the real problem happened only on a specific platform using a specific compiler, and only if optimization were enabled.



I tried several things in order to reproduce the problem and simplify it to the maximum. Here's an extract of a function called Serialize, that would take a bool parameter, and copy the string "true" or "false" to an existing destination buffer. Would this function be in a code review, there would be no way to tell that it, in fact, could crash if the bool parameter was an uninitialized value.



// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";

// Compute the length of the string we selected
const size_t len = strlen(whichString);

// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}


If this code is executed with clang 5.0.0 + optimizations, it will/can crash.



The expected ternary-operator boolValue ? "true" : "false" looked safe enough for me, I was assuming, "Whatever garbage value is in boolValue doesn't matter, since it will evaluate to true or false anyhow."



I have setup a Compiler Explorer example that shows the problem in the disassembly:



#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
bool uninitializedBool;

__attribute__ ((noinline)) // Note: the constructor must be declared noinline to trigger the problem
FStruct() {};
};

int main()
{
// Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
FStruct structInstance;

// Output "true" or "false" to stdout
Serialize(structInstance.uninitializedBool);
return 0;
}


The problem arises because of the optimizer: It was clever enough to deduce that the strings "true" and "false" only differs in length by 1. So instead of really calculating the length, it uses the value of the bool itself, which should technically be either 0 or 1, and goes like this:



const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization


While this is "clever", so to speak, my question is: Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way? Or is this a case of implementation-defined, in which case the implementation assumed that all its bools will only ever contain 0 or 1, and any other value is undefined behaviour territory?










share|improve this question









New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 77




    It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
    – John Kugelman
    23 hours ago








  • 1




    Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
    – Euro Micelli
    21 hours ago






  • 9




    I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
    – David Schwartz
    13 hours ago






  • 2




    Great question, and most important great job in analyzing this very odd behavior
    – frarugi87
    8 hours ago






  • 9




    @Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
    – Remz
    7 hours ago
















150














I know that "undefined behaviour" in C++ can pretty much allow the compiler to do anything it wants. However, I had a crash that surprised me, as I would have assumed the code looked safe enough. In this case, the real problem happened only on a specific platform using a specific compiler, and only if optimization were enabled.



I tried several things in order to reproduce the problem and simplify it to the maximum. Here's an extract of a function called Serialize, that would take a bool parameter, and copy the string "true" or "false" to an existing destination buffer. Would this function be in a code review, there would be no way to tell that it, in fact, could crash if the bool parameter was an uninitialized value.



// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";

// Compute the length of the string we selected
const size_t len = strlen(whichString);

// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}


If this code is executed with clang 5.0.0 + optimizations, it will/can crash.



The expected ternary-operator boolValue ? "true" : "false" looked safe enough for me, I was assuming, "Whatever garbage value is in boolValue doesn't matter, since it will evaluate to true or false anyhow."



I have setup a Compiler Explorer example that shows the problem in the disassembly:



#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
bool uninitializedBool;

__attribute__ ((noinline)) // Note: the constructor must be declared noinline to trigger the problem
FStruct() {};
};

int main()
{
// Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
FStruct structInstance;

// Output "true" or "false" to stdout
Serialize(structInstance.uninitializedBool);
return 0;
}


The problem arises because of the optimizer: It was clever enough to deduce that the strings "true" and "false" only differs in length by 1. So instead of really calculating the length, it uses the value of the bool itself, which should technically be either 0 or 1, and goes like this:



const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization


While this is "clever", so to speak, my question is: Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way? Or is this a case of implementation-defined, in which case the implementation assumed that all its bools will only ever contain 0 or 1, and any other value is undefined behaviour territory?










share|improve this question









New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 77




    It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
    – John Kugelman
    23 hours ago








  • 1




    Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
    – Euro Micelli
    21 hours ago






  • 9




    I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
    – David Schwartz
    13 hours ago






  • 2




    Great question, and most important great job in analyzing this very odd behavior
    – frarugi87
    8 hours ago






  • 9




    @Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
    – Remz
    7 hours ago














150












150








150


32





I know that "undefined behaviour" in C++ can pretty much allow the compiler to do anything it wants. However, I had a crash that surprised me, as I would have assumed the code looked safe enough. In this case, the real problem happened only on a specific platform using a specific compiler, and only if optimization were enabled.



I tried several things in order to reproduce the problem and simplify it to the maximum. Here's an extract of a function called Serialize, that would take a bool parameter, and copy the string "true" or "false" to an existing destination buffer. Would this function be in a code review, there would be no way to tell that it, in fact, could crash if the bool parameter was an uninitialized value.



// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";

// Compute the length of the string we selected
const size_t len = strlen(whichString);

// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}


If this code is executed with clang 5.0.0 + optimizations, it will/can crash.



The expected ternary-operator boolValue ? "true" : "false" looked safe enough for me, I was assuming, "Whatever garbage value is in boolValue doesn't matter, since it will evaluate to true or false anyhow."



I have setup a Compiler Explorer example that shows the problem in the disassembly:



#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
bool uninitializedBool;

__attribute__ ((noinline)) // Note: the constructor must be declared noinline to trigger the problem
FStruct() {};
};

int main()
{
// Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
FStruct structInstance;

// Output "true" or "false" to stdout
Serialize(structInstance.uninitializedBool);
return 0;
}


The problem arises because of the optimizer: It was clever enough to deduce that the strings "true" and "false" only differs in length by 1. So instead of really calculating the length, it uses the value of the bool itself, which should technically be either 0 or 1, and goes like this:



const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization


While this is "clever", so to speak, my question is: Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way? Or is this a case of implementation-defined, in which case the implementation assumed that all its bools will only ever contain 0 or 1, and any other value is undefined behaviour territory?










share|improve this question









New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I know that "undefined behaviour" in C++ can pretty much allow the compiler to do anything it wants. However, I had a crash that surprised me, as I would have assumed the code looked safe enough. In this case, the real problem happened only on a specific platform using a specific compiler, and only if optimization were enabled.



I tried several things in order to reproduce the problem and simplify it to the maximum. Here's an extract of a function called Serialize, that would take a bool parameter, and copy the string "true" or "false" to an existing destination buffer. Would this function be in a code review, there would be no way to tell that it, in fact, could crash if the bool parameter was an uninitialized value.



// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";

// Compute the length of the string we selected
const size_t len = strlen(whichString);

// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}


If this code is executed with clang 5.0.0 + optimizations, it will/can crash.



The expected ternary-operator boolValue ? "true" : "false" looked safe enough for me, I was assuming, "Whatever garbage value is in boolValue doesn't matter, since it will evaluate to true or false anyhow."



I have setup a Compiler Explorer example that shows the problem in the disassembly:



#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
bool uninitializedBool;

__attribute__ ((noinline)) // Note: the constructor must be declared noinline to trigger the problem
FStruct() {};
};

int main()
{
// Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
FStruct structInstance;

// Output "true" or "false" to stdout
Serialize(structInstance.uninitializedBool);
return 0;
}


The problem arises because of the optimizer: It was clever enough to deduce that the strings "true" and "false" only differs in length by 1. So instead of really calculating the length, it uses the value of the bool itself, which should technically be either 0 or 1, and goes like this:



const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization


While this is "clever", so to speak, my question is: Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way? Or is this a case of implementation-defined, in which case the implementation assumed that all its bools will only ever contain 0 or 1, and any other value is undefined behaviour territory?







c++ llvm undefined-behavior abi






share|improve this question









New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 15 hours ago









Peter Cordes

120k16183312




120k16183312






New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 23 hours ago









RemzRemz

246138




246138




New contributor




Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Remz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








  • 77




    It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
    – John Kugelman
    23 hours ago








  • 1




    Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
    – Euro Micelli
    21 hours ago






  • 9




    I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
    – David Schwartz
    13 hours ago






  • 2




    Great question, and most important great job in analyzing this very odd behavior
    – frarugi87
    8 hours ago






  • 9




    @Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
    – Remz
    7 hours ago














  • 77




    It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
    – John Kugelman
    23 hours ago








  • 1




    Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
    – Euro Micelli
    21 hours ago






  • 9




    I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
    – David Schwartz
    13 hours ago






  • 2




    Great question, and most important great job in analyzing this very odd behavior
    – frarugi87
    8 hours ago






  • 9




    @Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
    – Remz
    7 hours ago








77




77




It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
– John Kugelman
23 hours ago






It's a great question. It's a solid illustration of how undefined behavior isn't just a theoretical concern. When people say anything can happen as a result of UB, that "anything" can really be quite surprising. One might assume that undefined behavior still manifests in predictable ways, but these days with modern optimizers that's not at all true. OP took the time to create a MCVE, investigated the problem thoroughly, inspected the disassembly, and asked a clear, straightforward question about it. Couldn't ask for more.
– John Kugelman
23 hours ago






1




1




Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
– Euro Micelli
21 hours ago




Observe that the requirement that “non-zero evaluates to true” is a rule about Boolean operations including “assignment to a bool” (which might implicitly invoke a static_cast<bool>() depending on specifics). It is however not a requirement about the internal representation of a bool chosen by the compiler.
– Euro Micelli
21 hours ago




9




9




I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
– David Schwartz
13 hours ago




I have bookmarked this question. And the next time I need to explain to someone that undefined behavior really is undefined, this will be one of the first links I give them.
– David Schwartz
13 hours ago




2




2




Great question, and most important great job in analyzing this very odd behavior
– frarugi87
8 hours ago




Great question, and most important great job in analyzing this very odd behavior
– frarugi87
8 hours ago




9




9




@Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
– Remz
7 hours ago




@Cody No, alas it doesn't work. The famous trick for "rectifying" a bool using double-not (!!) doesn't necessarily work if you have an uninitialized bool. Since it is undefined behaviour anyway, the compiler can do want it wants. But in this particular case, if you have a bool, and you perform !! on it, the optimizer will simply remove the code and do nothing. For fun I also tried doing one not (!) on the bool, and the compiler decided to only flip bit#0. The funny result is that boolValue evaluated to true, and doing !boolValue still evaluated to true..!
– Remz
7 hours ago












5 Answers
5






active

oldest

votes


















69














You're compiling for the x86-64 System V ABI, which specifies that a bool as a function arg in a register is represented by the bit-patterns false=0 and true=1 in the low 8 bits of the register1. In memory, bool is a 1-byte type that again must have an integer value of 0 or 1.



(An ABI is a set of implementation choices that compilers for the same platform agree on so they can make code that calls each other's functions. The Windows x64 ABI defines bool the same way, and I think most non-x86 platforms do the same. This makes bool->int conversion cheap, just zero-extension, and allows optimizations like compiling a&&b to a bitwise AND for bool types. Some compilers do actually take advantage Boolean values as 8 bit in compilers. Are operations on them inefficient?. Or negate a bool with xor eax,1 to flip the low bit: Any possible code that can flip a bit/integer/bool between 0 and 1 in single CPU instruction)



The compiler is definitely allowed to take full advantage of an ABI guarantee in its code-gen, and make code like you found which optimizes strlen(whichString) to
5U - boolValue.
(BTW, this optimization is kind of clever, but maybe shortsighted vs. branching and inlining memcpyas stores of immediate data2.) Or the compiler could have created a table of pointers and indexed it with the integer value of the bool, again assuming it was a 0 or 1. (This possibility is what @Barmar's answer suggested.)



Your __attribute((noinline)) constructor with optimization enabled led to clang just loading a byte from the stack to use as uninitializedBool. It made space for the object in main with push rax (which is smaller and for various reason about as efficient as sub rsp, 8), so whatever garbage was in AL on entry to main is the value it used for uninitializedBool. This is why you actually got values that weren't just 0.



5 - random garbage can easily wrap to a large unsigned value, leading memcpy to go into unmapped memory. The destination is in static storage, not the stack, so you're not overwriting a return address or something.





Other implementations could make different choices, e.g. false=0 and true=any non-zero value. Then clang probably wouldn't make code that crashes for this specific instance of UB. I don't know of any implementations that choose this or anything other what x86-64 does for bool, but the C++ standard allows many things that nobody does or even would want to do on hardware that's anything like current CPUs.



It's unspecified what you'll find when you examine the object representation of a bool. (e.g. by memcpying the bool into unsigned char, which you're allowed to do because char* can alias anything. And unsigned char is guaranteed to have no padding bits, so the C++ standard does formally let you hexdump object representations without any UB. Pointer-casting to copy the object representation is different from assigning char foo = my_bool, of course, so booleanization to 0 or 1 wouldn't happen and you'd get the raw object representation.)



You've partially "hidden" the UB on this execution path from the compiler with noinline. Even if it doesn't inline, though, interprocedural optimizations could still make a version of the function that depends on the definition of another function. (First, clang is making an executable, not a Unix shared library where symbol-interposition can happen. Second, the definition in inside the class{} definition so all translation units must have the same definition. Like with the inline keyword.)



So a compiler could emit just a ret or ud2 (illegal instruction) as the definition for main, because the path of execution starting at the top of main unavoidably encounters Undefined Behaviour.



Any program that encounters UB is totally undefined for its entire existence. But UB inside a function or if() branch that never actually runs doesn't corrupt the rest of the program. In practice that means that compilers can decide to emit an illegal instruction, or a ret, or not emit anything and fall into the next block / function, for the whole basic block that can be proven at compile time to contain or lead to UB.



GCC and Clang in practice do actually sometimes emit ud2 on UB, instead of even trying to generate code for paths of execution that make no sense. Or for cases like falling off the end of a non-void function, gcc will sometimes omit a ret instruction. If you were thinking that "my function will just return with whatever garbage is in RAX", you are sorely mistaken. Modern C++ compilers don't treat the language like a portable assembly language any more. Your program really has to be valid C++, without making assumptions about how a stand-alone non inlined version of your function might look in asm.



Another fun example is Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?. x86 doesn't fault on unaligned integers, right? So why would a misaligned uint16_t* be a problem? Because alignof(uint16_t) == 2, and violating that assumption led to a segfault when auto-vectorizing with SSE2.



See also What Every C Programmer Should Know About Undefined Behavior #1/3, an article by a clang developer.





Footnote 1: The upper 56 bits can be garbage which the callee must ignore, as usual for types narrower than a register.



For example, a caller might have calculated a & 0x01010101 in RDI and used it for something else, before calling bool_func(a&1). The caller could optimize away the &1 because it already did that to the low byte as part of and edi, 0x01010101, and it knows the callee is required to ignore the high bytes.



Or if a bool is passed as the 3rd arg, maybe a caller optimizing for code-size loads it with mov dl, [mem] instead of movzx edx, [mem], saving 1 byte at the cost of a false dependency on the old value of RDX (or other partial-register effect, depending on CPU model). Or for the first arg, mov dil, byte [r10] instead of movzx edi, byte [r10], because both require a REX prefix anyway.



This is why clang emits movzx eax, dil in Serialize, instead of sub eax, edi. (For integer args, clang violates this ABI rule, instead depending on the undocumented behaviour of gcc and clang to zero- or sign-extend narrow integers to 32 bits. Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?
So I was interested to see that it doesn't do the same thing for bool.)





Footnote 2: After branching, you'd just have a 4-byte mov-immediate, or a 4-byte + 1-byte store. The length is implicit in the store widths + offsets.



OTOH, glibc memcpy will do two 4-byte loads/stores with an overlap that depends on length, so this really does end up making the whole thing free of conditional branches on the boolean. See the L(between_4_7): block in glibc's memcpy/memmove. Or at least, go the same way for either boolean in memcpy's branching to select a chunk size.



If inlining, you could use 2x mov-immediate + cmov and a conditional offset, or you could leave the string data in memory.



Or if tuning for Intel Ice Lake (with the Fast Short REP MOV feature), an actual rep movsb might be optimal. glibc memcpy might start using rep movsb for small sizes on CPUs with that feature, saving a lot of branching.






share|improve this answer























  • gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
    – liliscent
    12 hours ago










  • This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
    – SimonC
    12 hours ago










  • @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
    – Peter Cordes
    11 hours ago



















31














The compiler is allowed to assume that a boolean value passed as an argument is a valid boolean value (i.e. one which has been initialised or converted to true or false). The true value doesn't have to be the same as the integer 1 -- indeed, there can be various representations of true and false -- but the parameter must be some valid representation of one of those two values, where "valid representation" is implementation-defined.



So if you fail to initialise a bool, or if you succeed in overwriting it through some pointer of a different type, then the compiler's assumptions will be wrong and Undefined Behaviour will ensue. You had been warned:




50) Using a bool value in ways described by this International Standard as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false. (Footnote to para 6 of §6.9.1, Fundamental Types)







share|improve this answer



















  • 10




    The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
    – ShadowRanger
    22 hours ago








  • 1




    @ShadowRanger You can always inspect the object representation directly.
    – T.C.
    22 hours ago






  • 5




    @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
    – rici
    22 hours ago








  • 2




    Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
    – Peter Cordes
    16 hours ago






  • 2




    The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
    – Holger
    14 hours ago



















23














The function itself is correct, but in your test program, the statement that calls the function causes undefined behaviour by using the value of an uninitialized variable.



The bug is in the calling function, and it could be detected by code review or static analysis of the calling function. Using your compiler explorer link, the gcc 8.2 compiler does detect the bug. (Maybe you could file a bug report against clang that it doesn't find the problem).



Undefined behaviour means anything can happen, which includes the program crashing a few lines after the event that triggered the undefined behaviour.



NB. The answer to "Can undefined behaviour cause _____ ?" is always "Yes". That's literally the definition of undefined behaviour.






share|improve this answer

















  • 1




    Is the first clause true? Does merely copying an uninitialized bool trigger UB?
    – Joshua Green
    21 hours ago






  • 5




    @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
    – M.M
    21 hours ago








  • 5




    @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
    – David Schwartz
    13 hours ago










  • Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
    – MSalters
    5 hours ago










  • On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
    – supercat
    3 hours ago



















11














A bool is only allowed to hold the values 0 or 1, and the generated code can assume that it will only hold one of these two values. The code generated for the ternary in the assignment could use the value as the index into an array of pointers to the two strings, i.e. it might be converted to something like:



     // the compile could make asm that "looks" like this, from your source
const static char *strings = {"false", "true"};
const char *whichString = strings[boolValue];


If boolValue is uninitialized, it could actually hold any integer value, which would then cause accessing outside the bounds of the strings array.






share|improve this answer























  • @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
    – Barmar
    22 hours ago










  • You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
    – Remz
    22 hours ago






  • 1




    @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
    – Barmar
    22 hours ago








  • 1




    @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
    – Havenard
    22 hours ago








  • 1




    @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
    – Sid S
    20 hours ago



















4














Summarising your question a lot, you are asking Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way?



The standard says nothing about the internal representation of a bool. It only defines what happens when casting a bool to an int (or vice versa). Mostly, because of these integral conversions (and the fact that people rely rather heavily on them), the compiler will use 0 and 1, but it doesn't have to (although it has to respect the constraints of any lower level ABI it uses).



So, the compiler, when it sees a bool is entitled to consider that said bool contains either of the 'true' or 'false' bit patterns and do anything it feels like. So if the values for true and false are 1 and 0, respectively, the compiler is indeed allowed to optimise strlen to 5 - <boolean value>. Other fun behaviours are possible!



As gets repeatedly stated here, undefined behaviour has undefined results. Including but not limited to




  • Your code working as you expected it to

  • Your code failing at random times

  • Your code not being run at all.


See What every programmer should know about undefined behavior






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    Remz is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54120862%2fcan-an-uninitialized-bool-crash-a-program%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    69














    You're compiling for the x86-64 System V ABI, which specifies that a bool as a function arg in a register is represented by the bit-patterns false=0 and true=1 in the low 8 bits of the register1. In memory, bool is a 1-byte type that again must have an integer value of 0 or 1.



    (An ABI is a set of implementation choices that compilers for the same platform agree on so they can make code that calls each other's functions. The Windows x64 ABI defines bool the same way, and I think most non-x86 platforms do the same. This makes bool->int conversion cheap, just zero-extension, and allows optimizations like compiling a&&b to a bitwise AND for bool types. Some compilers do actually take advantage Boolean values as 8 bit in compilers. Are operations on them inefficient?. Or negate a bool with xor eax,1 to flip the low bit: Any possible code that can flip a bit/integer/bool between 0 and 1 in single CPU instruction)



    The compiler is definitely allowed to take full advantage of an ABI guarantee in its code-gen, and make code like you found which optimizes strlen(whichString) to
    5U - boolValue.
    (BTW, this optimization is kind of clever, but maybe shortsighted vs. branching and inlining memcpyas stores of immediate data2.) Or the compiler could have created a table of pointers and indexed it with the integer value of the bool, again assuming it was a 0 or 1. (This possibility is what @Barmar's answer suggested.)



    Your __attribute((noinline)) constructor with optimization enabled led to clang just loading a byte from the stack to use as uninitializedBool. It made space for the object in main with push rax (which is smaller and for various reason about as efficient as sub rsp, 8), so whatever garbage was in AL on entry to main is the value it used for uninitializedBool. This is why you actually got values that weren't just 0.



    5 - random garbage can easily wrap to a large unsigned value, leading memcpy to go into unmapped memory. The destination is in static storage, not the stack, so you're not overwriting a return address or something.





    Other implementations could make different choices, e.g. false=0 and true=any non-zero value. Then clang probably wouldn't make code that crashes for this specific instance of UB. I don't know of any implementations that choose this or anything other what x86-64 does for bool, but the C++ standard allows many things that nobody does or even would want to do on hardware that's anything like current CPUs.



    It's unspecified what you'll find when you examine the object representation of a bool. (e.g. by memcpying the bool into unsigned char, which you're allowed to do because char* can alias anything. And unsigned char is guaranteed to have no padding bits, so the C++ standard does formally let you hexdump object representations without any UB. Pointer-casting to copy the object representation is different from assigning char foo = my_bool, of course, so booleanization to 0 or 1 wouldn't happen and you'd get the raw object representation.)



    You've partially "hidden" the UB on this execution path from the compiler with noinline. Even if it doesn't inline, though, interprocedural optimizations could still make a version of the function that depends on the definition of another function. (First, clang is making an executable, not a Unix shared library where symbol-interposition can happen. Second, the definition in inside the class{} definition so all translation units must have the same definition. Like with the inline keyword.)



    So a compiler could emit just a ret or ud2 (illegal instruction) as the definition for main, because the path of execution starting at the top of main unavoidably encounters Undefined Behaviour.



    Any program that encounters UB is totally undefined for its entire existence. But UB inside a function or if() branch that never actually runs doesn't corrupt the rest of the program. In practice that means that compilers can decide to emit an illegal instruction, or a ret, or not emit anything and fall into the next block / function, for the whole basic block that can be proven at compile time to contain or lead to UB.



    GCC and Clang in practice do actually sometimes emit ud2 on UB, instead of even trying to generate code for paths of execution that make no sense. Or for cases like falling off the end of a non-void function, gcc will sometimes omit a ret instruction. If you were thinking that "my function will just return with whatever garbage is in RAX", you are sorely mistaken. Modern C++ compilers don't treat the language like a portable assembly language any more. Your program really has to be valid C++, without making assumptions about how a stand-alone non inlined version of your function might look in asm.



    Another fun example is Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?. x86 doesn't fault on unaligned integers, right? So why would a misaligned uint16_t* be a problem? Because alignof(uint16_t) == 2, and violating that assumption led to a segfault when auto-vectorizing with SSE2.



    See also What Every C Programmer Should Know About Undefined Behavior #1/3, an article by a clang developer.





    Footnote 1: The upper 56 bits can be garbage which the callee must ignore, as usual for types narrower than a register.



    For example, a caller might have calculated a & 0x01010101 in RDI and used it for something else, before calling bool_func(a&1). The caller could optimize away the &1 because it already did that to the low byte as part of and edi, 0x01010101, and it knows the callee is required to ignore the high bytes.



    Or if a bool is passed as the 3rd arg, maybe a caller optimizing for code-size loads it with mov dl, [mem] instead of movzx edx, [mem], saving 1 byte at the cost of a false dependency on the old value of RDX (or other partial-register effect, depending on CPU model). Or for the first arg, mov dil, byte [r10] instead of movzx edi, byte [r10], because both require a REX prefix anyway.



    This is why clang emits movzx eax, dil in Serialize, instead of sub eax, edi. (For integer args, clang violates this ABI rule, instead depending on the undocumented behaviour of gcc and clang to zero- or sign-extend narrow integers to 32 bits. Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?
    So I was interested to see that it doesn't do the same thing for bool.)





    Footnote 2: After branching, you'd just have a 4-byte mov-immediate, or a 4-byte + 1-byte store. The length is implicit in the store widths + offsets.



    OTOH, glibc memcpy will do two 4-byte loads/stores with an overlap that depends on length, so this really does end up making the whole thing free of conditional branches on the boolean. See the L(between_4_7): block in glibc's memcpy/memmove. Or at least, go the same way for either boolean in memcpy's branching to select a chunk size.



    If inlining, you could use 2x mov-immediate + cmov and a conditional offset, or you could leave the string data in memory.



    Or if tuning for Intel Ice Lake (with the Fast Short REP MOV feature), an actual rep movsb might be optimal. glibc memcpy might start using rep movsb for small sizes on CPUs with that feature, saving a lot of branching.






    share|improve this answer























    • gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
      – liliscent
      12 hours ago










    • This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
      – SimonC
      12 hours ago










    • @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
      – Peter Cordes
      11 hours ago
















    69














    You're compiling for the x86-64 System V ABI, which specifies that a bool as a function arg in a register is represented by the bit-patterns false=0 and true=1 in the low 8 bits of the register1. In memory, bool is a 1-byte type that again must have an integer value of 0 or 1.



    (An ABI is a set of implementation choices that compilers for the same platform agree on so they can make code that calls each other's functions. The Windows x64 ABI defines bool the same way, and I think most non-x86 platforms do the same. This makes bool->int conversion cheap, just zero-extension, and allows optimizations like compiling a&&b to a bitwise AND for bool types. Some compilers do actually take advantage Boolean values as 8 bit in compilers. Are operations on them inefficient?. Or negate a bool with xor eax,1 to flip the low bit: Any possible code that can flip a bit/integer/bool between 0 and 1 in single CPU instruction)



    The compiler is definitely allowed to take full advantage of an ABI guarantee in its code-gen, and make code like you found which optimizes strlen(whichString) to
    5U - boolValue.
    (BTW, this optimization is kind of clever, but maybe shortsighted vs. branching and inlining memcpyas stores of immediate data2.) Or the compiler could have created a table of pointers and indexed it with the integer value of the bool, again assuming it was a 0 or 1. (This possibility is what @Barmar's answer suggested.)



    Your __attribute((noinline)) constructor with optimization enabled led to clang just loading a byte from the stack to use as uninitializedBool. It made space for the object in main with push rax (which is smaller and for various reason about as efficient as sub rsp, 8), so whatever garbage was in AL on entry to main is the value it used for uninitializedBool. This is why you actually got values that weren't just 0.



    5 - random garbage can easily wrap to a large unsigned value, leading memcpy to go into unmapped memory. The destination is in static storage, not the stack, so you're not overwriting a return address or something.





    Other implementations could make different choices, e.g. false=0 and true=any non-zero value. Then clang probably wouldn't make code that crashes for this specific instance of UB. I don't know of any implementations that choose this or anything other what x86-64 does for bool, but the C++ standard allows many things that nobody does or even would want to do on hardware that's anything like current CPUs.



    It's unspecified what you'll find when you examine the object representation of a bool. (e.g. by memcpying the bool into unsigned char, which you're allowed to do because char* can alias anything. And unsigned char is guaranteed to have no padding bits, so the C++ standard does formally let you hexdump object representations without any UB. Pointer-casting to copy the object representation is different from assigning char foo = my_bool, of course, so booleanization to 0 or 1 wouldn't happen and you'd get the raw object representation.)



    You've partially "hidden" the UB on this execution path from the compiler with noinline. Even if it doesn't inline, though, interprocedural optimizations could still make a version of the function that depends on the definition of another function. (First, clang is making an executable, not a Unix shared library where symbol-interposition can happen. Second, the definition in inside the class{} definition so all translation units must have the same definition. Like with the inline keyword.)



    So a compiler could emit just a ret or ud2 (illegal instruction) as the definition for main, because the path of execution starting at the top of main unavoidably encounters Undefined Behaviour.



    Any program that encounters UB is totally undefined for its entire existence. But UB inside a function or if() branch that never actually runs doesn't corrupt the rest of the program. In practice that means that compilers can decide to emit an illegal instruction, or a ret, or not emit anything and fall into the next block / function, for the whole basic block that can be proven at compile time to contain or lead to UB.



    GCC and Clang in practice do actually sometimes emit ud2 on UB, instead of even trying to generate code for paths of execution that make no sense. Or for cases like falling off the end of a non-void function, gcc will sometimes omit a ret instruction. If you were thinking that "my function will just return with whatever garbage is in RAX", you are sorely mistaken. Modern C++ compilers don't treat the language like a portable assembly language any more. Your program really has to be valid C++, without making assumptions about how a stand-alone non inlined version of your function might look in asm.



    Another fun example is Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?. x86 doesn't fault on unaligned integers, right? So why would a misaligned uint16_t* be a problem? Because alignof(uint16_t) == 2, and violating that assumption led to a segfault when auto-vectorizing with SSE2.



    See also What Every C Programmer Should Know About Undefined Behavior #1/3, an article by a clang developer.





    Footnote 1: The upper 56 bits can be garbage which the callee must ignore, as usual for types narrower than a register.



    For example, a caller might have calculated a & 0x01010101 in RDI and used it for something else, before calling bool_func(a&1). The caller could optimize away the &1 because it already did that to the low byte as part of and edi, 0x01010101, and it knows the callee is required to ignore the high bytes.



    Or if a bool is passed as the 3rd arg, maybe a caller optimizing for code-size loads it with mov dl, [mem] instead of movzx edx, [mem], saving 1 byte at the cost of a false dependency on the old value of RDX (or other partial-register effect, depending on CPU model). Or for the first arg, mov dil, byte [r10] instead of movzx edi, byte [r10], because both require a REX prefix anyway.



    This is why clang emits movzx eax, dil in Serialize, instead of sub eax, edi. (For integer args, clang violates this ABI rule, instead depending on the undocumented behaviour of gcc and clang to zero- or sign-extend narrow integers to 32 bits. Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?
    So I was interested to see that it doesn't do the same thing for bool.)





    Footnote 2: After branching, you'd just have a 4-byte mov-immediate, or a 4-byte + 1-byte store. The length is implicit in the store widths + offsets.



    OTOH, glibc memcpy will do two 4-byte loads/stores with an overlap that depends on length, so this really does end up making the whole thing free of conditional branches on the boolean. See the L(between_4_7): block in glibc's memcpy/memmove. Or at least, go the same way for either boolean in memcpy's branching to select a chunk size.



    If inlining, you could use 2x mov-immediate + cmov and a conditional offset, or you could leave the string data in memory.



    Or if tuning for Intel Ice Lake (with the Fast Short REP MOV feature), an actual rep movsb might be optimal. glibc memcpy might start using rep movsb for small sizes on CPUs with that feature, saving a lot of branching.






    share|improve this answer























    • gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
      – liliscent
      12 hours ago










    • This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
      – SimonC
      12 hours ago










    • @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
      – Peter Cordes
      11 hours ago














    69












    69








    69






    You're compiling for the x86-64 System V ABI, which specifies that a bool as a function arg in a register is represented by the bit-patterns false=0 and true=1 in the low 8 bits of the register1. In memory, bool is a 1-byte type that again must have an integer value of 0 or 1.



    (An ABI is a set of implementation choices that compilers for the same platform agree on so they can make code that calls each other's functions. The Windows x64 ABI defines bool the same way, and I think most non-x86 platforms do the same. This makes bool->int conversion cheap, just zero-extension, and allows optimizations like compiling a&&b to a bitwise AND for bool types. Some compilers do actually take advantage Boolean values as 8 bit in compilers. Are operations on them inefficient?. Or negate a bool with xor eax,1 to flip the low bit: Any possible code that can flip a bit/integer/bool between 0 and 1 in single CPU instruction)



    The compiler is definitely allowed to take full advantage of an ABI guarantee in its code-gen, and make code like you found which optimizes strlen(whichString) to
    5U - boolValue.
    (BTW, this optimization is kind of clever, but maybe shortsighted vs. branching and inlining memcpyas stores of immediate data2.) Or the compiler could have created a table of pointers and indexed it with the integer value of the bool, again assuming it was a 0 or 1. (This possibility is what @Barmar's answer suggested.)



    Your __attribute((noinline)) constructor with optimization enabled led to clang just loading a byte from the stack to use as uninitializedBool. It made space for the object in main with push rax (which is smaller and for various reason about as efficient as sub rsp, 8), so whatever garbage was in AL on entry to main is the value it used for uninitializedBool. This is why you actually got values that weren't just 0.



    5 - random garbage can easily wrap to a large unsigned value, leading memcpy to go into unmapped memory. The destination is in static storage, not the stack, so you're not overwriting a return address or something.





    Other implementations could make different choices, e.g. false=0 and true=any non-zero value. Then clang probably wouldn't make code that crashes for this specific instance of UB. I don't know of any implementations that choose this or anything other what x86-64 does for bool, but the C++ standard allows many things that nobody does or even would want to do on hardware that's anything like current CPUs.



    It's unspecified what you'll find when you examine the object representation of a bool. (e.g. by memcpying the bool into unsigned char, which you're allowed to do because char* can alias anything. And unsigned char is guaranteed to have no padding bits, so the C++ standard does formally let you hexdump object representations without any UB. Pointer-casting to copy the object representation is different from assigning char foo = my_bool, of course, so booleanization to 0 or 1 wouldn't happen and you'd get the raw object representation.)



    You've partially "hidden" the UB on this execution path from the compiler with noinline. Even if it doesn't inline, though, interprocedural optimizations could still make a version of the function that depends on the definition of another function. (First, clang is making an executable, not a Unix shared library where symbol-interposition can happen. Second, the definition in inside the class{} definition so all translation units must have the same definition. Like with the inline keyword.)



    So a compiler could emit just a ret or ud2 (illegal instruction) as the definition for main, because the path of execution starting at the top of main unavoidably encounters Undefined Behaviour.



    Any program that encounters UB is totally undefined for its entire existence. But UB inside a function or if() branch that never actually runs doesn't corrupt the rest of the program. In practice that means that compilers can decide to emit an illegal instruction, or a ret, or not emit anything and fall into the next block / function, for the whole basic block that can be proven at compile time to contain or lead to UB.



    GCC and Clang in practice do actually sometimes emit ud2 on UB, instead of even trying to generate code for paths of execution that make no sense. Or for cases like falling off the end of a non-void function, gcc will sometimes omit a ret instruction. If you were thinking that "my function will just return with whatever garbage is in RAX", you are sorely mistaken. Modern C++ compilers don't treat the language like a portable assembly language any more. Your program really has to be valid C++, without making assumptions about how a stand-alone non inlined version of your function might look in asm.



    Another fun example is Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?. x86 doesn't fault on unaligned integers, right? So why would a misaligned uint16_t* be a problem? Because alignof(uint16_t) == 2, and violating that assumption led to a segfault when auto-vectorizing with SSE2.



    See also What Every C Programmer Should Know About Undefined Behavior #1/3, an article by a clang developer.





    Footnote 1: The upper 56 bits can be garbage which the callee must ignore, as usual for types narrower than a register.



    For example, a caller might have calculated a & 0x01010101 in RDI and used it for something else, before calling bool_func(a&1). The caller could optimize away the &1 because it already did that to the low byte as part of and edi, 0x01010101, and it knows the callee is required to ignore the high bytes.



    Or if a bool is passed as the 3rd arg, maybe a caller optimizing for code-size loads it with mov dl, [mem] instead of movzx edx, [mem], saving 1 byte at the cost of a false dependency on the old value of RDX (or other partial-register effect, depending on CPU model). Or for the first arg, mov dil, byte [r10] instead of movzx edi, byte [r10], because both require a REX prefix anyway.



    This is why clang emits movzx eax, dil in Serialize, instead of sub eax, edi. (For integer args, clang violates this ABI rule, instead depending on the undocumented behaviour of gcc and clang to zero- or sign-extend narrow integers to 32 bits. Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?
    So I was interested to see that it doesn't do the same thing for bool.)





    Footnote 2: After branching, you'd just have a 4-byte mov-immediate, or a 4-byte + 1-byte store. The length is implicit in the store widths + offsets.



    OTOH, glibc memcpy will do two 4-byte loads/stores with an overlap that depends on length, so this really does end up making the whole thing free of conditional branches on the boolean. See the L(between_4_7): block in glibc's memcpy/memmove. Or at least, go the same way for either boolean in memcpy's branching to select a chunk size.



    If inlining, you could use 2x mov-immediate + cmov and a conditional offset, or you could leave the string data in memory.



    Or if tuning for Intel Ice Lake (with the Fast Short REP MOV feature), an actual rep movsb might be optimal. glibc memcpy might start using rep movsb for small sizes on CPUs with that feature, saving a lot of branching.






    share|improve this answer














    You're compiling for the x86-64 System V ABI, which specifies that a bool as a function arg in a register is represented by the bit-patterns false=0 and true=1 in the low 8 bits of the register1. In memory, bool is a 1-byte type that again must have an integer value of 0 or 1.



    (An ABI is a set of implementation choices that compilers for the same platform agree on so they can make code that calls each other's functions. The Windows x64 ABI defines bool the same way, and I think most non-x86 platforms do the same. This makes bool->int conversion cheap, just zero-extension, and allows optimizations like compiling a&&b to a bitwise AND for bool types. Some compilers do actually take advantage Boolean values as 8 bit in compilers. Are operations on them inefficient?. Or negate a bool with xor eax,1 to flip the low bit: Any possible code that can flip a bit/integer/bool between 0 and 1 in single CPU instruction)



    The compiler is definitely allowed to take full advantage of an ABI guarantee in its code-gen, and make code like you found which optimizes strlen(whichString) to
    5U - boolValue.
    (BTW, this optimization is kind of clever, but maybe shortsighted vs. branching and inlining memcpyas stores of immediate data2.) Or the compiler could have created a table of pointers and indexed it with the integer value of the bool, again assuming it was a 0 or 1. (This possibility is what @Barmar's answer suggested.)



    Your __attribute((noinline)) constructor with optimization enabled led to clang just loading a byte from the stack to use as uninitializedBool. It made space for the object in main with push rax (which is smaller and for various reason about as efficient as sub rsp, 8), so whatever garbage was in AL on entry to main is the value it used for uninitializedBool. This is why you actually got values that weren't just 0.



    5 - random garbage can easily wrap to a large unsigned value, leading memcpy to go into unmapped memory. The destination is in static storage, not the stack, so you're not overwriting a return address or something.





    Other implementations could make different choices, e.g. false=0 and true=any non-zero value. Then clang probably wouldn't make code that crashes for this specific instance of UB. I don't know of any implementations that choose this or anything other what x86-64 does for bool, but the C++ standard allows many things that nobody does or even would want to do on hardware that's anything like current CPUs.



    It's unspecified what you'll find when you examine the object representation of a bool. (e.g. by memcpying the bool into unsigned char, which you're allowed to do because char* can alias anything. And unsigned char is guaranteed to have no padding bits, so the C++ standard does formally let you hexdump object representations without any UB. Pointer-casting to copy the object representation is different from assigning char foo = my_bool, of course, so booleanization to 0 or 1 wouldn't happen and you'd get the raw object representation.)



    You've partially "hidden" the UB on this execution path from the compiler with noinline. Even if it doesn't inline, though, interprocedural optimizations could still make a version of the function that depends on the definition of another function. (First, clang is making an executable, not a Unix shared library where symbol-interposition can happen. Second, the definition in inside the class{} definition so all translation units must have the same definition. Like with the inline keyword.)



    So a compiler could emit just a ret or ud2 (illegal instruction) as the definition for main, because the path of execution starting at the top of main unavoidably encounters Undefined Behaviour.



    Any program that encounters UB is totally undefined for its entire existence. But UB inside a function or if() branch that never actually runs doesn't corrupt the rest of the program. In practice that means that compilers can decide to emit an illegal instruction, or a ret, or not emit anything and fall into the next block / function, for the whole basic block that can be proven at compile time to contain or lead to UB.



    GCC and Clang in practice do actually sometimes emit ud2 on UB, instead of even trying to generate code for paths of execution that make no sense. Or for cases like falling off the end of a non-void function, gcc will sometimes omit a ret instruction. If you were thinking that "my function will just return with whatever garbage is in RAX", you are sorely mistaken. Modern C++ compilers don't treat the language like a portable assembly language any more. Your program really has to be valid C++, without making assumptions about how a stand-alone non inlined version of your function might look in asm.



    Another fun example is Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?. x86 doesn't fault on unaligned integers, right? So why would a misaligned uint16_t* be a problem? Because alignof(uint16_t) == 2, and violating that assumption led to a segfault when auto-vectorizing with SSE2.



    See also What Every C Programmer Should Know About Undefined Behavior #1/3, an article by a clang developer.





    Footnote 1: The upper 56 bits can be garbage which the callee must ignore, as usual for types narrower than a register.



    For example, a caller might have calculated a & 0x01010101 in RDI and used it for something else, before calling bool_func(a&1). The caller could optimize away the &1 because it already did that to the low byte as part of and edi, 0x01010101, and it knows the callee is required to ignore the high bytes.



    Or if a bool is passed as the 3rd arg, maybe a caller optimizing for code-size loads it with mov dl, [mem] instead of movzx edx, [mem], saving 1 byte at the cost of a false dependency on the old value of RDX (or other partial-register effect, depending on CPU model). Or for the first arg, mov dil, byte [r10] instead of movzx edi, byte [r10], because both require a REX prefix anyway.



    This is why clang emits movzx eax, dil in Serialize, instead of sub eax, edi. (For integer args, clang violates this ABI rule, instead depending on the undocumented behaviour of gcc and clang to zero- or sign-extend narrow integers to 32 bits. Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?
    So I was interested to see that it doesn't do the same thing for bool.)





    Footnote 2: After branching, you'd just have a 4-byte mov-immediate, or a 4-byte + 1-byte store. The length is implicit in the store widths + offsets.



    OTOH, glibc memcpy will do two 4-byte loads/stores with an overlap that depends on length, so this really does end up making the whole thing free of conditional branches on the boolean. See the L(between_4_7): block in glibc's memcpy/memmove. Or at least, go the same way for either boolean in memcpy's branching to select a chunk size.



    If inlining, you could use 2x mov-immediate + cmov and a conditional offset, or you could leave the string data in memory.



    Or if tuning for Intel Ice Lake (with the Fast Short REP MOV feature), an actual rep movsb might be optimal. glibc memcpy might start using rep movsb for small sizes on CPUs with that feature, saving a lot of branching.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 11 hours ago

























    answered 15 hours ago









    Peter CordesPeter Cordes

    120k16183312




    120k16183312












    • gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
      – liliscent
      12 hours ago










    • This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
      – SimonC
      12 hours ago










    • @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
      – Peter Cordes
      11 hours ago


















    • gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
      – liliscent
      12 hours ago










    • This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
      – SimonC
      12 hours ago










    • @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
      – Peter Cordes
      11 hours ago
















    gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
    – liliscent
    12 hours ago




    gcc and clang in practice do actually sometimes emit ud2. Or for cases like falling off the end of a non-void function... ---> I'm tempting to bolden this specific sentence, it's actually quite common to get a SIGILL if you ignore that compiler warning. I'm pretty sure almost everyone has encountered this before.
    – liliscent
    12 hours ago












    This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
    – SimonC
    12 hours ago




    This answer deserves way more upvotes. Thanks for clarifying everything so well and including the links. Got more than I bargained for.
    – SimonC
    12 hours ago












    @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
    – Peter Cordes
    11 hours ago




    @liliscent: good suggestion, that kind of behaviour when UB is visible at compile time is fundamentally different from how compilers used to behave, and is probably surprising to a lot of people. (I like it; noisy failure is a good thing, especially when there's no clear way to interpret something in the source, like falling off the end of a function.)
    – Peter Cordes
    11 hours ago













    31














    The compiler is allowed to assume that a boolean value passed as an argument is a valid boolean value (i.e. one which has been initialised or converted to true or false). The true value doesn't have to be the same as the integer 1 -- indeed, there can be various representations of true and false -- but the parameter must be some valid representation of one of those two values, where "valid representation" is implementation-defined.



    So if you fail to initialise a bool, or if you succeed in overwriting it through some pointer of a different type, then the compiler's assumptions will be wrong and Undefined Behaviour will ensue. You had been warned:




    50) Using a bool value in ways described by this International Standard as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false. (Footnote to para 6 of §6.9.1, Fundamental Types)







    share|improve this answer



















    • 10




      The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
      – ShadowRanger
      22 hours ago








    • 1




      @ShadowRanger You can always inspect the object representation directly.
      – T.C.
      22 hours ago






    • 5




      @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
      – rici
      22 hours ago








    • 2




      Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
      – Peter Cordes
      16 hours ago






    • 2




      The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
      – Holger
      14 hours ago
















    31














    The compiler is allowed to assume that a boolean value passed as an argument is a valid boolean value (i.e. one which has been initialised or converted to true or false). The true value doesn't have to be the same as the integer 1 -- indeed, there can be various representations of true and false -- but the parameter must be some valid representation of one of those two values, where "valid representation" is implementation-defined.



    So if you fail to initialise a bool, or if you succeed in overwriting it through some pointer of a different type, then the compiler's assumptions will be wrong and Undefined Behaviour will ensue. You had been warned:




    50) Using a bool value in ways described by this International Standard as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false. (Footnote to para 6 of §6.9.1, Fundamental Types)







    share|improve this answer



















    • 10




      The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
      – ShadowRanger
      22 hours ago








    • 1




      @ShadowRanger You can always inspect the object representation directly.
      – T.C.
      22 hours ago






    • 5




      @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
      – rici
      22 hours ago








    • 2




      Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
      – Peter Cordes
      16 hours ago






    • 2




      The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
      – Holger
      14 hours ago














    31












    31








    31






    The compiler is allowed to assume that a boolean value passed as an argument is a valid boolean value (i.e. one which has been initialised or converted to true or false). The true value doesn't have to be the same as the integer 1 -- indeed, there can be various representations of true and false -- but the parameter must be some valid representation of one of those two values, where "valid representation" is implementation-defined.



    So if you fail to initialise a bool, or if you succeed in overwriting it through some pointer of a different type, then the compiler's assumptions will be wrong and Undefined Behaviour will ensue. You had been warned:




    50) Using a bool value in ways described by this International Standard as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false. (Footnote to para 6 of §6.9.1, Fundamental Types)







    share|improve this answer














    The compiler is allowed to assume that a boolean value passed as an argument is a valid boolean value (i.e. one which has been initialised or converted to true or false). The true value doesn't have to be the same as the integer 1 -- indeed, there can be various representations of true and false -- but the parameter must be some valid representation of one of those two values, where "valid representation" is implementation-defined.



    So if you fail to initialise a bool, or if you succeed in overwriting it through some pointer of a different type, then the compiler's assumptions will be wrong and Undefined Behaviour will ensue. You had been warned:




    50) Using a bool value in ways described by this International Standard as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false. (Footnote to para 6 of §6.9.1, Fundamental Types)








    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 22 hours ago

























    answered 23 hours ago









    ricirici

    152k19134198




    152k19134198








    • 10




      The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
      – ShadowRanger
      22 hours ago








    • 1




      @ShadowRanger You can always inspect the object representation directly.
      – T.C.
      22 hours ago






    • 5




      @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
      – rici
      22 hours ago








    • 2




      Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
      – Peter Cordes
      16 hours ago






    • 2




      The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
      – Holger
      14 hours ago














    • 10




      The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
      – ShadowRanger
      22 hours ago








    • 1




      @ShadowRanger You can always inspect the object representation directly.
      – T.C.
      22 hours ago






    • 5




      @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
      – rici
      22 hours ago








    • 2




      Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
      – Peter Cordes
      16 hours ago






    • 2




      The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
      – Holger
      14 hours ago








    10




    10




    The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
    – ShadowRanger
    22 hours ago






    The "true value doesn't have to be the same as the integer 1" is kind of misleading. Sure, the actual bit pattern could be something else, but when implicitly converted/promoted (the only way you'd see a value other than true/false), true is always 1, and false is always 0. Of course, such a compiler would also be unable to use the trick this compiler was trying to use (using the fact that bools actual bit pattern could only be 0 or 1), so it's kind of irrelevant to the OP's problem.
    – ShadowRanger
    22 hours ago






    1




    1




    @ShadowRanger You can always inspect the object representation directly.
    – T.C.
    22 hours ago




    @ShadowRanger You can always inspect the object representation directly.
    – T.C.
    22 hours ago




    5




    5




    @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
    – rici
    22 hours ago






    @shadowranger: my point is that the implementation is in charge. If it limits valid representations of true to the bit pattern 1, that's its prerogative. If it chooses some other set of representations, then it indeed could not use the optimisation noted here. If it does choose that particular representation, then it can. It only needs to be internally consistent. You can examine the representation of a bool by copying it into a byte array; that is not UB (but it is implementation-defined)
    – rici
    22 hours ago






    2




    2




    Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
    – Peter Cordes
    16 hours ago




    Yes, optimizing compilers (i.e. real-world C++ implementation) often will sometimes emit code that depends on a bool having a bit-pattern of 0 or 1. They don't re-booleanize a bool every time they read it from memory (or a register holding a function arg). That's what this answer is saying. examples: gcc4.7+ can optimize return a||b to or eax, edi in a function returning bool, or MSVC can optimize a&b to test cl, dl. x86's test is a bitwise and, so if cl=1 and dl=2 test sets flags according to cl&dl = 0.
    – Peter Cordes
    16 hours ago




    2




    2




    The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
    – Holger
    14 hours ago




    The point about undefined behavior is that the compiler is allowed to draw far more conclusions about it, e.g. to assume that a code path which would lead to accessing an uninitialized value is never taken at all, as ensuring that is precisely the responsibility of the programmer. So it’s not just about the possibility that the low level values could be different than zero or one.
    – Holger
    14 hours ago











    23














    The function itself is correct, but in your test program, the statement that calls the function causes undefined behaviour by using the value of an uninitialized variable.



    The bug is in the calling function, and it could be detected by code review or static analysis of the calling function. Using your compiler explorer link, the gcc 8.2 compiler does detect the bug. (Maybe you could file a bug report against clang that it doesn't find the problem).



    Undefined behaviour means anything can happen, which includes the program crashing a few lines after the event that triggered the undefined behaviour.



    NB. The answer to "Can undefined behaviour cause _____ ?" is always "Yes". That's literally the definition of undefined behaviour.






    share|improve this answer

















    • 1




      Is the first clause true? Does merely copying an uninitialized bool trigger UB?
      – Joshua Green
      21 hours ago






    • 5




      @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
      – M.M
      21 hours ago








    • 5




      @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
      – David Schwartz
      13 hours ago










    • Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
      – MSalters
      5 hours ago










    • On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
      – supercat
      3 hours ago
















    23














    The function itself is correct, but in your test program, the statement that calls the function causes undefined behaviour by using the value of an uninitialized variable.



    The bug is in the calling function, and it could be detected by code review or static analysis of the calling function. Using your compiler explorer link, the gcc 8.2 compiler does detect the bug. (Maybe you could file a bug report against clang that it doesn't find the problem).



    Undefined behaviour means anything can happen, which includes the program crashing a few lines after the event that triggered the undefined behaviour.



    NB. The answer to "Can undefined behaviour cause _____ ?" is always "Yes". That's literally the definition of undefined behaviour.






    share|improve this answer

















    • 1




      Is the first clause true? Does merely copying an uninitialized bool trigger UB?
      – Joshua Green
      21 hours ago






    • 5




      @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
      – M.M
      21 hours ago








    • 5




      @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
      – David Schwartz
      13 hours ago










    • Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
      – MSalters
      5 hours ago










    • On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
      – supercat
      3 hours ago














    23












    23








    23






    The function itself is correct, but in your test program, the statement that calls the function causes undefined behaviour by using the value of an uninitialized variable.



    The bug is in the calling function, and it could be detected by code review or static analysis of the calling function. Using your compiler explorer link, the gcc 8.2 compiler does detect the bug. (Maybe you could file a bug report against clang that it doesn't find the problem).



    Undefined behaviour means anything can happen, which includes the program crashing a few lines after the event that triggered the undefined behaviour.



    NB. The answer to "Can undefined behaviour cause _____ ?" is always "Yes". That's literally the definition of undefined behaviour.






    share|improve this answer












    The function itself is correct, but in your test program, the statement that calls the function causes undefined behaviour by using the value of an uninitialized variable.



    The bug is in the calling function, and it could be detected by code review or static analysis of the calling function. Using your compiler explorer link, the gcc 8.2 compiler does detect the bug. (Maybe you could file a bug report against clang that it doesn't find the problem).



    Undefined behaviour means anything can happen, which includes the program crashing a few lines after the event that triggered the undefined behaviour.



    NB. The answer to "Can undefined behaviour cause _____ ?" is always "Yes". That's literally the definition of undefined behaviour.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered 22 hours ago









    M.MM.M

    104k11115235




    104k11115235








    • 1




      Is the first clause true? Does merely copying an uninitialized bool trigger UB?
      – Joshua Green
      21 hours ago






    • 5




      @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
      – M.M
      21 hours ago








    • 5




      @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
      – David Schwartz
      13 hours ago










    • Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
      – MSalters
      5 hours ago










    • On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
      – supercat
      3 hours ago














    • 1




      Is the first clause true? Does merely copying an uninitialized bool trigger UB?
      – Joshua Green
      21 hours ago






    • 5




      @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
      – M.M
      21 hours ago








    • 5




      @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
      – David Schwartz
      13 hours ago










    • Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
      – MSalters
      5 hours ago










    • On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
      – supercat
      3 hours ago








    1




    1




    Is the first clause true? Does merely copying an uninitialized bool trigger UB?
    – Joshua Green
    21 hours ago




    Is the first clause true? Does merely copying an uninitialized bool trigger UB?
    – Joshua Green
    21 hours ago




    5




    5




    @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
    – M.M
    21 hours ago






    @JoshuaGreen see [dcl.init]/12 "If an indeterminate value is produced by an evaluation, the behaviour is undefined except in the following cases:" (and none of those cases have an exception for bool). Copying requires evaluating the source
    – M.M
    21 hours ago






    5




    5




    @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
    – David Schwartz
    13 hours ago




    @JoshuaGreen And the reason for that is that you might have a platform that triggers a hardware fault if you access some invalid values for some types. These are sometimes called "trap representations".
    – David Schwartz
    13 hours ago












    Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
    – MSalters
    5 hours ago




    Itanium, while obscure, is a CPU that's still in production, has trap values, and has two at least semi-modern C++ compilers (Intel/HP). It literally has true, false and not-a-thing values for booleans.
    – MSalters
    5 hours ago












    On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
    – supercat
    3 hours ago




    On the flip side, the answer to "Does the standard require all compilers to process something a certain way" is generally "no", even/especially in cases where it's obvious that any quality compiler should do so; the more obvious something is, the less need there should be for the authors of the Standard to actually say it.
    – supercat
    3 hours ago











    11














    A bool is only allowed to hold the values 0 or 1, and the generated code can assume that it will only hold one of these two values. The code generated for the ternary in the assignment could use the value as the index into an array of pointers to the two strings, i.e. it might be converted to something like:



         // the compile could make asm that "looks" like this, from your source
    const static char *strings = {"false", "true"};
    const char *whichString = strings[boolValue];


    If boolValue is uninitialized, it could actually hold any integer value, which would then cause accessing outside the bounds of the strings array.






    share|improve this answer























    • @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
      – Barmar
      22 hours ago










    • You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
      – Remz
      22 hours ago






    • 1




      @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
      – Barmar
      22 hours ago








    • 1




      @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
      – Havenard
      22 hours ago








    • 1




      @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
      – Sid S
      20 hours ago
















    11














    A bool is only allowed to hold the values 0 or 1, and the generated code can assume that it will only hold one of these two values. The code generated for the ternary in the assignment could use the value as the index into an array of pointers to the two strings, i.e. it might be converted to something like:



         // the compile could make asm that "looks" like this, from your source
    const static char *strings = {"false", "true"};
    const char *whichString = strings[boolValue];


    If boolValue is uninitialized, it could actually hold any integer value, which would then cause accessing outside the bounds of the strings array.






    share|improve this answer























    • @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
      – Barmar
      22 hours ago










    • You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
      – Remz
      22 hours ago






    • 1




      @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
      – Barmar
      22 hours ago








    • 1




      @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
      – Havenard
      22 hours ago








    • 1




      @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
      – Sid S
      20 hours ago














    11












    11








    11






    A bool is only allowed to hold the values 0 or 1, and the generated code can assume that it will only hold one of these two values. The code generated for the ternary in the assignment could use the value as the index into an array of pointers to the two strings, i.e. it might be converted to something like:



         // the compile could make asm that "looks" like this, from your source
    const static char *strings = {"false", "true"};
    const char *whichString = strings[boolValue];


    If boolValue is uninitialized, it could actually hold any integer value, which would then cause accessing outside the bounds of the strings array.






    share|improve this answer














    A bool is only allowed to hold the values 0 or 1, and the generated code can assume that it will only hold one of these two values. The code generated for the ternary in the assignment could use the value as the index into an array of pointers to the two strings, i.e. it might be converted to something like:



         // the compile could make asm that "looks" like this, from your source
    const static char *strings = {"false", "true"};
    const char *whichString = strings[boolValue];


    If boolValue is uninitialized, it could actually hold any integer value, which would then cause accessing outside the bounds of the strings array.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 15 hours ago









    Peter Cordes

    120k16183312




    120k16183312










    answered 23 hours ago









    BarmarBarmar

    421k35244345




    421k35244345












    • @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
      – Barmar
      22 hours ago










    • You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
      – Remz
      22 hours ago






    • 1




      @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
      – Barmar
      22 hours ago








    • 1




      @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
      – Havenard
      22 hours ago








    • 1




      @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
      – Sid S
      20 hours ago


















    • @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
      – Barmar
      22 hours ago










    • You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
      – Remz
      22 hours ago






    • 1




      @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
      – Barmar
      22 hours ago








    • 1




      @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
      – Havenard
      22 hours ago








    • 1




      @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
      – Sid S
      20 hours ago
















    @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
    – Barmar
    22 hours ago




    @SidS Thanks. Theoretically, the internal representations could be the opposite of how they cast to/from integers, but that would be perverse.
    – Barmar
    22 hours ago












    You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
    – Remz
    22 hours ago




    You are right, and your example will also crash. However it is "visible" to a code review that you are using an uninitialized variable as an index to an array. Also, it would crash even in debug (for example some debugger/compiler will initialize with specific patterns to make it easier to see when it crashes). In my example, the surprising part is that the usage of the bool is invisible: The optimizer decided to use it in a calculation not present in the source code.
    – Remz
    22 hours ago




    1




    1




    @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
    – Barmar
    22 hours ago






    @Remz I'm just using the array to show what the generated code could be equivalent to, not suggesting that anyone would actually write that.
    – Barmar
    22 hours ago






    1




    1




    @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
    – Havenard
    22 hours ago






    @Remz Recast the bool to int with *(int *)&boolValue and print it for debugging purposes, see if it is anything other than 0 or 1 when it crashes. If that's the case, it pretty much confirms the theory that the compiler is optimizing the inline-if as an array which explains why it is crashing.
    – Havenard
    22 hours ago






    1




    1




    @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
    – Sid S
    20 hours ago




    @Havenard, int is likely to be bigger than bool so that wouldn't prove anything.
    – Sid S
    20 hours ago











    4














    Summarising your question a lot, you are asking Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way?



    The standard says nothing about the internal representation of a bool. It only defines what happens when casting a bool to an int (or vice versa). Mostly, because of these integral conversions (and the fact that people rely rather heavily on them), the compiler will use 0 and 1, but it doesn't have to (although it has to respect the constraints of any lower level ABI it uses).



    So, the compiler, when it sees a bool is entitled to consider that said bool contains either of the 'true' or 'false' bit patterns and do anything it feels like. So if the values for true and false are 1 and 0, respectively, the compiler is indeed allowed to optimise strlen to 5 - <boolean value>. Other fun behaviours are possible!



    As gets repeatedly stated here, undefined behaviour has undefined results. Including but not limited to




    • Your code working as you expected it to

    • Your code failing at random times

    • Your code not being run at all.


    See What every programmer should know about undefined behavior






    share|improve this answer


























      4














      Summarising your question a lot, you are asking Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way?



      The standard says nothing about the internal representation of a bool. It only defines what happens when casting a bool to an int (or vice versa). Mostly, because of these integral conversions (and the fact that people rely rather heavily on them), the compiler will use 0 and 1, but it doesn't have to (although it has to respect the constraints of any lower level ABI it uses).



      So, the compiler, when it sees a bool is entitled to consider that said bool contains either of the 'true' or 'false' bit patterns and do anything it feels like. So if the values for true and false are 1 and 0, respectively, the compiler is indeed allowed to optimise strlen to 5 - <boolean value>. Other fun behaviours are possible!



      As gets repeatedly stated here, undefined behaviour has undefined results. Including but not limited to




      • Your code working as you expected it to

      • Your code failing at random times

      • Your code not being run at all.


      See What every programmer should know about undefined behavior






      share|improve this answer
























        4












        4








        4






        Summarising your question a lot, you are asking Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way?



        The standard says nothing about the internal representation of a bool. It only defines what happens when casting a bool to an int (or vice versa). Mostly, because of these integral conversions (and the fact that people rely rather heavily on them), the compiler will use 0 and 1, but it doesn't have to (although it has to respect the constraints of any lower level ABI it uses).



        So, the compiler, when it sees a bool is entitled to consider that said bool contains either of the 'true' or 'false' bit patterns and do anything it feels like. So if the values for true and false are 1 and 0, respectively, the compiler is indeed allowed to optimise strlen to 5 - <boolean value>. Other fun behaviours are possible!



        As gets repeatedly stated here, undefined behaviour has undefined results. Including but not limited to




        • Your code working as you expected it to

        • Your code failing at random times

        • Your code not being run at all.


        See What every programmer should know about undefined behavior






        share|improve this answer












        Summarising your question a lot, you are asking Does the C++ standard allow a compiler to assume a bool can only have an internal numerical representation of '0' or '1' and use it in such a way?



        The standard says nothing about the internal representation of a bool. It only defines what happens when casting a bool to an int (or vice versa). Mostly, because of these integral conversions (and the fact that people rely rather heavily on them), the compiler will use 0 and 1, but it doesn't have to (although it has to respect the constraints of any lower level ABI it uses).



        So, the compiler, when it sees a bool is entitled to consider that said bool contains either of the 'true' or 'false' bit patterns and do anything it feels like. So if the values for true and false are 1 and 0, respectively, the compiler is indeed allowed to optimise strlen to 5 - <boolean value>. Other fun behaviours are possible!



        As gets repeatedly stated here, undefined behaviour has undefined results. Including but not limited to




        • Your code working as you expected it to

        • Your code failing at random times

        • Your code not being run at all.


        See What every programmer should know about undefined behavior







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 13 hours ago









        Tom TannerTom Tanner

        7,91022249




        7,91022249






















            Remz is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            Remz is a new contributor. Be nice, and check out our Code of Conduct.













            Remz is a new contributor. Be nice, and check out our Code of Conduct.












            Remz is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54120862%2fcan-an-uninitialized-bool-crash-a-program%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            How to make a Squid Proxy server?

            第一次世界大戦

            Touch on Surface Book