Path: csiph.com!weretis.net!feeder8.news.weretis.net!eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Corrupted C99 _Bool? Date: Thu, 20 Jul 2023 22:17:51 -0700 Organization: A noiseless patient Spider Lines: 90 Message-ID: <86o7k5vo5c.fsf@linuxsc.com> References: <2569e26b-c60a-4873-a759-ee00a50e2bf9n@googlegroups.com> <875y6oq10u.fsf@nosuchdomain.example.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: dont-email.me; posting-host="2d78163bfb891e7187c96a580a5a5784"; logging-data="3290646"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18sfTfjGiFlT2gLz9BiYLkYmXCh7J0QbV8=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:MijxG0jB4I/8InBgd7yAN+alGMc= sha1:pyI7KiRnCeJNtF/tU36fKwTqzlA= Xref: csiph.com comp.lang.c:171011 Keith Thompson writes: > Opus writes: > >> On 12/07/2023 02:32, das...@gmail.com wrote: >> >>> C99 indicates that a _Bool is an unsigned integer type. When a >>> variable of a type other than _Bool is assigned to a _Bool, C99 >>> specifies that the _Bool receives the value of 0 if the variable was >>> 0, or 1 otherwise. >>> But what does C99 say about a _Bool that is corrupted to a value >>> other than 0 or 1 (perhaps through a pointer error in another part >>> of the program)? Will this corrupted _Bool still function >>> correctly? >> >> It's undefined behavior. Just like if you corrupt any value of any >> other type. >> >>> For example: >>> _Bool a = true; >>> _Bool b; /* Assume b is corrupted to "2" somehow */ >>> if (a && b) >>> { >>> /* Would the compiler be able to use a bitwise */ >>> /* AND above, because it assumes all _Bools */ >>> /* must be 0 or 1, so that if (1 && 2) becomes */ >>> /* if (1 & 2) and evaluates false? */ >>> } >>> Thanks for any clarification. >> >> If the memory holding a _Bool value gets corrupted, there is obviously >> no way for the compiler to automatically correct it. How would it? >> >> It's UB, but the practical result will of course depend on the >> target. A _Bool is held by an integer under the hood in practice. >> >> Compilers do force a value of 1 for any value other than 0 assigned to >> it *during assignment*, and 0 otherwise. But again, only when an >> assignment occurs (or a cast). > > More precisely, *conversion* from any scalar type to _Bool yields a > value of 0 if the converted value is 0, 1 otherwise. Assignment > performs an implicit conversion; so do argument passing, initialization, > and a return statement. And of course a cast performs an explicit > conversion. > >> For instance, the expression '(int)(_Bool) 5' yields 1. >> >> But if you alias a _Bool, say: >> >> _Bool b; >> >> *(int *) &b = 5; >> >> While UB, in practice in most implementations, b will be represented >> internally as an int and here will hold the value 5. Not 1. > > _Bool is most likely smaller than int, so the assignment is likely to > clobber bytes not belonging to the _Bool object. > >> This would cause a problem if you expect the value to be either 0 or 1 >> exclusively. For instance if you cast it back to an int and do some >> artihmetic or bitwise operations with that. >> >> Regarding what would happen to '(a && b)' in your example, the '&&' >> operator is NOT a bitwise operator. Even when dealing strictly with >> _Bool's on each side of the operator, it can't be implemented with a >> bitwise operation when compiled. Quoting the standard regarding the >> '&&' operator: >> >> "The && operator shall yield 1 if both of its operands compare unequal >> to 0; otherwise, it yields 0. The result has type int. >> Unlike the bitwise binary & operator, the && operator guarantees left >> to-right evaluation; >> if the second operand is evaluated, there is a sequence point between >> the evaluations of the first and second operands. If the first operand >> compares equal to 0, the second operand is not evaluated." > > Yes, but if a and b are both of type _Bool, the compiler can > assume that they both have a value of 0 or 1. [...] Whether a compiler can safely make such an assumption depends on implementation choices pertaining to how _Bool objects are represented. > A compiler *can* implement `a && b` as `a & b` if both are of type > _Bool and neither is volatile. Again, such a conclusion might or might not be valid, depending on other aspects of the implementation.