jyegiguere@watmath.UUCP (07/08/87)
There's been a debate raging on the nets lately about what is a "proper" definition for the TRUE and FALSE macros in C. The way we define these at the Computer Systems Group here at UW (which we include in the <stdio.h> file for Waterloo C) is: #define TRUE ( 0 == 0 ) #define FALSE ( 1 == 0 ) The point here is that instead of letting the program arbitrarily decide what TRUE and FALSE should be, we let the compiler decide, which adds to the portability. Since the above definitions are simple constant expressions, any compiler worth its salt should recognize them as such and not generate any less efficient code than a direct comparison to a straight integer constant. In actual effect, the definition for FALSE above could just as well be replaced with #define FALSE 0 since in C only non-zero values are considered to be 'true' in logical expressions. People have noted that some programmers code things like if( my_func() == TRUE ) when my_func() returns a non-zero value that isn't necessarily the same as TRUE. In such cases why not code if( my_func() != FALSE ) since anyone who declares FALSE to be anything other than zero is going to run into a few problems. I still prefer the TRUE form if I know for SURE that the function will return either TRUE or FALSE as defined in <stdio.h>. In any case, I find both forms above more readable than if( my_func() ) or if( !my_func() ) This, however, is due to personal preferences and I don't believe that one form is right and the other wrong (as long as they both do the exact same thing!) ----------------------------- Eric Giguere Computer Systems Group, UW Disclaimer: The above views are NOT intended to represent those of either the Computer Systems Group or the University of Waterloo.
ado@elsie.UUCP (07/09/87)
In article <13851@watmath.UUCP>, jyegiguere@watmath.UUCP (Eric Giguere) writes: > The way we define these at the Computer Systems Group here at UW > (which we include in the <stdio.h> file for Waterloo C) > is: > > #define TRUE ( 0 == 0 ) > #define FALSE ( 1 == 0 ) I used to do this (well, actually, #define FALSE (!TRUE) is what I'd use), but code such as #define TRUE (0 == 0) #define FALSE (0 != 0) main() { int i; i = TRUE; return i; } gets flagged if you use lint's "-h" option: try.c(8): warning: constant in conditional context To keep lint quiet, I've reverted to #define TRUE 1 #define FALSE 0 -- UUCP: ..seismo!elsie!ado ARPA: elsie!ado@seismo.CSS.GOV Elsie and Ado are trademarks of Borden, Inc. and Ampex.
alyce@itsgw.UUCP (07/09/87)
For years I worked on projects that defined typedef enum {FALSE, TRUE} bool; in a project-wide header file. FALSE & TRUE were only used (by most programmers) in assignments; "if (first_time)" and "if ( !found )" were the style used for comparisons. We could define variables and functions to be of type bool, and we always had this dream that someday we would have a version of lint that would warn against setting or comparing bool variables to non-bool values. Even now that I'm back in school, where standards & conventions are unheard of, I still use the bool type because I find it useful, readable, & maintainable.
guy%gorodish@Sun.COM (Guy Harris) (07/09/87)
> #define TRUE ( 0 == 0 ) > #define FALSE ( 1 == 0 ) Which can be replaced by #define TRUE 1 #define FALSE 0 in any C implementation that isn't horribly broken. From K&R, page 189: 7.6 Relational operators ... The operators < (less than), ..., all yield 0 if the specified relation is false and 1 if it is true. ... 7.7 Equality operators ... The == (equal to) and the != (not equal to) operators are exactly analogous to the relational operators except for their lower precedence. (Thus a<b == c<d is 1 whenever a<b and c<d have the same truth value.) The ANSI C draft says much the same thing. Unless substantial evidence indicates otherwise, I assume that any compiler I deal with will at least get the basics of C right (if a compiler I use doesn't, I'll tell its maintainer ASAP, and possibly look at the problem myself). Even if there are such compilers, if I don't have to deal with them I will write my code as if I could use standard C safely, because I don't want to encourage screwups like that; if I have to work around a compiler bug, I'll flag the code in question as being a workaround. > People have noted that some programmers code things like > > if( my_func() == TRUE ) > > when my_func() returns a non-zero value that isn't necessarily the > same as TRUE. In such cases why not code > > if( my_func() != FALSE ) > > since anyone who declares FALSE to be anything other than zero is > going to run into a few problems. I presume that in this case "my_func()" returns something to be thought of as a Boolean, except that any non-zero value, not just 1, maps to TRUE. If the function doesn't return a value that is a Boolean (since C doesn't permit you to explicitly say that something is Boolean, this means that it is not intended to be thought of as Boolean), you shouldn't compare it against TRUE *or* FALSE; if you want to know if the return value from "my_func" is zero, compare it against zero. Guy Harris {ihnp4, decvax, seismo, decwrl, ...}!sun!guy guy@sun.com
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/10/87)
In article <13851@watmath.UUCP> jyegiguere@watmath.waterloo.edu (Eric Giguere) writes: > #define TRUE ( 0 == 0 ) > #define FALSE ( 1 == 0 ) >The point here is that instead of letting the program arbitrarily >decide what TRUE and FALSE should be, we let the compiler decide, >which adds to the portability. You're under a misconception; nothing whatever is gained by this. See my next comment. >In actual effect, the definition for FALSE above could just as well >be replaced with > #define FALSE 0 Furthermore, you might have well written #define TRUE 1 since (1 == 0) has precisely the value 1 in C. In C expression evaluation, relationals and other nominally Boolean operators produce either 0 or 1 for their value -- never anything else. In any context where the "truth" of an expression is being tested (for example in an "if" condition), any non-zero expression is considered to be "true" and only zero is considered "false"; thus testing for truth works with a superset of the values produced by expressions that compute truth values. All this is built into the rules of the C language and is NOT up to the implementation. I for one wish C had been designed with an explicit Boolean data type distinct from integral types, but it wasn't. I do find it helpful to maintain the conceptual distinction in one's code, however, never using an arithmetic expression where a relational test (against 0) is called for, and never performing arithmetic on conceptually Boolean data. ("What, never?" "No, never!" "What, never?!" "Well, hardly ever!")
ron@topaz.rutgers.edu (Ron Natalie) (07/10/87)
Poeple seem to be missing the point. If you are going to define your own boolean type, it doesn't matter what you use for TRUE and FALSE as long as you're consistant. TRUE can be 't' and FALSE could be 'f' as long as you always use them that way. The problem is that C defines FALSE to be zero and TRUE to be non zero. Hence, while you may define FALSE to be 0, there is nothing you can define true to be that will consistantly work as things that set true values are free to use any non-zero number. The correct test for truth is the value by itself, for example if(expr) while(expr) for(;expr;) expr ? x : y FALSE can either be !expr or expr == 0 (or its #define'd equivelents). --------------------------------------------------------------------- On a different light, I've noticed nobody has mentioned the construct of seting a variable of TRUE by incrementing it. Many, many programs process their options by code like the following: switch(*argv[1]) { case 'b': bflag++; break; case 'c': cflag++; break; The flag variables are unitialized extern chars (and hence are zero). Some times however, this approach is used inside of loops, I avoid using this because I am always afraid that someday someone will cause the code to be executed exactly 256 times. -Ron
ron@topaz.rutgers.edu (Ron Natalie) (07/10/87)
FROM CTYPE.H (essentially similar in both System V and BSD): #define isupper(c) ((_ctype_+1)[c]&_U) Guy, just since relational operators are defined to return 0 and 1, does not mean that all truth values fall in this category. If it were, then the conditional expressions would have been trained to only consider 0 and 1. -Ron
jyegiguere@watmath.UUCP (07/12/87)
There have been some interesting comments, all valid. Yes, in a proper implementation #define TRUE 1 #define FALSE 0 should work properly. The solution I offered was one that should guarantee portability. The use of an enumeration typedef enum { FALSE, TRUE } bool; is fine as long as you're sure that your programs will only be used on systems that support enumerations. (Thankfully, most of them do support it.) I think, however, that lint is pretty picky if it flags an expression of the form i = ( 0 == 0 ); which is perfectly valid if you read the grammar to the language. Any decent compiler will simply fold this value into the appropriate constant value. ------------------------ Eric Giguere
guy%gorodish@Sun.COM (Guy Harris) (07/12/87)
> There have been some interesting comments, all valid. Yes, in a > proper implementation > > #define TRUE 1 > #define FALSE 0 > > should work properly. The solution I offered was one that should > guarantee portability. Nope. If you assume that the implementation can get the definition of the "==" operator wrong to the extent that "0 == 0" does not evaluate to 1, and that to be safe you have to define TRUE as "0 == 0", you should also assume that it can get the definition of the "+" operator wrong so that "1 + 1" doesn't evaluate to 2. The definition of the "==" operator is pretty clearly spelled out in all descriptions of C; anybody who gets *that* wrong is capable of just about any screwup. Unless the quality of C implementations out there is *so* dismal that C cannot be taken seriously as a portable language for implementing software (in which case, you shouldn't implementing software in C if you intend to port it), writing code that "defends" against the possibility that an implementation of the language is fundamentally flawed is silly (*and* does not "guarantee portability" unless you defend against practically every screwup that can be imagined), nor does assuming that the implementation is *not* fundamentally flawed run the risk of unportability as you seem to be implying here. > I think, however, that lint is pretty picky if it flags an expression of > the form > > i = ( 0 == 0 ); > > which is perfectly valid if you read the grammar to the language. > Any decent compiler will simply fold this value into the appropriate > constant value. It's syntactically valid, but so what? That's not the point. "lint" complains about this if it is asked to "Apply a number of heuristic tests to attempt to intuit bugs, improve style, and reduce waste." The key words here are "heuristic" and "intuit". It is trying to detect code that is syntactically valid, and perhaps even semantically valid, but possibly wrong anyway. This test will pick up errors such as writing if (datum & MASK == VALUE) rather than if ((datum & MASK) == VALUE) to test whether a subfield of "datum" is equal to a particular value. (This could be considered an argument in favor of bitfields; with bitfields, you can directly express what you mean here, and not fact the possibility of getting the test wrong. You can't use bitfields in all cases - if the format of "datum" is externally imposed, you will probably need the mask, since the order in which bit fields are placed within structure is implementation-dependent.) Guy Harris {ihnp4, decvax, seismo, decwrl, ...}!sun!guy guy@sun.com
guy%gorodish@Sun.COM (Guy Harris) (07/13/87)
> Guy, just since relational operators are defined to return 0 and 1, > does not mean that all truth values fall in this category. Ron, if you can find any claim that all truth values *do* fall into that category in my posting, you have much better eyes than I do. In fact, I quote from that very posting: I presume that in this case "my_func()" returns something to be thought of as a Boolean, except that any non-zero value, not just 1, maps to TRUE. The point is that if somebody's going to define TRUE and FALSE, *with TRUE being the particular value of "true" generated by the relational operators*, there's no point in defining TRUE as "0 == 0" instead of "1". Guy Harris {ihnp4, decvax, seismo, decwrl, ...}!sun!guy guy@sun.com
dhesi@bsu-cs.UUCP (07/13/87)
Never test an expression against TRUE or FALSE. -- Rahul Dhesi UUCP: {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi
devine@vianet.UUCP (Bob Devine) (07/15/87)
In article <13851@watmath.UUCP>, jyegiguere@watmath.UUCP (Eric Giguere) writes: > The way we define these at the Computer Systems Group here at UW > (which we include in the <stdio.h> file for Waterloo C) > is: > #define TRUE ( 0 == 0 ) > #define FALSE ( 1 == 0 ) This doesn't really gain you much. FALSE is still recognized as 0 and TRUE can only be defined as non-zero. Having the compiler fill in the blanks is no step forward. For those who wrote code as "if (such_and_such == TRUE)" believing it to really mean "if (such_and_such != FALSE)", the above does nothing. Bob Devine
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/16/87)
In article <13259@topaz.rutgers.edu> ron@topaz.rutgers.edu (Ron Natalie) writes: >The problem is that C defines FALSE to be zero and TRUE to be non >zero. Hence, while you may define FALSE to be 0, there is nothing >you can define true to be that will consistantly work as things that >set true values are free to use any non-zero number. This isn't quite precise. There are two contexts: (a) setting a "true" value; (b) testing the "truth" of a value. When C specifies the former, for example the value of a relational expression, it specifies that the value will be either 0 or 1 (NOT "any non-zero value" for true); when C specifies the latter, then indeed any non-zero value tests as "true". >The correct test for truth is the value by itself ... Right -- i.e., "if (expr)", NOT "if (expr == TRUE)". >On a different light, I've noticed nobody has mentioned the construct >of seting a variable of TRUE by incrementing it. Yucky! The principle "say what you mean" implies that "flag = TRUE;" is much better than "flag++". Note also that if one changes the initial value of the flag (to have the option enabled by default, for example, while leaving in support for the previous enabling option), the ++ approach gives it a funny value instead of the simple truth value 1.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/16/87)
In article <13260@topaz.rutgers.edu> ron@topaz.rutgers.edu (Ron Natalie) writes: >#define isupper(c) ((_ctype_+1)[c]&_U) One could legitimately consider that to be an implementation bug, although the C spec only calls for these functions to return nonzero if true rather than 1. It does, however, prevent one from doing arithmetic on these function values without first converting them to 0/1 values (via ...!=0). This doesn't bother me too much, since I don't generally approve of arithmetic on Boolean data, but it is unnecessarily sloppy. Note that any reasonable compiler will generate the same code in cases such as "if ( isupper( c ) )" when the macro is defined as: #define isupper( c ) (((_ctype_ + 1)[c] & _U) != 0) I'm considering making this change in my version of ctype.h, much as I have already fixed up feof() and ferror() in stdio.h.
devine@vianet.UUCP (Bob Devine) (07/17/87)
In article <13259@topaz.rutgers.edu> ron@topaz.rutgers.edu (Ron Natalie) writes: >On a different light, I've noticed nobody has mentioned the construct >of seting a variable of TRUE by incrementing it. In article <6123@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > Yucky! The principle "say what you mean" implies that "flag = TRUE;" > is much better than "flag++". I've used this method many times -- but with a purpose in mind. Doing it with a test allows a easy check for multiple use of a option. For example, W_flag is set to 0 at the start and the following code is inside of a getopt() loop. if (W_flag++) { /* AI == Artificial Intimidation */ printf("Don't type the W flag twice again. Or else...\n"); exit(rand()); }
steele@unc.cs.unc.edu (Oliver Steele) (07/19/87)
In article <208@vianet.UUCP> devine@vianet.UUCP (Bob Devine) writes: > I've used this method [writing "flag++" to set the boolean "flag" to >true] many times -- but with a purpose in mind. Doing it with a test >allows a easy check for multiple use of a option. For example, W_flag >is set to 0 at the start and the following code is inside of a getopt() >loop. > > if (W_flag++) > { > /* AI == Artificial Intimidation */ > printf("Don't type the W flag twice again. Or else...\n"); > exit(rand()); > } It's often more useful to have each use be a toggle (W_flag = !W_flag). The default value of the flag can then be reversed by use of an alias or an environment variable without locking the user into that default choice. ------------------------------------------------------------------------------ Oliver Steele ...!{decvax,ihnp4}!mcnc!unc!steele steele%unc@mcnc.org "They're directly beneath us, Moriarty. Release the piano!"
karl@haddock.ISC.COM (Karl Heuer) (07/20/87)
In article <208@vianet.UUCP> devine@vianet.UUCP (Bob Devine) writes: >In article <6123@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: >> Yucky! The principle "say what you mean" implies that "flag = TRUE;" >> is much better than "flag++". > >[But] doing it with a test allows a easy check for multiple use of a option. > if (W_flag++) ... One problem with this is that it's possible to overflow, the most likely case being if you implement boolean with "char" and execute this statement 256 times. (I know your example contained an "exit", but I'm thinking about the general case.) I agree with Doug. I never use boolean "++" in my own code anymore, for the reason he states and also because the notation is misleading: it suggests that "--" can be used symmetrically as set-to-FALSE. I'd rather have a true boolean datatype in the language, with "++" and "--" defined as Set and Clear, respectively. I've done this in C++. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
arnold@apollo.uucp (Ken Arnold) (07/21/87)
In article <13891@watmath.UUCP> Eric Giguere writes: >I think, however, that lint is pretty picky if >it flags an expression of the form > > i = ( 0 == 0 ); > >which is perfectly valid if you read the grammar to the language. >Any decent compiler will simply fold this value into the appropriate >constant value. You seem to have missed the whole point of lint. lint's job is not to check for syntactic correctness. Its job is to check for unusual constructs which *are* syntactically correct, but which are potentially bugs. In the normal case, one does not say if (1 == 2) If such a statement occurrs in the code, it is *probably* because someone mistyped something, or possibly used a #define constant which they didn't realize was a constant. Thus, lint isn't being "pretty picky" -- lint is doing its job. Generally speaking, it's the C compiler's job to check for syntax, and lint's job to check for legal-but-questionable semantics. The only case where lint will not complain about constants in conditional context is while (1) since this is a common construct used in place of for (;;) for reasons which escape me personally, but seem obvious to the people who do it. (Please don't let's start an argument over this -- it is as unimportant as the "i++" stuff, and I'm not attacking you personally if you do this; I'm just insulting your mother :-) Since it is an absolute C truth that any non-zero value is true, and any zero value is false, there is no reason whatsoever to be cute and use such a construct as "0 == 0" to get TRUE, since it is incorrect to test against TRUE anyway. Silicon Graphics did (and probably still does) something like this, and it drove me up a wall and a half, since it made it impossible to run lint on my code unless I #undef'ed TRUE and FALSE, and the #define'ed them myself, which was ugly as sin and twice as expensive (emotionally speaking). Ken Arnold
dparter@ccvaxa.UUCP (07/23/87)
steele@unc.cs.unc.edu writes: [ refering to flag variables ... ] > It's often more useful to have each use be a toggle (W_flag = !W_flag). > The default value of the flag can then be reversed by use of an alias or > an environment variable without locking the user into that default > choice. This is a double-edged sword: Suppose a `new' command (foo) is built on top of an existing, well-known command (bar) using some of bar's 'toggle' flags, and the documentation for foo says something like this: "... all other options are the same as for bar." The well-versed user, who knows that the flags for bar are really toggles, can use "foo -flag" to reverse the decision to make "-flag" the default. The typical user, knowing that he wants the "-flag" option when he uses bar, not knowing it is the default for foo, nor that it is a toggle, uses "bar -flag" -- which does the exact opposite of what he wanted.... This situation is made worse by documentation that does not make it clear that the flags are really toggles, and by the indirection in the foo documentation. --david David W. Parter gould/csd - urbana uucp: ihnp4!uiucdcs!ccvaxa!dparter arpa: dparter@gswd-vms.arpa --or-- dparter@gswd-vms.gould.com
peter@sugar.UUCP (Peter da Silva) (08/03/87)
I have seen programs where flags are turned off with '-' and on with '+'. -- -- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter (I said, NO PHOTOS!)