Check whether an argument in command line is a number

K

Keith Thompson

luserXtrog said:
Eric Sosman said:
luserXtrog wrote: [...]
How did you find those sections without reading any of the surrounding
material? Everything is a statement.
    Nonsense.  `int i = 42;', for example, is not a statement.
`int main(void) { return 0; }' is not a statement.

I explicitly mentioned function definitions as another case entirely.
You have not been paying attention.

Sure he has. You said "Everything is a statement", which you later
said was a deliberate exaggeration, but that wasn't obvious at the
time. He simply provided two counterexamples. You were speaking
figuratively, and he took it literally. It's not a big deal.

Incidentally, given
int main(void) { return 0; }
the whole thing is not a statement, but both
return 0;
and
{ return 0; }
*are* statements. The latter may seem surprising, but the syntax for
a function-definition includes a compound-statement. (It *could*
have used "statement" rather than "compound-statement", making
int main(void) return 0; /* not legal C! */
legal, but it didn't, so it isn't.
Section 6.8.2, entitled "Compound statement", describes how a block
may be used wherever a statement is required.
    6.8.2p1 gives the syntax of a compound statement, and 6.8.p2 says
in its entirety "A compound statement is a block," and that's all there
is in 6.8.2.  Where is the description you, er, describe?
    "A compound statement is a block" does not imply that all blocks are
compound statements.  For example, `if (a) b=42; else c=24;' has three
blocks, none of them compound statements (6.8.4p3).

You're right.  I've always assumed that "block" and "compound
statement" are synonymous, but they're not.  But I dislike the way the
standard expresses this.
[big snip]

What's the point?  Certain declarations have block scope, but you
can only have a declaration within a compound statement anyway; this:

    if (x == 3)
        int y;

is illegal.  (That's not quite true; a for statement can have a
declaration.)  

Which is just some bullshit adopted from C++. not C90. not portable.
This is the oddditty. Not the other way 'round.

If you don't like it, that's fine, but there's no need to get upset
about it. Personally I think it's a very nice feature, though I do
tend to avoid using it in C because support for C99-specific features
isn't universal.
I don't ever understand that question.
It seems like you guys are operating with some conceptual model that
shoots straight to some virtual machine assembler listing, where the
blocks have nice rectangular borders around them. I think you need to
dwell in the abstract syntax tree a little more.

You seem to be mining the footnotes. I think you need to look at the
pictures more. In the grammar it's all about statements. Statement
this; statement that; conditional statement; iterative statement.

The word 'block' is your block.

My question was specifically about the word "block". I honestly have
no idea what you're trying to say here.

I just checked the C90 standard; C90 6.6.2 says:

A _compound statement_ (also called a _block_) allows a set of
statements to be grouped into one syntactic unit, which may have
its own set of declarations and initializations (as discussed in
6.1.2.4). The initializers of objects that have automatic storage
duration are evaluated and the values are stored in the objects in
the order their declarators appear in the translation unit.

So in C90, "block" and "compound statement" were synonymous; this was
a change from C90 to C99.

Checking the C99 Rationale (something I should have done sooner), I
see that it answers my question. You can't have an explicit
declaration in any context where the expansion of what constitutes a
block makes any difference, but you can have an implicit one. A
compound literal (a new C99 feature) creates an implicit object with
automatic storage duration associated with the enclosing block;
attempting to access that object after the block terminates invokes
undefined behavior. (A compound literal outside any function creates
an implicit object with static storage duration.) If, for example,
the substatements of an if statement were not treated as blocks, then
this:

{
if (condition)
/* single statement using a compound literal */
else
/* single statement using a compound literal */
/* code that refers to the object */
}

would have well-defined behavior, because the implicit objects
would have the scope of the enclosing compound statement, but this:

{
if (condition) {
/* single statement using a compound literal */
}
else {
/* single statement using a compound literal */
}
/* code that refers to the object */
}

would have undefined behavior, because the object would vanish as the
end of the *inner* compound statement. It's common (but not
universal) practice to use compound statements for all selection and
iteration statements; here we'd have a case where adding braces would
introduce undefined behavior.

The solution chosen by the committee was to treat the single
statements as blocks, which means you have undefined behavior either
with or without the added braces. At least it's consistent.

For more details including a more complete example and a marvelously
obscure quiet change, see section 6.8 of the C99 Rationale,
<http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf>.
 
L

luserXtrog

luserXtrog said:
[...]
     "A compound statement is a block" does not imply that all blocks are
compound statements.  For example, `if (a) b=42; else c=24;' has three
blocks, none of them compound statements (6.8.4p3).
But they're not blocks, either. They fail to be compound statements
because they are simple statements. Elementary statements.

     On what do you base your claim that "they're not blocks, either?"
Here is the text of the paragraph I cited, whose meaning seems to
have eluded you:

        A selection statement *is a block* [emphasis mine] whose scope
        is a strict subset of the scope of its enclosing block. Each
        associated substatement *is also a block* [emphasis mine] whose
        scope is a strict subset of the scope of the selection
        statement.

Which parts of "is a block" and "is also a block" are you having trouble
understanding?

Ok. I suppose they are blocks. But how does that aid one's
understanding of anything. The preceding paragraph tells you all you
need to know in terms of statements.

1.8.4p2:
A selection statement selects among a set of statements depending on
the value of a
controlling expression.
 
K

Keith Thompson

Harald van Dijk said:
After

for (int i = 0; i <= 10; i++) { ... }

the i should go out of scope, if only so that

for (int i = 0; i <= 10; i++) { ... }
for (int i = 0; i <= 10; i++) { ... }

does not give an error for a redefinition of i.

Right, but that could have been handled as a special case, without
making a simple statement a block just because it happens to be a
substatement of a selection or iteration statement.

For the real reason, see my other reply.
 
L

luserXtrog

luserXtrog said:
luserXtrog wrote: [...]
How did you find those sections without reading any of the surrounding
material? Everything is a statement.
Nonsense. `int i = 42;', for example, is not a statement.
`int main(void) { return 0; }' is not a statement.
I explicitly mentioned function definitions as another case entirely.
You have not been paying attention.

Sure he has. You said "Everything is a statement", which you later
said was a deliberate exaggeration, but that wasn't obvious at the
time. He simply provided two counterexamples. You were speaking
figuratively, and he took it literally. It's not a big deal.

In the attempt to illustrate that my position leads to this obvious
contradiction, it omits relevant aspects of my position.
straw man.
Incidentally, given
int main(void) { return 0; }
the whole thing is not a statement, but both
return 0;
and
{ return 0; }
*are* statements. The latter may seem surprising, but the syntax for
a function-definition includes a compound-statement. (It *could*
have used "statement" rather than "compound-statement", making
int main(void) return 0; /* not legal C! */
legal, but it didn't, so it isn't.

It would be !!OxC00L; though!{>


[big snip]
Which is just some bullshit adopted from C++. not C90. not portable.
This is the oddditty. Not the other way 'round.

If you don't like it, that's fine, but there's no need to get upset
about it. Personally I think it's a very nice feature, though I do
tend to avoid using it in C because support for C99-specific features
isn't universal.

Agreed. Too much poo poo. But I am severely nonplussed about it.

Everytime I've started to use a declaration in a for loop, I've
found that I need to figure out where the index got to just afterward
(due to some other information that terminates the loop early). So I
invariably end up wrapping the whole thing in braces, where you were
already allowed to declare a variable. Then there are just too many
brackets floating around, so I delete the inner ones, maybe squeeze
the loop on one line. Declarations in a for loop have always been
dead-ends for me. much ado etc.

My question was specifically about the word "block". I honestly have
no idea what you're trying to say here.

I'm trying to say that

do if (!isdigit(*p)) printf("yo mama cant C!"); while(*p++);

shouldn't be all that surprising.
I just checked the C90 standard; C90 6.6.2 says:

A _compound statement_ (also called a _block_) allows a set of
statements to be grouped into one syntactic unit, which may have
its own set of declarations and initializations (as discussed in
6.1.2.4). The initializers of objects that have automatic storage
duration are evaluated and the values are stored in the objects in
the order their declarators appear in the translation unit.

So in C90, "block" and "compound statement" were synonymous; this was
a change from C90 to C99.

Checking the C99 Rationale (something I should have done sooner), I
see that it answers my question. You can't have an explicit
declaration in any context where the expansion of what constitutes a
block makes any difference, but you can have an implicit one. A
compound literal (a new C99 feature) creates an implicit object with
automatic storage duration associated with the enclosing block;
attempting to access that object after the block terminates invokes
undefined behavior. (A compound literal outside any function creates
an implicit object with static storage duration.) If, for example,
the substatements of an if statement were not treated as blocks, then
this:

{
if (condition)
/* single statement using a compound literal */
else
/* single statement using a compound literal */
/* code that refers to the object */
}

would have well-defined behavior, because the implicit objects
would have the scope of the enclosing compound statement, but this:

{
if (condition) {
/* single statement using a compound literal */
}
else {
/* single statement using a compound literal */
}
/* code that refers to the object */
}

would have undefined behavior, because the object would vanish as the
end of the *inner* compound statement. It's common (but not
universal) practice to use compound statements for all selection and
iteration statements; here we'd have a case where adding braces would
introduce undefined behavior.

The solution chosen by the committee was to treat the single
statements as blocks, which means you have undefined behavior either
with or without the added braces. At least it's consistent.

For more details including a more complete example and a marvelously
obscure quiet change, see section 6.8 of the C99 Rationale,
<http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf>.

Yes. yes. yes.

But this is all an additional layer of implicit enclosing blocks in
the
semantic interpretation. None of this should be necessary to grok the
syntax, which refers ubiquitously to expressions.

And if it's only necessary to tidy up issues related to the adoption
of C++ features, then surely /this/ is the oddity?!
 
K

Keith Thompson

luserXtrog said:
I'm trying to say that

do if (!isdigit(*p)) printf("yo mama cant C!"); while(*p++);

shouldn't be all that surprising.

Oh, was that your point?

I disagree; I did find it surprising, and I'm not too surprised that
somebody else found it so surprising that he didn't recognize it as C.
The do-while statement is relatively rare in C; I think the last time
I used one myself was in an IOCCC entry. Its syntax is unusual, in
that there's a keyword at the end of it, unlike any other selection or
iteration statement. Even among the rare cases of do-while statements
that I've seen, I don't think I had ever seen one whose controlled
sub-statement was an if statement not surrounded by braces.

Certainly it's valid C; nobody is arguing otherwise. It's just
unusual.

[...]
Yes. yes. yes.

But this is all an additional layer of implicit enclosing blocks in
the semantic interpretation. None of this should be necessary to
grok the syntax, which refers ubiquitously to expressions.

Did you mean statements rather than expressions?

But that's not the point. My point was a question about the
definition of the word "block". It arose from a discussion of your
do-if-while statement, but it wasn't directly related to it. I never
said that the definition of "block" caused any problem in grokking the
syntax. (And, of course, I found the answer to my question in the
Rationale.)
And if it's only necessary to tidy up issues related to the adoption
of C++ features, then surely /this/ is the oddity?!

Not that it makes any difference what is and is not an oddity,
compound literals are not a C++ feature. (At least I don't see them
in the C++98 standard; perhaps they've been added more recently.)
 
K

Keith Thompson

Keith Thompson said:
The do-while statement is relatively rare in C; I think the last time
I used one myself was in an IOCCC entry.
[...]

Correction: I forgot about the "do { ... } while (0)" idiom for macro
definitions.
 
B

Ben Bacarisse

Keith Thompson said:
Incidentally, given
int main(void) { return 0; }
the whole thing is not a statement, but both
return 0;
and
{ return 0; }
*are* statements. The latter may seem surprising, but the syntax for
a function-definition includes a compound-statement. (It *could*
have used "statement" rather than "compound-statement", making
int main(void) return 0; /* not legal C! */
legal, but it didn't, so it isn't.

Except that syntax would have made

int main(void);

ambiguous. The ; could mark the end of an expression statement
(making the whole thing a definition) or it could simply be the end of
a declaration (as now).
 
J

James Dow Allen

Is it too much to ask you to confirm to usenet posting standards and
use capitalisation and set you line length correctly?

Evidently you're not one who killfiles Google Groups users.
Is it too much to ask you to have a clue about one of its major
deficiencies?? Or are you pretending not to know, as part
of an anti-Google elitism?

For all its deficiencies, Google is an excellent way to access
Usenet. About the *only* reason I'm posting from "Save the Dodoes
Foundation" more often these days is to avoid the sophomoric
prejudice against Google so prevalent in groups like this.

(Sorry if you see this message twice. My connection is very flakey
right now and I'm *not* posting via Google which gives rather
reliable success/failure feedback.)

Hope this helps.
James Dow Allen
 
M

Mark Wooding

James Dow Allen said:
Evidently you're not one who killfiles Google Groups users.
Is it too much to ask you to have a clue about one of its major
deficiencies??

I'd have thought that those aware of GG's deficiencies would be more
rather than less likely to have killfiled its users. Besides, does
Google Groups really make it that difficult to type uppercase letters?
For all its deficiencies, Google is an excellent way to access
Usenet. About the *only* reason I'm posting from "Save the Dodoes
Foundation" more often these days is to avoid the sophomoric
prejudice against Google so prevalent in groups like this.

Err... Finding ill-capitalized and wrap-damaged (or unwrapped) text
difficult to read doesn't seem like a prejudice to me. In fact, it
seems pretty normal.

It seems to me that Google Groups does indeed have a deficient posting
interface (and you seem to agree: `For all its deficiencies ...').
Since its users presumably see some advantage in using Google Groups, it
doesn't seem reasonable or fair that the same users both benefit
personally from those advantages and inflict the costs of its
deficiencies on other readers.

-- [mdw]
 
L

luserXtrog

     Perl?  Ada?  PL/X?  Whatever the language is, it's one I don't
speak fluently.

Apropos, here is the rule as expressed by Mr. Kernighan:

As a general rule in C, anywhere you can use a simple statement, you
can use any compound statement, which is just a number of simple or
compound ones enclosed in {}. There is no semicolon after the } of a
compound statement, but there is a semicolon after the last non-
compound statement inside the {}.

The ability to replace single statements by complex ones at will is
one feature that makes C much more pleasant to use than Fortran.
Logic (like the exchange in the previous example) which would require
several GOTO's and labels in Fortran can and should be done in C
without any, using compound statements.

http://www.lysator.liu.se/c/bwk-tutor.html

Thus the statement is the natural component of an iteration
statement. The ability to replace a single statement with a
block is a longstanding feature intended to lure users from
Fortran. C99's added "block" confusion notwithstanding, it
seems likely to be a useful skill to view the iteration and
selection statements in this manner.

Thus "do if (cond) statement while(cond);" should not be
surprising to anyone who learned C after 1974.
 
K

Keith Thompson

luserXtrog said:
luserXtrog wrote: [...]
#include <stdio.h>
int main(void) {
    char *s="12345a";
    do if (!isdigit(*s)) {
[...]

Thus "do if (cond) statement while(cond);" should not be
surprising to anyone who learned C after 1974.

I fail to see how that follows from the snipped explanation of blocks
and compound statements. *I* find it surprising, and I would never
write it that way; I'd write:

do {
if (cond) {
statement;
}
} while (cond);

I'm not saying it's wrong, I'm not even saying it's bad style. I'm
just saying that it's unusual enough to be surprising.
Ack. Or one could remember to #include <ctype.h> at the top.

You need the cast even if you have the #include. *s is of type char.
isdigit() takes an argument of type int. If plain char is signed, and
*s is negative, the implicit conversion to int will yield a negative
value, and the behavior of isdigit() will be undefined. You need to
convert to unsigned char to be sure of having a valid argument.
 
L

luserXtrog

luserXtrog said:
luserXtrog wrote: [...]
#include <stdio.h>
int main(void) {
    char *s="12345a";
    do if (!isdigit(*s)) {
[...]

Thus "do if (cond) statement while(cond);" should not be
surprising to anyone who learned C after 1974.

I fail to see how that follows from the snipped explanation of blocks
and compound statements.  *I* find it surprising, and I would never
write it that way; I'd write:

    do {
        if (cond) {
            statement;
        }
    } while (cond);

I'm not saying it's wrong, I'm not even saying it's bad style.  I'm
just saying that it's unusual enough to be surprising.

Alright already. It's unusual. I suppose that must have been
part of my initial motive. Incidentally, after looking at it
for so long, I think it really should be a regular 'while'
loop.
You need the cast even if you have the #include.  *s is of type char.
isdigit() takes an argument of type int.  If plain char is signed, and
*s is negative, the implicit conversion to int will yield a negative
value, and the behavior of isdigit() will be undefined.  You need to
convert to unsigned char to be sure of having a valid argument.

Ah, but in the example, the string was filled with 7bit-safe ASCII
characters. *s here has very little chance of being negative.
But in the generalization to use for 8bit data, one would certainly
need to consider this issue and rectify the conversion by explicit
means. It was a useful critique to this end.

I'm surprised anyone's still awake. I intended this to be a
breakfast cudchew. What timezone are you in? I'm somewhere
between Chicago and Mexico City.
 
J

James Kuyper

luserXtrog said:
I'm surprised anyone's still awake. I intended this to be a
breakfast cudchew. What timezone are you in? I'm somewhere
between Chicago and Mexico City.

This is an international newsgroup, you can't make any useful
assumptions about when it will be quiet. According to the latest weekly
stats report,

Top 10 time zones
========================================================================
1: -0700...................................................... : 305
2: UTC........................................................ : 193
3: -0400...................................................... : 123
4: +0100...................................................... : 93
5: -0600...................................................... : 73
6: +0200...................................................... : 63
7: -0500...................................................... : 52
8: +1200...................................................... : 7
9: +0500...................................................... : 6
10: +0300...................................................... : 5

Keith is, according to the headers in the message you responded to, in
zone -0700, the single most popular one, and he posted his message about
00:59:34 local time.
 
K

Keith Thompson

luserXtrog said:
luserXtrog wrote: [...]
    char *s="12345a";
    do if (!isdigit(*s)) {
[...]
You need the cast even if you have the #include.  *s is of type char.
isdigit() takes an argument of type int.  If plain char is signed, and
*s is negative, the implicit conversion to int will yield a negative
value, and the behavior of isdigit() will be undefined.  You need to
convert to unsigned char to be sure of having a valid argument.

Ah, but in the example, the string was filled with 7bit-safe ASCII
characters. *s here has very little chance of being negative.

[...]

How do you know the characters are ASCII?

I believe the code is safe, but not because of any assumptions about
ASCII. The standard guarantees that any members of the basic
character set have non-negative values. (In EBCDIC, 'a' == 129, which
doesn't fit in 7 bits -- but that means that, on an EBCDIC system,
plain char must be unsigned (assuming CHAR_BIT==8).) But that's an
awfully brittle assumption. It's much safer to get into the habit of
always casting the argument to isdigit().
 
L

luserXtrog

luserXtrog said:
luserXtrog wrote: [...]
    char *s="12345a";
    do if (!isdigit(*s)) {
[...]
You need the cast even if you have the #include.  *s is of type char..
isdigit() takes an argument of type int.  If plain char is signed, and
*s is negative, the implicit conversion to int will yield a negative
value, and the behavior of isdigit() will be undefined.  You need to
convert to unsigned char to be sure of having a valid argument.
Ah, but in the example, the string was filled with 7bit-safe ASCII
characters. *s here has very little chance of being negative.

[...]

How do you know the characters are ASCII?

I believe the code is safe, but not because of any assumptions about
ASCII.  The standard guarantees that any members of the basic
character set have non-negative values.  (In EBCDIC, 'a' == 129, which
doesn't fit in 7 bits -- but that means that, on an EBCDIC system,
plain char must be unsigned (assuming CHAR_BIT==8).)  But that's an
awfully brittle assumption.  It's much safer to get into the habit of
always casting the argument to isdigit().

Acknowledged with gratitude.
 
K

Keith Thompson

pete said:
Keith Thompson wrote: [...]
It's much safer to get into the habit of
always casting the argument to isdigit().

That depends on what you mean by "always".

Agreed. I should have said this applies when the argument is of type
char.
(isdigit(EOF)) is defined as being equal to zero.

True -- but I've never felt the need to call any of the is*() or to*()
functions with EOF as an argument.

[...]
 
F

Flash Gordon

Keith said:
pete said:
Keith Thompson wrote: [...]
It's much safer to get into the habit of
always casting the argument to isdigit().
That depends on what you mean by "always".

Agreed. I should have said this applies when the argument is of type
char.
(isdigit(EOF)) is defined as being equal to zero.

True -- but I've never felt the need to call any of the is*() or to*()
functions with EOF as an argument.

If you are processing input as it arrives from a stream read with getc
it can happen.
int c = tolower(getc(f));

if (isdigit(c)) ....

Handle EOF
 
K

Keith Thompson

Flash Gordon said:
Keith said:
pete said:
Keith Thompson wrote: [...]
It's much safer to get into the habit of
always casting the argument to isdigit().
That depends on what you mean by "always".

Agreed. I should have said this applies when the argument is of type
char.
(isdigit(EOF)) is defined as being equal to zero.

True -- but I've never felt the need to call any of the is*() or to*()
functions with EOF as an argument.

If you are processing input as it arrives from a stream read with getc
it can happen.
int c = tolower(getc(f));

if (isdigit(c)) ....

Handle EOF

Sure, it can, and I think that's exactly why those functions are
specified to handle EOF as an argument. But I'd say it's more common,
and better style, to check for end-of-file *before* checking for
character classes.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top