Re: Learning C with Older books ?.
From: Tim Rentsch (txr_at_alumnus.caltech.edu)
Date: 12/18/04
- Next message: Howard Hinnant: "Re: Why does ANSI not define a function to determine the size of (m)allocated mem? (like _msize)"
- Previous message: Jack Klein: "Re: initializing data at compile time"
- In reply to: Dan Pop: "Re: Learning C with Older books ?."
- Next in thread: Richard Bos: "Re: Learning C with Older books ?."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: 18 Dec 2004 14:41:41 -0800
Dan.Pop@cern.ch (Dan Pop) writes:
> In <kfnzn0mkkia.fsf@alumnus.caltech.edu> Tim Rentsch <txr@alumnus.caltech.edu> writes:
>
> >Doing a Google Groups search with "-W" and "unsigned", and limiting
> >the time to Jan 1, 2003, to the present (and in comp.lang.c and with
> >dan.pop@cern.ch as author) yielded 11 hits. None of these contained
> >any substantive discussion of different -W options.
>
> There is only one "-W" option as far as I know.
Sorry that this description was unclear. The word "constructs"
or "events" (which is gcc's choice, not mine) might have been
better. I meant the different code conditions that -W flags,
something like the following:
-W Print extra warning messages for these events:
o A function can return either with or without a
value.
o An expression-statement or the left-hand side of a
comma expression contains no side effects.
o An unsigned value is compared against zero with <
or <=.
o A comparison like x<=y<=z appears;
o Storage-class specifiers like "static" are not the
first things in a declaration.
o The return type of a function has a type qualifier
such as "const".
o A comparison between signed and unsigned values
could produce an incorrect result when the signed
value is converted to unsigned. (But don't warn
if -Wno-sign-compare is also specified.)
o An aggregate has a partly bracketed initializer.
o An aggregate has an initializer which does not
initialize all members.
Based on your comments below it's only the signed/unsigned comparison
that you object to. Is that right or are there others? For the
sake of discussion let's assume the above list gives the conditions
flagged by -W.
> And my main objection
> to it is that I get the following kind of warnings:
>
> fangorn:~/tmp 127> cat test.c
> int main()
> {
> int i;
>
> for (i = 0; i < sizeof(int); i++) ;
> return 0;
> }
> fangorn:~/tmp 128> gcc -W test.c
> test.c: In function `main':
> test.c:5: warning: comparison between signed and unsigned
>
> that cannot be shut up without using a programming style I do not like
> (either use of unnecessary casts or proliferation of unsigned variables).
I agree that these conditions can be annoying. I do consider them
useful however, both because of potentially incorrect results, and
because of the rules for how signs are dealt with in integral
promotions is one of the few areas where ANSI is at odds with many
(most?) pre-ANSI compilers; I think it helps to raise consciousness
about that. (The ANSI decision to use "value preserving" rather than
"signedness preserving" as the semantics for these promotions was not,
IMO, the best choice; but whether it's a good choice or not is
incidental to considering the warning useful.)
It's nice to see that the comments about the signed/unsigned warning
condition has generated some good discussion further along this
thread.
> Of course, I could disable this specific warning with -Wno-sign-compare
> but I can't find enough value in what's left to justify bothering with
> baroque sequences of -W... options.
Your regular compilation environment must be different from mine. I
hardly ever either type in or see any of the various compiler flags
that are set for essentially all compilations I do. So even if the
benefit were small, in my environment it would be worth the investment
which is essentially zero.
> OTOH, I couldn't find any objectionable warning generated by -Wall when
> dealing with correct code. In all cases, shutting such warnings up
> results in code whose correctness is clearer than before. -Wall also
> helped me get rid of the "implicit int in function definitions" habit
> inherited from the K&R C days.
Certainly I agree that many or most -Wall warnings are worth
getting rid of, with the particular case of implicitly 'int'
function being one of them.
> So, what's wrong with -Wall, from your point of view?
Ok, the $64 question. Again for the sake of discussion let's take
the list below as the set of warnings covered by -Wall:
-Wchar-subscripts - using values of type 'char' to subscript
-Wcomment - when /* appears in a comment string (& more)
-Wformat - check printf et al format strings against args
-Wnonnull - NULL arg to "nonnull" attribute parameters
-Wimplicit - decl w/o type, or function call w/o decl
-Wmain - type of main() is suspicious
-Wmissing-braces - initializers not fully bracketed
-Wparentheses - eg, if(x=a+b). also, some unpaired if's
-Wsequence-point - possible violations of sequence point rules
-Wreturn-type - non-void function 'return;'; no function type
-Wswitch - switch( ENUM ) that doesn't cover all cases
-Wswitch-default - switch() w/o 'default:' case
-Wswitch-enum - switch( ENUM ) cases != ENUM values
-Wtrigraphs - if any trigraphs are encountered
-Wunused - unused variable, value, label, static function
-Wunused-parameter - unused parameter
-Wuninitialized - variable might be used without having been set
-Wunknown-pragmas - unrecognized #pragma
-Wstrict-aliasing - code that may break with -fstrict-aliasing
I realize this set probably doesn't exactly match the gcc that anyone
is currently using, but I think it will help the discussion if we
proceed under the assumption that these conditions make up the current
set of -Wall conditions.
I would break these down into four categories - the always useful, the
usually useful, the probably useful, and the objectionable.
The 'always useful' are those that simply never get turned off (that
is, in my environment). These conditions are ones I consider
synonymous with errors. (Normally I expect -Werror will be set [1]
whether the compilation is a "development" compilations or a
"production" compilation.) The always useful ones are:
-Wchar-subscripts - using values of type 'char' to subscript
-Wformat - check printf et al format strings against args
-Wmain - type of main() is suspicious
-Wreturn-type - non-void function 'return;'; no function type
The 'usually useful' are those that are on by default in production
code but could be turned off during development or sometimes on a
case-by-case basis for production compiles in unusual circumstances.
An example is the setting for the -Wtrigraphs condition - usually code
should have no trigraphs but sometimes it needs to, and -Wtrigraphs
needs to be disabled in those cases. The usually useful ones are:
-Wimplicit - decl w/o type, or function call w/o decl
-Wcomment - when /* appears in a comment string (& more)
-Wmissing-braces - initializers not fully bracketed
-Wswitch - switch( ENUM ) that doesn't cover all cases
-Wtrigraphs - if any trigraphs are encountered
-Wunused - unused variable, value, label, static function
-Wunused-parameter - unused parameter
-Wuninitialized - variable might be used without having been set
The 'probably useful' are those that most likely in one of the
previous two categories but I don't have enough specific experience
with them to give an opinion as to which. Operationally they are
treated just as the 'usually useful' ones are. The probably useful
ones are:
-Wnonnull - NULL arg to "nonnull"-attributed parameters
-Wsequence-point - possible violations of sequence point rules
-Wswitch-enum - switch( ENUM ) cases != ENUM values
-Wunknown-pragmas - unrecognized #pragma
-Wstrict-aliasing - code that may break with -fstrict-aliasing
The 'objectionable' are those that might get used as diagnostic tools
from time to time but are turned off during all regular compiles. The
objectionable ones are:
-Wparentheses - eg, if(x=a+b). also, some unpaired if's
-Wswitch-default - switch() w/o 'default:' case
Discussion for -Wswitch-default:
-Wswitch-default
Warn whenever a "switch" statement does not have a
"default" case.
First of all, either -Wswitch or -Wswitch-enum seems like a more
accurate diagnostic tool when the switch() expression is of an
enumerated type [2].
Second, the decision to put in a 'default:' case is a programming
practice that is appropriate in some circumstances but not in others.
Apparently there is no way to locally override the warning for
switch() statements where the 'default:' is judged better left out.
Third, automatically adding a 'default:' to every switch() can reduce
the value of -Wswitch/-Wswitch-enum. It can be useful to leave off
the 'default:' so that when additional values are added to an
enumerated type then switch() statements on values of that type will
be flagged. Including -Wswitch-default will thwart this programming
practice.
Discussion for -Wparentheses:
-Wparentheses
Warn if parentheses are omitted in certain contexts,
such as when there is an assignment in a context where
a truth value is expected, or when operators are
nested whose precedence people often get confused
about.
Also warn about constructions where there may be con-
fusion to which "if" statement an "else" branch
belongs. [Example/explanation omitted.]
First, this warning covers several rather different conditions lumped
together under a single name. The different conditions should have
been broken out under separately invokable switches.
Second, the several conditions are essentially different because they
protect against different kinds of error causes. The reason for
writing 'if((x=a+b))' rather than 'if(x=a+b)' is to show that it
wasn't a mistype for 'if(x==a+b)'. The case with extra parentheses
inside expressions is different - here the concern is not that
mistyping might have occurred but that a non-expert reader might be
confused about precedence. (Curiously, the 'if((x=a+b))' form is
"more clear" only *after* reaching a certain level of experience.)
The case with braces for unpaired if's is different in still another
way - here the concern is any reader might be mislead (possibly after
future editing) by other formatting cues to mis-perceive the actual
structure. ([3]) They do have something in common: to some
extent they all reflect "style choices".
Third, as it happens the style choice about bracing to help with
unpaired if's is one I agree with. I expect I would set this warning
condition (as a 'usually useful' one) if it were separately available.
Fourth, the style choice to add redundant parentheses in certain
circumstances is one I disagree with. To say that another way, I
might choose to add redundant parentheses under some circumstances,
but the developer should make the choice about when, not the compiler.
Fifth, encouraging putting in redundant parentheses sends the wrong
message. In effect, we're telling developers that they needn't bother
learning the different levels of precedence because no one else knows
them either. This (probably unintended) side-effect of -Wparentheses
is especially harmful in novices, affected at a time when they are
most impressionable.
Sixth, adding redundant parentheses to correct "mistakes" flagged
under -Wparentheses develops bad programming habits. Clearly the code
'if((x=a+b))' does exactly the same thing as 'if(x=a+b)', but we have
to put in the extra parentheses "to shut up the compiler". So the
presence of -Wparentheses messages dilutes the effect of other warning
conditions, which are more likely to require code changes that
are substantive rather than just cosmetic.
Finally, a comment about -Wall generally and not about any specific
flag -W<xxx> on the list.
The set of -W<xxx> in -Wall tends to grow over time. This is
important because code that compiles today (or, on platform X) might
not compile tomorrow (or on platform Y). Because I work on source
that is distributed in bundles that include warning flag settings for
compilation directives (usually in makefiles), I've learned to avoid
using -Wall since its meaning in the target environment might very
well be different than the meaning of -Wall in the originating
environment. To a certain extent the same thing is true of individual
warning condition sets, but these happen on a smaller scale. The
set of conditions flagged by -Wall can increase drastically.
What's the word for this again? Oh yes, it's "innovation". :)
So there's my $64 answer. (Braces himself for the rebuttals
sure to follow...)
Notes:
[1] To be more precise, I normally try to act as though -Werror
were set whether it actually is or not.
[2] Whoever is writing gcc documentation should consult a dictionary
more often; there is no such word as "enumeral". (Authority:
several unabridged dictionaries.)
[3] Personally I think any developer who doesn't use braces in every
case where controlled statements are not on the same line as their
control keyword is at least mildly insane. But I digress.
- Next message: Howard Hinnant: "Re: Why does ANSI not define a function to determine the size of (m)allocated mem? (like _msize)"
- Previous message: Jack Klein: "Re: initializing data at compile time"
- In reply to: Dan Pop: "Re: Learning C with Older books ?."
- Next in thread: Richard Bos: "Re: Learning C with Older books ?."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]