473,394 Members | 1,866 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

Ex 7-5

mdh
Hi All,
I have been struggling with this for a few hours. The exercise is a
variation on the RPN calculator, asking to use scanf or sscanf for
input.

I have included the ( partially completed) code below, but this is the
puzzle. ( It's from my old standby Tondo and Gimpel)

Firstly, it compiles with out any errors!!!

If I set a breakpoint in main, and step through the code, it returns
the expected output.
If I set a breakpoint in 'getop' at the first 'while' statement, it
seems to skip the line "sscanf(lastc, "%c", &c);"
If no breakpoint is set, I get the same output as the line above, but
cannot be sure, where the error is.
ps...if the formatting below looks wrong, please tell me / please let
me know how to present it for the clc.

/********/

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXOP 10
#define NUMBER '0'

double pop(void);
void push (double);
int getop(char *);

int main (int argc, const char * argv[])
{
char line[MAXOP];
int type;
while ( (type = getop(line)) != EOF)
switch (type)
{
case NUMBER:
push (atof(line));
break;
case '+':
push (pop() + pop());
break;
case '\n':
printf("\t%.5g", pop());
break;
default:
printf("Error: unknown command \'%s\'\n", line);
break;
}

return 0;
}
int getop(char line[])
{
int c, rc, i;
static char lastc[] = " ";

sscanf(lastc, "%c", &c); /** I ***think** this line is the problem?
***/
lastc[0] = ' ';

while ( (line[0]=c) == ' ' || c == '\t')
if ( scanf("%c", &c) == EOF)
c=EOF;
line[1]= '\0';
if ( !isdigit(c) && c != '.')
return c;
i = 0;
if ( isdigit(c))

do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i] = c))
break;
}while (rc != EOF);

if ( c == '.')

do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i]= c))
break;
}while (rc != EOF);
line[i]= '\0';
if ( rc != EOF)
lastc[0] = c;

return NUMBER;

}

#define MAXSTACK 20

double stack[MAXSTACK];
double *dptr = stack;
double *eptr = stack + MAXSTACK;
double pop(void){
if ( dptr >= stack)
return *(--dptr) ;
else{
printf("Error: Stack Empty");
return 0.00;
}

}

void push (double d){

if ( dptr < eptr)
*dptr++ = d;
else
printf("Error: Stack Full");

}
Sep 13 '08 #1
57 2325
mdh <md**@comcast.netwrites:
I have been struggling with this for a few hours. The exercise is a
variation on the RPN calculator, asking to use scanf or sscanf for
input.

I have included the ( partially completed) code below, but this is the
puzzle. ( It's from my old standby Tondo and Gimpel)

Firstly, it compiles with out any errors!!!
[...]
>
ps...if the formatting below looks wrong, please tell me / please let
me know how to present it for the clc.
First: Don't use tabs when posting to Usenet.

I can't even tell what tabstop setting you're using. Some of your
indentation is consistent with 4-column tabs, but not all of it.

I find that "indent -nut -kr -nce" gives reasonable results. "-nut"
say not to use tabs, "-kr" says to use K&R format, and "-nce" says not
to "cuddle" else; the latter generates this:

if (cond) {
stmt;
}
else {
stmt;
}

rather than this:

if (cond) {
stmt;
} else {
stmt;
}

I also strongly prefer to use braces for compound statements, even
when there's only a single controlled statement.

These are matters of taste, and reasonable people will violently
disagree with me on any or all of these points (though anyone who
disagrees about tabs is just wrong).

[...]
int c, rc, i;
static char lastc[] = " ";

sscanf(lastc, "%c", &c); /** I ***think** this line is the problem?
***/
[...]

It's certainly *a* problem. sscanf's "%c" option requires a char*;
you're giving it an int*. Hilarity ensues.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 13 '08 #2
mdh <md**@comcast.netwrites:
ps...if the formatting below looks wrong, please tell me / please let
me know how to present it for the clc.
Already commented on so I'll move on...
/********/

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXOP 10
#define NUMBER '0'

double pop(void);
void push (double);
int getop(char *);

int main (int argc, const char * argv[])
{
char line[MAXOP];
int type;
while ( (type = getop(line)) != EOF)
switch (type)
{
case NUMBER:
push (atof(line));
break;
case '+':
push (pop() + pop());
break;
case '\n':
printf("\t%.5g", pop());
break;
default:
printf("Error: unknown command \'%s\'\n", line);
break;
}

return 0;
}
int getop(char line[])
I'd pass in the maximum size of line rather than reply on the #define.
{
int c, rc, i;
static char lastc[] = " ";

sscanf(lastc, "%c", &c); /** I ***think** this line is the problem?
***/
lastc[0] = ' ';

while ( (line[0]=c) == ' ' || c == '\t')
if ( scanf("%c", &c) == EOF)
c=EOF;
OK, the problem of using %c with an int * has been covered, but I would
suggest you getchar() (or fgetc) instead anyway. It makes everything
much simpler. Of course you have to avoid storing the "last" string,
but see below for more on that.

The whole thing can be:

while ((c = getchar()) == ' ' || c == '\t') /* do nothing */;
line[0] = c;

Although note that setting line[0] to (char)EOF is odd...
line[1]= '\0';
if ( !isdigit(c) && c != '.')
return c;
i = 0;
if ( isdigit(c))

do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i] = c))
scanf is wrong again here. If it fails you will add another digit
(the old c) to line. I don't know of that matters, but it looks
wrong. Also you must check that there is room for the digit.
break;
}while (rc != EOF);

if ( c == '.')

do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i]= c))
break;
}while (rc != EOF);
line[i]= '\0';
I'd try to avoid these two loops. I am sure you can come up with a
way to have only one!
if ( rc != EOF)
lastc[0] = c;
I think is is better to put the char back on the stream rather doing
this fiddling with "last". For one thing, some other part of the
program may one day need to get input and having that char that ended
the last input stored here prevents that from ever being done.
>
return NUMBER;

}

#define MAXSTACK 20

double stack[MAXSTACK];
double *dptr = stack;
double *eptr = stack + MAXSTACK;
double pop(void){
if ( dptr >= stack)
return *(--dptr) ;
Oops. If dptr == stack you can't access *--dptr.
else{
printf("Error: Stack Empty");
return 0.00;
}

}

void push (double d){

if ( dptr < eptr)
*dptr++ = d;
else
printf("Error: Stack Full");

}
--
Ben.
Sep 13 '08 #3
On Fri, 12 Sep 2008 18:29:28 -0700, Keith Thompson posted:
> int c, rc, i;
static char lastc[] = " ";

sscanf(lastc, "%c", &c); /** I ***think** this line is the problem?
***/
[...]

It's certainly *a* problem. sscanf's "%c" option requires a char*;
you're giving it an int*. Hilarity ensues.
He did get it right as far as copying from Tondo & Gimpel is concerned.
The format specifier would need to be %d. Beyond that, it might be a
suitable solution for the clc wiki for K&R solns, where none is posted for
7-5.
--
Communism, like any other revealed religion, is largely made up of
prophecies.
H. L. Mencken
Sep 13 '08 #4
mdh
On Sep 12, 6:29*pm, Keith Thompson <ks...@mib.orgwrote:
mdh <m...@comcast.netwrites:
[...]* *int c, rc, i;
* *static char lastc[] = " ";
* *sscanf(lastc, "%c", &c); */** *I ***think** this line is theproblem?
***/

[...]

It's certainly *a* problem. *sscanf's "%c" option requires a char*;
you're giving it an int*. *Hilarity ensues.

--

As Ron Ford noted (below), T&G ( which I missed) declare c as an int
( presumably to hold EOF) but use it as a "char" in sscanf. Was this
correct at some point in C? In other words did something change after
their book was published (1989 according to my version). If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? :-) thanks.
Sep 13 '08 #5
mdh
On Sep 12, 7:22*pm, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
mdh <m...@comcast.netwrites:
ps...if the formatting below looks wrong, please tell me / please let
me know how to present it for the clc.

Already commented on so I'll move on...
I need to figure out how to make those tabs go away when I post here.
Will work on that.
>

Oops. *If dptr == stack you can't access *--dptr.
OOps..I missed that too...but I could not get past the other problem
first.
I think their solution was also mainly to show the use of scanf/sscanf
as opposed to writing the best code..so your thoughts on a better
solution are appreciated.

Thank you Ben.
Sep 13 '08 #6
mdh wrote:
On Sep 12, 6:29 pm, Keith Thompson <ks...@mib.orgwrote:
>mdh <m...@comcast.netwrites:
[...] int c, rc, i;
>> static char lastc[] = " ";
sscanf(lastc, "%c", &c); /** I ***think** this line is the problem?
***/
[...]

It's certainly *a* problem. sscanf's "%c" option requires a char*;
you're giving it an int*. Hilarity ensues.


As Ron Ford noted (below), T&G ( which I missed) declare c as an int
( presumably to hold EOF) but use it as a "char" in sscanf. Was this
correct at some point in C? In other words did something change after
their book was published (1989 according to my version).
I doubt it.
If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? :-) thanks.
Assuming sizeof int sizeof char, the value read will but placed in the
first byte of c. The first byte may be the most significant byte, so
you will end up with a very big number.

--
Ian Collins.
Sep 13 '08 #7
On Fri, 12 Sep 2008 21:29:36 -0700 (PDT), mdh <md**@comcast.net>
wrote:
>On Sep 12, 6:29*pm, Keith Thompson <ks...@mib.orgwrote:
>mdh <m...@comcast.netwrites:
[...]* *int c, rc, i;
* *static char lastc[] = " ";
* *sscanf(lastc, "%c", &c); */** *I ***think** this line is the problem?
***/

[...]

It's certainly *a* problem. *sscanf's "%c" option requires a char*;
you're giving it an int*. *Hilarity ensues.

--


As Ron Ford noted (below), T&G ( which I missed) declare c as an int
( presumably to hold EOF) but use it as a "char" in sscanf. Was this
correct at some point in C? In other words did something change after
their book was published (1989 according to my version). If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? :-) thanks.
You seem to be mixing the return value from sscanf with the object in
which it stores the converted input. sscanf will return EOF if the
end of file indicator is set for the stream. It will never store EOF
as the result of a %c conversion specification.

This is different than fgetc whose return value is an int so that both
characters and EOF can be returned as appropriate.

Passing an int* where %c expects a char* invokes undefined behavior.
Consider the case where the two pointer types have different sizes or
representations or are passed by different mechanisms. Even if
everything is the same, if sizeof(int) >1 then sscanf will only update
part of the int. Which part depends on what endian your system is.

--
Remove del for email
Sep 13 '08 #8
mdh
On Sep 12, 10:13*pm, Barry Schwarz <schwa...@dqel.comwrote:
On Fri, 12 Sep 2008 21:29:36 -0700 (PDT), mdh <m...@comcast.net>
.
--
As Ron Ford noted (below), T&G ( which I missed) declare c as an int
( presumably to hold EOF) but use it as a "char" in sscanf. Was this
correct at some point in C? In other words did something change after
their book was published (1989 according to my version). If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? *:-) thanks.

You seem to be mixing the return value from sscanf with the object in
which it stores the converted input. *sscanf will return EOF if the
end of file indicator is set for the stream. *It will never store EOF
as the result of a %c conversion specification.

Yes...I missed that ...thank you.
>
Passing an int* where %c expects a char* invokes undefined behavior.
Hmmm...first time I have seen T&G wrong, that is why I thought that
perhaps when they wrote their solution ( in 1989), it was permissible
to do something like that...not, let me hasten to add, that I picked
that up before it was pointed out.
Thanks for the correction of return value versus conversion
specifications and storage....I had not clearly distinguished between
the two...another reason for exercises.

Sep 13 '08 #9
Ron Ford said:

<snip>
He did get it right as far as copying from Tondo & Gimpel is concerned.
The format specifier would need to be %d. Beyond that, it might be a
suitable solution for the clc wiki for K&R solns, where none is posted
for 7-5.
Then post one, dear chap.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Sep 13 '08 #10
On Fri, 12 Sep 2008 22:36:58 -0700 (PDT), mdh posted:
>>
Passing an int* where %c expects a char* invokes undefined behavior.

Hmmm...first time I have seen T&G wrong, that is why I thought that
perhaps when they wrote their solution ( in 1989), it was permissible
to do something like that...not, let me hasten to add, that I picked
that up before it was pointed out.
Thanks for the correction of return value versus conversion
specifications and storage....I had not clearly distinguished between
the two...another reason for exercises.
I've seen the T&G solns get outdated, but this one is wrong from the
git-go. I thought before that you would change the format specifier on the
scanf; now I think you need to change the type declaration of c to char.

Please repost when you get something that compiles and behaves.
--
A judge is a law student who marks his own examination papers.
H. L. Mencken
Sep 13 '08 #11
Ian Collins <ia******@hotmail.comwrites:
mdh wrote:
[...]
>If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? :-) thanks.

Assuming sizeof int sizeof char, the value read will but placed in the
first byte of c. The first byte may be the most significant byte, so
you will end up with a very big number.
And if it happens to be the least significant byte, and the int
happens to have been (un)initialized to 0, it could appear to work.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 13 '08 #12
mdh
On Sep 12, 11:22*pm, Ron Ford <r...@example.invalidwrote:
On Fri, 12 Sep 2008 22:36:58 -0700 (PDT), mdh posted:
Passing an int* where %c expects a char* invokes undefined behavior.
Hmmm...first time I have seen T&G wrong, that is why I thought that
perhaps when they wrote their solution ( in 1989), it was permissible
to do something like that...

I've seen the T&G solns get outdated, but this one is wrong from the
git-go. *
Please repost when you get something that compiles and behaves.
--

Ron...as a newbie, it does give me a kick to look at their solution
and see that it is incorrect. Will work on a correct solution.
Sep 13 '08 #13
mdh
On Sep 13, 12:59*am, Keith Thompson <ks...@mib.orgwrote:
Ian Collins <ian-n...@hotmail.comwrites:
mdh wrote:
[...]
If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? *:-) thanks.
And if it happens to be the least significant byte, and the int
happens to have been (un)initialized to 0, it could appear to work.
But it still invokes ub?...certainly the enemy of the clc! :-)
Sep 13 '08 #14
mdh <md**@comcast.netwrites:
On Sep 12, 10:13Â*pm, Barry Schwarz <schwa...@dqel.comwrote:
<snip>
>Passing an int* where %c expects a char* invokes undefined behavior.

Hmmm...first time I have seen T&G wrong, that is why I thought that
perhaps when they wrote their solution ( in 1989), it was permissible
to do something like that
I don't think so. There was experience of C on word addressed
machines long before 1989. On such systems it was common for the
conversion from, say, int * to char * to perform arithmetic. Treating
one as the other without a conversion (as must happen if you pass a
type of pointer that is not the expected one) accesses the wrong
address -- often dramatically the wrong address depend on the scheme
used.

It is also true that there was a lot of code around at that time that
assumed all pointers were the same and that C's pointer conversion
rules were there just to please the compiler, but I am sure that C's
designers knew about this issue and designed C with it in mind.

C developed, in part from B and BCPL. Both B and BCPL had a
word-based view of objects and could be implemented on character, bit
or word addressed machines with equal ease. One of C's goals was to
have better treatment for character data. It is not an accident that
pointer conversions in the original C require a cast -- Ritchie must
have know that on some architectures the conversion would require an
actual arithmetic operation.

I don't have T&G, but from some of the examples I have seen posted I
can't help thinking that it may not be the stellar example that I
had always assumed it to be. That aside, passing an int * to scanf
with a %c format is wrong, even in the oldest C.

I don't think the exercise is meant to be read as "use *only* scanf".
I think it means replace the messy loops that read the number with a
scanf call. Replacing getch with scanf("%c", ...) is daft, but if one
was forced to, the only sane way is with a function:

int scanc(void)
{
char c;
return scanf("%c", &c) == 1 ? c : EOF;
}

so you can get behaviour that is as convenient as getch.

--
Ben.
Sep 13 '08 #15
m
In article <87************@bsb.me.uk>,
Ben Bacarisse <be********@bsb.me.ukwrote:
mdh <md**@comcast.netwrites:
On Sep 12, 10:13Â*pm, Barry Schwarz <schwa...@dqel.comwrote:
<snip>
Passing an int* where %c expects a char* invokes undefined behavior.
Hmmm...first time I have seen T&G wrong, that is why I thought that
perhaps when they wrote their solution ( in 1989), it was permissible
to do something like that

I don't think so.
It is also true that there was a lot of code around at that time that
assumed all pointers were the same and that C's pointer conversion
rules were there just to please the compiler, but I am sure that C's
designers knew about this issue and designed C with it in mind.
I don't have T&G, but from some of the examples I have seen posted I
can't help thinking that it may not be the stellar example that I
had always assumed it to be. That aside, passing an int * to scanf
with a %c format is wrong, even in the oldest C.

I don't think the exercise is meant to be read as "use *only* scanf".
I think it means replace the messy loops that read the number with a
scanf call. Replacing getch with scanf("%c", ...) is daft, but if one
was forced to, the only sane way is with a function:

int scanc(void)
{
char c;
return scanf("%c", &c) == 1 ? c : EOF;
}

so you can get behaviour that is as convenient as getch.


Ben...thank you.
Sep 13 '08 #16
mdh wrote:
On Sep 13, 12:59 am, Keith Thompson <ks...@mib.orgwrote:
>Ian Collins <ian-n...@hotmail.comwrites:
>>mdh wrote:
[...]
>>>If this is
indeed true( that it was correct at some point) could someone
elaborate on what changed and how this now causes "hilarity" or, as
has been my forte lately, ub? :-) thanks.
And if it happens to be the least significant byte, and the int
happens to have been (un)initialized to 0, it could appear to work.

But it still invokes ub?...certainly the enemy of the clc! :-)
The fact that the behavior is undefined is what permits the compiler to
generate code which does the things described above. "undefined
behavior" is the answer to "why?"; the explanations you've just been
give the answer to "how?".
Sep 13 '08 #17
mdh
On Sep 12, 6:29*pm, Keith Thompson <ks...@mib.orgwrote:
mdh <m...@comcast.netwrites:

[...]* *int c, rc, i;
* *static char lastc[] = " ";
* *sscanf(lastc, "%c", &c); */** *I ***think** this line is theproblem?
***/

[...]

It's certainly *a* problem. *sscanf's "%c" option requires a char*;
you're giving it an int*. *Hilarity ensues.
May I just pursue this a little further....sadly, once again, having
given this some more thought!!! :-)

K&R often use c ( declared as an int) to assign a character. So, for
example, p69, getline takes 2 arguments. A char array, and an integer.
int c;
while ( .......(c=getchar() != EOF).....)

I understand why c is declared as an int here, but why is then
different from scanf where one uses an int to hold a character?

Thanks

Sep 13 '08 #18
mdh said:

<snip>
>
K&R often use c ( declared as an int) to assign a character. So, for
example, p69, getline takes 2 arguments. A char array, and an integer.
int c;
while ( .......(c=getchar() != EOF).....)
Not like that, they don't. Compare and contrast the above with the actual
call, assignment, and comparison:

while ( .......((c=getchar()) != EOF).....)

The difference is significant, and has to do with precedence.
I understand why c is declared as an int here,
Then it will be obvious to you why you pass scanf a char *, not an int *,
when you need scanf to read a char for you. If that is not obvious, then
you haven't really understood why c is an int.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Sep 13 '08 #19
On Sep 13, 3:04 pm, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
<snip>
int scanc(void)
{
char c;
return scanf("%c", &c) == 1 ? c : EOF;
}

so you can get behaviour that is as convenient as getch.
That is not equal to getchar. (however it *might* be equal to that
'getch' you are talking about...)
Assume char is signed.

fix:

int scanc(void) {
char c;
return scanf("%c", &c) == 1 ? (unsigned char)c : EOF;
}

I hope you understand why this is necessary. Else this would not work:

int c;
c = scanc();
if(isupper(c)) /* ... */
Sep 13 '08 #20
vi******@gmail.com writes:
On Sep 13, 3:04 pm, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
<snip>
> int scanc(void)
{
char c;
return scanf("%c", &c) == 1 ? c : EOF;
}

so you can get behaviour that is as convenient as getch.

That is not equal to getchar. (however it *might* be equal to that
'getch' you are talking about...)
It was in the context of the K&R exercise that uses a function (that
they write) called getch. I don't think my function is the same but
then that was not really the point. If you *have* to use scanf to
read a char, then wrap it in a function that gives you an error
return.
Assume char is signed.

fix:

int scanc(void) {
char c;
return scanf("%c", &c) == 1 ? (unsigned char)c : EOF;
}

I hope you understand why this is necessary.
I understand why it is better. I hope you understand that I was
writing about old code in K&R that happily uses char to store the
result of getchar() -- in their code for getch for example[1]. In K&R
C (old but on topic I think) "the definition of C guarantees that any
character in the machine's standard character set will never be
negative" (page 40). Of course, exactly what this standard character
set is left a little vague, but the effect is that in K&R C you can
use char to store characters. You still need int to be able to see
EOF, but if you are reading text files, char is OK for storage and
there is not need for casts on the is* macros or when returning stored
characters. It is understandable, but sad, that this simple fact is
not true any more.

I am sure that if I'd not been reading my old copy of K&R (to see what
was being discussed) I'd have written your more portable version :-)
Else this would not work:

int c;
c = scanc();
if(isupper(c)) /* ... */
That's fine in K&R C (with my version of scanc)! I don't want anyone
to get the idea I think you are wrong. You are right. It would have
been better had I written the more portable modern version. I am only
explaining why I went all sloppy.

[1] I hope that, if I had K&R2, I would have been seeing code that is
rather more portable. Does the code for getch() in chapter 4 still
(in the second edition, I mean) store the result of getchar() in a
char and return these (uncast) as ints? I don't have it (or access to
decent library) so I can't check.

--
Ben.
Sep 13 '08 #21
On Sat, 13 Sep 2008 16:47:41 +0100, Ben Bacarisse
<be********@bsb.me.ukwrote:

>
[1] I hope that, if I had K&R2, I would have been seeing code that is
rather more portable. Does the code for getch() in chapter 4 still
(in the second edition, I mean) store the result of getchar() in a
char and return these (uncast) as ints? I don't have it (or access to
decent library) so I can't check.
In my copy, K&R2, 3rd printing, it still does.
Richard Harter, cr*@tiac.net
http://home.tiac.net/~cri, http://www.varinoma.com
Save the Earth now!!
It's the only planet with chocolate.
Sep 13 '08 #22
cr*@tiac.net (Richard Harter) writes:
On Sat, 13 Sep 2008 16:47:41 +0100, Ben Bacarisse
<be********@bsb.me.ukwrote:
>>[1] I hope that, if I had K&R2, I would have been seeing code that is
rather more portable. Does the code for getch() in chapter 4 still
(in the second edition, I mean) store the result of getchar() in a
char and return these (uncast) as ints? I don't have it (or access to
decent library) so I can't check.

In my copy, K&R2, 3rd printing, it still does.
Thanks. That is, of course, less than maximally portable but it fits
with K&R's rather more relaxed style.

--
Ben.
Sep 13 '08 #23
mdh <md**@comcast.netwrites:
On Sep 12, 6:29*pm, Keith Thompson <ks...@mib.orgwrote:
>mdh <m...@comcast.netwrites:
>

[...]* *int c, rc, i;
* *static char lastc[] = " ";
* *sscanf(lastc, "%c", &c); */** *I ***think** this line is the problem?
***/

[...]

It's certainly *a* problem. *sscanf's "%c" option requires a char*;
you're giving it an int*. *Hilarity ensues.

May I just pursue this a little further....sadly, once again, having
given this some more thought!!! :-)

K&R often use c ( declared as an int) to assign a character. So, for
example, p69, getline takes 2 arguments. A char array, and an integer.
int c;
while ( .......(c=getchar() != EOF).....)
RH already pointed out the misplaced parentheses.
I understand why c is declared as an int here, but why is then
different from scanf where one uses an int to hold a character?
getchar() returns an int value, so of course you want to assign the
result to an int object. (You can *mostly* get away with assigning
the result to a char object, since the assignment implicitly converts
the value to the required type before storing it. I say "mostly"
because it causes problems if getchar() returns either EOF or a value
greater than CHAR_MAX; the latter is possible if plain char is
signed.)

scanf with "%c" requires a *pointer* to a char object; scanf will then
store a 1-byte value in that object. If you pass it a pointer to an
int object, you're lying to it. There's no implicit conversion to
save you; char and int values can be mixed, but char* and int* values
cannot.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 13 '08 #24
mdh
On Sep 13, 7:54*am, Richard Heathfield <r...@see.sig.invalidwrote:
mdh said:

<snip>
K&R often use c ( declared as an int) to assign a character. So, for
example, p69, getline takes 2 arguments. A char array, and an integer.
int c;
while ( .......(c=getchar() != EOF).....)

Not like that, they don't. Compare and contrast the above with the actual
call, assignment, and comparison:

* while ( .......((c=getchar()) != EOF).....)

Sorry...was in a hurry this am. Yes...I meant to write it as you do.

>
I understand why c is declared as an int here,

Then it will be obvious to you why you pass scanf a char *, not an int *,
when you need scanf to read a char for you.

Hopefully it is so as to avoid ub? :-)

If that is not obvious, then
you haven't really understood why c is an int.
Well...let me put my neck on the line, hoping that any sword swoops
will miss all the other scars!!!

In ( .......((c=getchar()) != EOF).....), c **might** not be of type
char, it might be an EOF which is guaranteed **not** to be the value
of any character, usually "-1" so c has to be big enough to hold this
value in addition to the full character set.
Now..having said that, it's the next line on p69 that I have simply
done because it has always worked, that I now ponder.


s[i] = c; where s[] is a char array, and c is now, by exclusion, a
variable of type integer that contains an object of type character.
Sep 13 '08 #25
mdh said:

<snip>
In ( .......((c=getchar()) != EOF).....), c **might** not be of type
char,
I think you mean 'the return value of getchar might not represent a
character'...
it might be an EOF which is guaranteed **not** to be the value
of any character, usually "-1" so c has to be big enough to hold this
value in addition to the full character set.
....in which case yes, that's right. The point here is that getchar combines
its normal "give me the result" mechanism with its exception reporting
mechanism, so there needs to be a way to tell them apart, whereas scanf
does its exception reporting via the return value but gives you the result
via pointers. So there is no need to play extra-wide games with scanf.
Now..having said that, it's the next line on p69 that I have simply
done because it has always worked, that I now ponder.
s[i++] = c; is fine, because you couldn't get here if c weren't
representable as a char.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Sep 13 '08 #26
mdh
On Sep 13, 11:45*am, Keith Thompson <ks...@mib.orgwrote:
mdh <m...@comcast.netwrites:
Keith...just saw your reply.
>
May I just pursue *this a little further....sadly, once again, having
given this some more thought!!! *:-)
getchar() returns an int value, so of course you want to assign the
result to an int object. *(You can *mostly* get away with assigning
the result to a char object, since the assignment implicitly converts
the value to the required type before storing it.

Aha...that's what I was missing.
*I say "mostly"
because it causes problems if getchar() returns either EOF or a value
greater than CHAR_MAX; the latter is possible if plain char is
signed.)

Which..presumably is the reason one tests for the result of the
returned value?

>
scanf with "%c" requires a *pointer* to a char object; scanf will then
store a 1-byte value in that object. *If you pass it a pointer to an
int object, you're lying to it. *There's no implicit conversion to
save you; char and int values can be mixed, but char* and int* values
cannot.

got it...thank you.
Sep 13 '08 #27
mdh
On Sep 13, 7:54*am, Richard Heathfield <r...@see.sig.invalidwrote:
mdh said:

<snip>
K&R often use c ( declared as an int) to assign a character. So, for
example, p69, getline takes 2 arguments. A char array, and an integer.
int c;
while ( .......(c=getchar() != EOF).....)

Not like that, they don't. Compare and contrast the above with the actual
call, assignment, and comparison:

* while ( .......((c=getchar()) != EOF).....)

The difference is significant, and has to do with precedence.
I understand why c is declared as an int here,

Then it will be obvious to you why you pass scanf a char *, not an int *,
when you need scanf to read a char for you. If that is not obvious, then
you haven't really understood why c is an int.


Richard...just read Keith's response to my further question..so thank
you.
Sep 13 '08 #28
mdh
On Sep 13, 3:03*pm, Richard Heathfield <r...@see.sig.invalidwrote:
mdh said:

<snip>
In ( .......((c=getchar()) != EOF).....), c **might** not be of type
char,

I think you mean 'the return value of getchar might not represent a
character'...

Ah...of course....C truly is a precise language...and the funny thing
is, the more precise the language is, the clearer the concept becomes
to me. Thank you as always.

Sep 13 '08 #29
On Sat, 13 Sep 2008 11:45:52 -0700, Keith Thompson posted:
scanf with "%c" requires a *pointer* to a char object; scanf will then
store a 1-byte value in that object. If you pass it a pointer to an
int object, you're lying to it. There's no implicit conversion to
save you; char and int values can be mixed, but char* and int* values
cannot.
I think that's the punchline, but I just realized where the gotcha may have
come from, which is the difference in the format specifiers between printf
and scanf. I tried

char s[] = "Constantinople";
printf (" %c \n", s);

and got:
c10.c:33: warning: format '%c' expects type 'int', but argument 2 has type
'char
*'

%c differs between printf and scanf.
--
An idealist is one who, on noticing that roses smell better than a cabbage,
concludes that it will also make better soup.
H. L. Mencken
Sep 13 '08 #30
mdh said:

<snip>
>
Ah...of course....C truly is a precise language...and the funny thing
is, the more precise the language is, the clearer the concept becomes
to me.
And that's why some of us are so "picky" or "pedantic" when discussing C.
This often irritates people, but the intent is in fact to make things as
clear as possible. Woolly explanations lead to woolly thinking.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Sep 13 '08 #31
Ron Ford said:

<snip>
%c differs between printf and scanf.
No, it doesn't. In each case, it represents a request to process a
character.

The principal difference between printf and scanf - as far as interfaces
go, anyway - is that scanf expects a pointer to the object whose value is
to be changed, whereas printf needs only the value that is to be printed.

In the case of scanf, this leads to mild conclusion because %c and %s both
expect char * (whereas in printf %c expects an int value that is
representable as an unsigned char, and %s expects a char *). The
difference between scanf's %c char * and %s char * is that the %c
specifier only needs a pointer to a single char, whereas the %s specifier
needs a pointer to the first byte of a block of memory sufficient to store
the string being read into that block.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Sep 13 '08 #32
On Sat, 13 Sep 2008 06:01:08 +0000, Richard Heathfield posted:
Ron Ford said:

<snip>
>He did get it right as far as copying from Tondo & Gimpel is concerned.
The format specifier would need to be %d. Beyond that, it might be a
suitable solution for the clc wiki for K&R solns, where none is posted
for 7-5.

Then post one, dear chap.
/* This is the same calculator that
Bob Wightman wrote for Ex 4-4 with a different Getop
I aced his unused flag variable */
#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
#include<math.h>

#define MAXOP 100
#define NUMBER 0
#define TRUE 1
#define FALSE 0

int Getop(char s[]);
void push(double val);
double pop(void);
void showTop(void);
void duplicate(void);
void swapItems(void);
void clearStack();

int main(void)
{
int type;
double op2;
char s[MAXOP];

while((type = Getop(s)) != EOF)
{
switch(type)
{
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop()- op2);
break;
case '/':
op2 = pop();
if(op2)
push(pop() / op2);
else
printf("\nError: division by zero!");
break;
case '%':
op2 = pop();
if(op2)
push(fmod(pop(), op2));
else
printf("\nError: division by zero!");
break;
case '?':
showTop();
break;
case '#':
duplicate();
break;
case '~':
swapItems();
break;
case '!':
clearStack();
case '\n':
printf("\n\t%.8g\n", pop());
break;
default:
printf("\nError: unknown command %s.\n", s);
break;
}
}
return EXIT_SUCCESS;
}

#define MAXVAL 100

int sp = 0; /* Next free stack position. */
double val[MAXVAL]; /* value stack. */

/* push: push f onto stack. */
void push(double f)
{
if(sp < MAXVAL)
val[sp++] = f;
else
printf("\nError: stack full can't push %g\n", f);
}

/*pop: pop and return top value from stack.*/
double pop(void)
{
if(sp 0)
return val[--sp];
else
{
printf("\nError: stack empty\n");
return 0.0;
}
}

void showTop(void)
{
if(sp 0)
printf("Top of stack contains: %8g\n", val[sp-1]);
else
printf("The stack is empty!\n");
}
void duplicate(void)
{
double temp = pop();

push(temp);
push(temp);
}

void swapItems(void)
{
double item1 = pop();
double item2 = pop();

push(item1);
push(item2);
}

/* pop only returns a value if sp is greater than zero. So setting the
stack pointer to zero will cause pop to return its error */

void clearStack(void)
{
sp = 0;
}

int getch(void);
void unGetch(int);

int Getop(char line[])
{
int rc, i;
// Tondo and Gimpel has the next declaration wrongly an int
char c;
static char lastc[] = " ";

sscanf(lastc, "%c", &c);
lastc[0] = ' ';

while ( (line[0]=c) == ' ' || c == '\t')
if ( scanf("%c", &c) == EOF) c=EOF;

line[1]= '\0';
if ( !isdigit(c) && c != '.') return c;

i = 0;
if ( isdigit(c)) do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i] = c)) break;

} while (rc != EOF);

if ( c == '.') do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i]= c)) break;
} while (rc != EOF);
line[i]= '\0';
if ( rc != EOF) lastc[0] = c;
return NUMBER;
}
#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

/* Getch: get a ( possibly pushed back) character. */
int getch(void)
{
return (bufp 0) ? buf[--bufp]: getchar();
}

/* unGetch: push character back on input. */
void unGetch(int c)
{
if(bufp >= BUFSIZE)
printf("\nUnGetch: too many characters\n");
else
buf[bufp++] = c;
}

// gcc -o calc -Wall -std=c99 kr7_5_3.c
--
Adultery is the application of democracy to love.
H. L. Mencken
Sep 14 '08 #33
mdh <md**@comcast.netwrites:
[...]
In ( .......((c=getchar()) != EOF).....), c **might** not be of type
char, it might be an EOF which is guaranteed **not** to be the value
of any character, usually "-1" so c has to be big enough to hold this
value in addition to the full character set.
Not quite. The value returned by getchar() is definitely of type int.
That value might or might not be within the range of type char (i.e.,
CHAR_MIN .. CHAR_MAX).
Now..having said that, it's the next line on p69 that I have simply
done because it has always worked, that I now ponder.

s[i] = c; where s[] is a char array, and c is now, by exclusion, a
variable of type integer that contains an object of type character.
There are no types "integer" or "character". c is a variable of
type int. It *is* (not contains) an object of type int. It contains
a value of type int, a value which is also within the range of type
unsigned char (given that it was returned by getchar() and is not
equal to EOF).

Which is another wrinkle, BTW. The int value returned by getchar()
is either the value EOF or a value within the range of unsigned
char (0 .. UCHAR_MAX). Assuming it's not EOF, assigning this
value to a variable of type char will *probably* work, but
isn't guaranteed. For example, assume an implementation where
CHAR_BIT==8, plain char is signed, and there are no padding bits.
Reading a byte with the value 0xff causes getchar() to return 255,
which is outside the range of type char -- and converting 255 to
char yields an implementation-defined value or (in C99) raises an
implementation-defined signal. But we generally feel free to copy
the result of getchar() into an array of char.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 14 '08 #34
Ron Ford <ro*@example.invalidwrites:
On Sat, 13 Sep 2008 11:45:52 -0700, Keith Thompson posted:
>scanf with "%c" requires a *pointer* to a char object; scanf will then
store a 1-byte value in that object. If you pass it a pointer to an
int object, you're lying to it. There's no implicit conversion to
save you; char and int values can be mixed, but char* and int* values
cannot.

I think that's the punchline, but I just realized where the gotcha may have
come from, which is the difference in the format specifiers between printf
and scanf. I tried

char s[] = "Constantinople";
printf (" %c \n", s);

and got:
c10.c:33: warning: format '%c' expects type 'int', but argument 2 has type
'char
*'

%c differs between printf and scanf.
Well, for corresponding formats, printf generally expects an argument
of type foo and scanf expects an argument of type foo*, because scanf
has to store the value it reads in an object, and the pointer argument
tells it where the object is.

But even considering that, printf and scanf aren't entirely
consistent, and with good reason in most cases. The difference is
argument promotion -- or rather, the difference is motivated by
argument promotion. printf with "%c" expects an argument of type int
-- and if you pass it an object of type char, the value will be
promoted to int. And a character constant like 'x' is *already* of
type int. On the other hand, scanf with "%c" requires an argument of
type char* (treated as a pointer to a single char object, not as a
pointer to the beginning of a string), because it needs an actual char
object, not an int object, in which to store the value.

char can be promoted to int because the conversion just yields the
same value in a different type. char* can't be quietly promoted to
int* because a char* value, to be valid, must point to an object of
type char; there's no int object for the resulting int* value to point
to.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 14 '08 #35
Ron Ford <ro*@example.invalidwrites:
/* This is the same calculator that
Bob Wightman wrote for Ex 4-4 with a different Getop
I aced his unused flag variable */
It looks like the Chapter 7 re-write exercise. Ex 7-5 (I think -- in
my K&R it is 7-2) says re-write the calculator to use scanf and/or
sscanf for the input and number conversion. Also, do you know if this
is Bob Wightman of Tondo and Gimpel's code? It looks much that the
posted by mdh with a correction but I am now unsure whose it is.

I have a few comments on the code:
void clearStack();
void clearStack(void); is better (like the others).

<snip>
case '!':
clearStack();
case '\n':
printf("\n\t%.8g\n", pop());
break;
I think there is a missing break after clearStack(); The pop() does
not make sense if the stack has been cleared so a fall-though can't
have been intended.

<snip>
switch(type)
{
case NUMBER:
push(atof(s));
break;
My big problem with the "solution" is that is does not do what is
intended! I don't interpret the suggestion to rewrite to use scanf
"for the number conversion" to include the use of scanf for the bit it
does badly (reading a single character) and not for the bit it does
well (reading and converting numbers). It seems to be a daft
non-solution.
int getch(void);
void unGetch(int);
These are not needed any more. Seems odd to leave them there.
int Getop(char line[])
I ask about the author because this version is a step backwards.
K&R's getop passes in the buffer length. Now you can get by with
"global" (#define) size but that is not such a good design. Worse,
though, is that this code does not even check that there is room. It
is, in effect, a giant gets.
{
int rc, i;
// Tondo and Gimpel has the next declaration wrongly an int
char c;
static char lastc[] = " ";

sscanf(lastc, "%c", &c);
Why on earth do this? Surely c = last[0]; is clearer, but I'd simple
have a single last_char variable. I seems way too complicated.
lastc[0] = ' ';

while ( (line[0]=c) == ' ' || c == '\t')
if ( scanf("%c", &c) == EOF) c=EOF;

line[1]= '\0';
if ( !isdigit(c) && c != '.') return c;

i = 0;
if ( isdigit(c)) do
Personally, I find hiding the "do" over here to very poor style.
{
rc = scanf("%c", &c);
if (!isdigit(line[++i] = c)) break;

} while (rc != EOF);

if ( c == '.') do
{
rc = scanf("%c", &c);
if (!isdigit(line[++i]= c)) break;
} while (rc != EOF);
line[i]= '\0';
if ( rc != EOF) lastc[0] = c;
return NUMBER;
}
I don't think this is what K&R had in mind.
#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

/* Getch: get a ( possibly pushed back) character. */
int getch(void)
{
return (bufp 0) ? buf[--bufp]: getchar();
}

/* unGetch: push character back on input. */
void unGetch(int c)
{
if(bufp >= BUFSIZE)
printf("\nUnGetch: too many characters\n");
else
buf[bufp++] = c;
}
No longer needed.

OK, rather the all this knocking, here is my solution. I interpret
the intent as to try to use scanf to simplify the calculator, not to
complicate it by using scanf to read characters. At the same time,
you have to think about the problems of scanf. The main one being
that '+' and '-' are the start of numbers as far as it is concerned so
you can't just try to read a number. Also, at this stage ungetc has
not been introduced so my preferred solution of reading a char
(skipping space) using " %c" does not work because we can't put the
character back if it is a digit.

Anyway, I would do this (skip down to just after main for the getop
function if that is all you want to see):

#include <stdio.h>

#define NUMBER '0'

int getop(double *val);
double push(double val);
double pop(void);
void clear(void);

int main(void)
{
int op;
double num;

while ((op = getop(&num)) != EOF) {
switch (op) {
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
num = pop();
push(pop() - num);
break;
case '/':
num = pop();
if (num)
push(pop() / num);
else fprintf(stderr, "Error: division by zero.");
break;
case '#':
push(push(pop()));
break;
case 'c':
clear();
break;
case '=':
printf("\t%g\n", push(pop()));
break;
default:
fprintf(stderr, "Error: unknown operation %c.\n", op);
break;
}
}
return 0;
}

int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0-9]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}
/* This is the simple stack almost straight out of the original */

#define MAXVAL 100

static int sp = 0; /* Next free stack position. */
static double val[MAXVAL];

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

double pop(void)
{
if (sp 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

void clear(void)
{
sp = 0;
}

--
Ben.
Sep 14 '08 #36
mdh
On Sep 14, 7:08*am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
Ron Ford <r...@example.invalidwrites:
/* *This is the same calculator that
Bob Wightman wrote for Ex 4-4 with a different Getop

>
It looks like the Chapter 7 re-write exercise. *Ex 7-5 (I think -- in
my K&R it is 7-2) says re-write the calculator to use scanf and/or
sscanf for the input and number conversion. *Also, do you know if this
is Bob Wightman of Tondo and Gimpel's code?

I do not know who Bob Whitman is, but the code I originally showed was
straight out of T&G, p176, 2nd ed.

>*It looks much that the
posted by mdh with a correction but I am now unsure whose it is.

I have a few comments on the code:
**I interpret
the intent as to try to use scanf to simplify the calculator, not to
complicate it by using scanf to read characters.
int getop(double *num)
{
* * *int rc;
* * *char s[2];
* * *if ((rc = scanf(" %1[^0-9]", s)) == 1) /* <--Q1
* * * * * return s[0];
* * *else if (rc != EOF && scanf("%lf", num) == 1) <---- Q2
* * * * * return NUMBER;
* * *else return EOF;

}

/* This is the simple stack almost straight out of the original */

Ben...very elegant!
I just have a couple of questions.

Q1. What is the purpose of the "1" after the "%" as in "%1[^0-9]
(and I am hoping the [^0-9] is the same as regex ie a single character
that is not 0 through 9?) ( I am trying to understand what we are
looking for :-) )

Q2. Will an "lf" directive match a negative double correctly?

Ps...have not heard the word "daft" since the Goon show!
Sep 14 '08 #37
mdh wrote:
>
.... snip ...
>
May I just pursue this a little further....sadly, once again,
having given this some more thought!!! :-)

K&R often use c ( declared as an int) to assign a character. So,
for example, p69, getline takes 2 arguments. A char array, and
an integer.
No, they use an int to receive the output of getc() (and similar
functions) because getc() returns an int. And getc returns an int
because such is needed to return EOF.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Sep 14 '08 #38
mdh <md**@comcast.netwrites:
On Sep 14, 7:08Â*am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
,snip>
>int getop(double *num)
{
Â* Â* Â*int rc;
Â* Â* Â*char s[2];
Â* Â* Â*if ((rc = scanf(" %1[^0-9]", s)) == 1) /* <--Q1
Â* Â* Â* Â* Â* return s[0];
Â* Â* Â*else if (rc != EOF && scanf("%lf", num) == 1) <---- Q2
Â* Â* Â* Â* Â* return NUMBER;
Â* Â* Â*else return EOF;

}
<snip>
I just have a couple of questions.

Q1. What is the purpose of the "1" after the "%" as in "%1[^0-9]
(and I am hoping the [^0-9] is the same as regex ie a single character
that is not 0 through 9?) ( I am trying to understand what we are
looking for :-) )
We need to (a) skip spaces -- hence the "<space>%[...]" and (b) read
any one character that is not a digit. The only format that can read
from a specified set (yes, ^ means any character not in the following
set) is %[] and that will read an unlimited number of characters
unless we say home many we want: 1.
Q2. Will an "lf" directive match a negative double correctly?
Yes, but in this case it will never see one. The whole point is read
a double only when we know there is one really there. This is exactly
what the "old" code does -- +12 and -13 are taken as an op (+ and -)
followed by a number not as a signed double. It is much harder to
allow scanf to read signed doubles *and* to interpret + and - as
operators.
Ps...have not heard the word "daft" since the Goon show!
My mother said "daft as brush" all the time. I'm rather fond of the
word.

--
Ben.
Sep 14 '08 #39
On Sun, 14 Sep 2008 15:08:16 +0100, Ben Bacarisse posted:
>/* This is the same calculator that
Bob Wightman wrote for Ex 4-4 with a different Getop
I aced his unused flag variable */

It looks like the Chapter 7 re-write exercise. Ex 7-5 (I think -- in
my K&R it is 7-2) says re-write the calculator to use scanf and/or
sscanf for the input and number conversion. Also, do you know if this
is Bob Wightman of Tondo and Gimpel's code? It looks much that the
posted by mdh with a correction but I am now unsure whose it is.
What I did, Ben, was import the soln posted for 4-4 for the clc wiki. The
fella's name who gets the attribution there is Bob Wightman. I cut out
Getop and pasted in the Tondo and Gimpel version of Getop that mdh is
trying to implement.

I didn't look closely at the Getop I cut out. My guess is that when I do,
I'll cut out more spare parts.

The thing I don't know how to do now is test. It behaves when I do:
10 5 * or
67 4 %

, but I don't know enough about scanf *and* testing an app that uses a
stack to know that this solution is robust. I was thinking that people
with greater experience could illuminate.
>
I have a few comments on the code:
>void clearStack();

void clearStack(void); is better (like the others).

<snip>
> case '!':
clearStack();
case '\n':
printf("\n\t%.8g\n", pop());
break;

I think there is a missing break after clearStack(); The pop() does
not make sense if the stack has been cleared so a fall-though can't
have been intended.

<snip>
> switch(type)
{
case NUMBER:
push(atof(s));
break;

My big problem with the "solution" is that is does not do what is
intended! I don't interpret the suggestion to rewrite to use scanf
"for the number conversion" to include the use of scanf for the bit it
does badly (reading a single character) and not for the bit it does
well (reading and converting numbers). It seems to be a daft
non-solution.
Can you elaborate? I see 4 cases:
1) the posted solution for 4-4 is daft.
2) T&G's Getop is daft.
3) The inclusion of T&G's Getop in 4-4 is daft.
4) This portion of your criticism is daft.
>
>int getch(void);
void unGetch(int);

These are not needed any more. Seems odd to leave them there.
These will probably end up as spare parts.
>
>int Getop(char line[])

I ask about the author because this version is a step backwards.
K&R's getop passes in the buffer length. Now you can get by with
"global" (#define) size but that is not such a good design. Worse,
though, is that this code does not even check that there is room. It
is, in effect, a giant gets.
int Getop(char line[]) is a gemisch of a few things. K&R and T&G use getop
with a small g, which, according to gcc, differs from large G Getop. mdh
used line[] where T&G used s[], but the substitution was consistent
throughout. I would prefer using s[], so as to stay closer to known
solutions.

The T&G soln for this only shows getop, and the first sentence of the
explanation is:

The function getop (page 78 K&R) is the only routine modified.

< rest snipped for now>
--
The capacity of human beings to bore one another seems to be vastly greater
than that of any other animal.
H. L. Mencken
Sep 14 '08 #40
mdh
On Sep 14, 8:52*am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
mdh <m...@comcast.netwrites:
What is the purpose of the "1" after the "%" as in "%1[^0-9]

We need to (a) skip spaces -- hence the "<space>%[...]" and (b) read
any one character that is not a digit.
Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore, it
skips over white space( blanks, tabs, newlines, etc) as it looks for
input values"

Now, I assume that the space you have will work, but could you perhaps
explain the seemingly contradictory statements.

Thanks.
Sep 15 '08 #41
mdh <md**@comcast.netwrites:
On Sep 14, 8:52Â*am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
<snip>
>We need to (a) skip spaces -- hence the "<space>%[...]" and (b) read
any one character that is not a digit.

Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore, it
skips over white space( blanks, tabs, newlines, etc) as it looks for
input values"

Now, I assume that the space you have will work, but could you perhaps
explain the seemingly contradictory statements.
K&R are maybe out of date. Is this K&R2? I don't know exactly when
whitespace-as-a-directive came into scanf, but I have known about it
for quite a long time.

In ISO scanf implementations, %c does not skip white space and any
sequence of white space in the format is taken to be an instruction to
skip white space in the input. This has been true for nearly 20 years
(it was true in C90).

--
Ben.
Sep 15 '08 #42
mdh wrote:
On Sep 14, 8:52 am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
>mdh <m...@comcast.netwrites:
>>What is the purpose of the "1" after the "%" as in "%1[^0-9]
We need to (a) skip spaces -- hence the "<space>%[...]" and (b) read
any one character that is not a digit.

Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore, it
skips over white space( blanks, tabs, newlines, etc) as it looks for
input values"
K&R was simplifying matters. What the standard actually says is that a
directive can consist of "one or more white-space characters" (7.9.16.2p3).

What it says about such directives is in p5: "A directive composed of
white-space character(s) is executed by reading input up to the first
non-white-space character (which remains unread), or until no more
characters can be read."

When you consider what p8 says about conversion specifications : "Input
white-space characters (as specified by the isspace function) are
skipped, unless the specification includes a [, c, or n specifier.", the
net result is that white space directives in a format strings usually
has precisely the same effect as if it were absent. However, this is not
the case for white space directives that precede a conversion
specification with a [, c, or n specifier, which is precisely what's
relevant here.

Though it does not come up in this example, there's another related why
in which K&R's statement about blanks and tabs is incorrect. A blank or
tab which occurs inside of a '[' specifier, as for example "%[ab \t]" is
not "a directive composed of white-space characters", it is only a
directive containing white-space. It is not ignored, but plays a key
role in determining how that directive is applied to the input.
Sep 15 '08 #43
mdh
On Sep 15, 7:29*am, James Kuyper <jameskuy...@verizon.netwrote:
mdh wrote:
On Sep 14, 8:52 am, Ben Bacarisse <ben.use...@bsb.me.ukwrote:
mdh <m...@comcast.netwrites:
.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore, it
skips over white space( blanks, tabs, newlines, etc) as it looks for
input values"

K&R was simplifying matters. What the standard actually says is that a
directive can consist of "one or more white-space characters" (7.9.16.2p3).
"A directive composed of
white-space character(s) is executed by reading input up to the first
non-white-space character (which remains unread), or until no more
characters can be read."

When you consider what p8 says about conversion specifications : "Input
white-space characters (as specified by the isspace function) are
skipped, unless the specification includes a [, c, or n specifier."..........
>this is not
the case for white space directives that precede a conversion
specification with a [, c, or n specifier, which is precisely what's
relevant here.
there's another related why
in which K&R's statement about blanks and tabs is incorrect. A blank or
tab which occurs inside of a '[' specifier, as for example "%[ab \t]" is
not "a directive composed of white-space characters", it is only a
directive containing white-space. It is not ignored, but plays a key
role in determining how that directive is applied to the input.


When you put all this together, is there some logic as to why those
who wrote the specifications chose to do it this way, or is this
simply something you remember? I **though** I had figured out some
reason earlier, but I know better than to give my theory!!!! :-)

Sep 15 '08 #44
mdh wrote:
....|
When you put all this together, is there some logic as to why those
who wrote the specifications chose to do it this way, or is this
simply something you remember? I **though** I had figured out some
reason earlier, but I know better than to give my theory!!!! :-)
I don't know the exact reason, but by reverse engineering I think that
the idea was that one or more white space characters in the input
would be used to delimit fields, but would otherwise be ignored; this
mimics the way that human beings normally read lines of text. I think
it was also intended that format strings be allowed to have extra
white space, as needed, to make them more readable.
The exceptions for the [, c, and n specifiers were made to allow space
characters to be read in, rather than ignored, on those less common
occasions where there was a need to do so.
Sep 15 '08 #45
mdh
On Sep 15, 11:42*am, jameskuy...@verizon.net wrote:
>
I don't know the exact reason, but by reverse engineering I think that
the idea was .......

Thank you James.
Sep 15 '08 #46
mdh wrote:
>
.... snip ...
>
Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore,
it skips over white space( blanks, tabs, newlines, etc) as it
looks for input values"

Now, I assume that the space you have will work, but could you
perhaps explain the seemingly contradictory statements.
The format string is NOT the input that scanf is scanning. Also a
%c specification in the format does not skip leading blanks.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Sep 15 '08 #47
CBFalconer wrote:
mdh wrote:
... snip ...

Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore,
it skips over white space( blanks, tabs, newlines, etc) as it
looks for input values"

Now, I assume that the space you have will work, but could you
perhaps explain the seemingly contradictory statements.

The format string is NOT the input that scanf is scanning. ...
The cited comment from K&R was about blanks and tabs in the format
string. mdh's question was about a format string (which you snipped)
which did contain a blank. Neither comment was about "scanning". The
format string is indeed input to scanf(), even if it is not what
scanf() is "scanning", and both comments were about what scanf() does
with the format string it receives as input.

So, what exactly was the point of your comment?
... Also a
%c specification in the format does not skip leading blanks.
The format string that you snipped did not have a %c specification. It
did have a %[ specification.
Sep 15 '08 #48
On Sun, 14 Sep 2008 15:08:16 +0100, Ben Bacarisse posted:
Ron Ford <ro*@example.invalidwrites:
[reordered and snipped]
#include <stdio.h>

#define NUMBER '0'

int getop(double *val);
double push(double val);
double pop(void);
void clear(void);

int main(void)
{
int op;
double num;

while ((op = getop(&num)) != EOF) {
switch (op) {
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
num = pop();
push(pop() - num);
break;
case '/':
num = pop();
if (num)
push(pop() / num);
else fprintf(stderr, "Error: division by zero.");
break;
case '#':
push(push(pop()));
break;
case 'c':
clear();
break;
case '=':
printf("\t%g\n", push(pop()));
break;
default:
fprintf(stderr, "Error: unknown operation %c.\n", op);
break;
}
}
return 0;
}

Alright, Ben, I'm back in the saddle and have some time to put toward a
solution here. What you have above is close to every solution I've seen
for this calculator with a couple exceptions. One is that you don't have a
case for '!', but that's no big deal, we can add it. The other is that
you have a differing type for the argument of getop. That's a significant
change.

This little program is getting to be 200 lines, so if we can figure out the
switch control part of it, then we can omit almost all of it until we think
we have a final solution, which would be a good thing. We could simply
leave the case for say '%', to be able to have something that any clc'er
can compile and check behavior for.

Now that I've had a few days to think about it, I would actually like to
end up with a calculator I can use. The feature that I don't think we've
coded for that has reference to the above is how better to manipulate the
stack. My HP something C had a rotate feature that, along with other
operations, allowed me to get whatever stack values I wanted in the first
and second positions. So it is that I would have a relevant value on the
stack, and usually the final calculation would be to rotate the stack, and
what was on the top would be on the bottom, and magically, the other
relevant value was in the second position, ready for the final divide that
would be the answer.

So, I think we need a rotate that does

4
3
2
1
=>
3
2
1
4

and a swap that does

3
2
1
4
=>
3
2
4
1

, and then we'd have something eminently useful.
>void clearStack();

void clearStack(void); is better (like the others).

<snip>
> case '!':
clearStack();
case '\n':
printf("\n\t%.8g\n", pop());
break;

I think there is a missing break after clearStack(); The pop() does
not make sense if the stack has been cleared so a fall-though can't
have been intended.
Every other case has a break statement, so I wouldn't see why this one
doesn't need one too.

> switch(type)
{
case NUMBER:
push(atof(s));
break;

My big problem with the "solution" is that is does not do what is
intended! I don't interpret the suggestion to rewrite to use scanf
"for the number conversion" to include the use of scanf for the bit it
does badly (reading a single character) and not for the bit it does
well (reading and converting numbers). It seems to be a daft
non-solution.
You claim there's a better sscanf soln waiting to happen here. I don't
disbelieve.

>
int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0-9]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}
If this does the trick, it's a clearly better soln than T&G's. What does s
represent here?

/* This is the simple stack almost straight out of the original */

#define MAXVAL 100

static int sp = 0; /* Next free stack position. */
static double val[MAXVAL];

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}
You add static as a storage class modifier. You change the function from
void to double. That would mean that the 101st value gets returned. I'm
not sure that pulling the plug on the whole stack is the best way to deal
with that.

>
double pop(void)
{
if (sp 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}
I thought mdh made an error by returning a decimal zero, but that's what
K&R has. Why don't you?
>
void clear(void)
{
sp = 0;
}
Can you speak to the static nature of sp and this method of clearing the
stack?

--
A cynic is a man who, when he smells flowers, looks around for a coffin.
H. L. Mencken
Sep 17 '08 #49
Ron Ford <ro*@example.invalidwrites:
On Sun, 14 Sep 2008 15:08:16 +0100, Ben Bacarisse posted:
>Ron Ford <ro*@example.invalidwrites:

[reordered and snipped]
>#include <stdio.h>

#define NUMBER '0'

int getop(double *val);
double push(double val);
double pop(void);
void clear(void);

int main(void)
{
int op;
double num;

while ((op = getop(&num)) != EOF) {
switch (op) {
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
num = pop();
push(pop() - num);
break;
case '/':
num = pop();
if (num)
push(pop() / num);
else fprintf(stderr, "Error: division by zero.");
break;
case '#':
push(push(pop()));
break;
case 'c':
clear();
break;
case '=':
printf("\t%g\n", push(pop()));
break;
default:
fprintf(stderr, "Error: unknown operation %c.\n", op);
break;
}
}
return 0;
}


Alright, Ben, I'm back in the saddle and have some time to put toward a
solution here. What you have above is close to every solution I've seen
for this calculator with a couple exceptions.
That is because I copied it from K&R as did, I expect, everyone else.
To do the exercises it suffices to change the bits they suggest you
change. Laziness is a virtue in programming.
One is that you don't have a
case for '!', but that's no big deal, we can add it. The other is that
you have a differing type for the argument of getop. That's a significant
change.
All part of the exercise's instructions to "re-write the calculator".
It did not say "re-write getop" so I felt free to make changes in
keeping with the instruction to "use scanf/sscanf to read and convert
the numbers".
Now that I've had a few days to think about it, I would actually like to
end up with a calculator I can use.
<snip rotate>
, and then we'd have something eminently useful.
Variables would be more useful in my opinion. Another K&R exercise.
>>void clearStack();

void clearStack(void); is better (like the others).

<snip>
>> case '!':
clearStack();
case '\n':
printf("\n\t%.8g\n", pop());
break;

I think there is a missing break after clearStack(); The pop() does
not make sense if the stack has been cleared so a fall-though can't
have been intended.

Every other case has a break statement, so I wouldn't see why this one
doesn't need one too.
Glad you agree. The pop() causes undefined behaviour without the break;
>int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0-9]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}

If this does the trick, it's a clearly better soln than T&G's. What does s
represent here?
It is to read a single character using " 1%[...]". The 1 means we
read only one character, but %[...] is like %s -- it needs an array
and stores a null at the end. Hence the 2 bytes.

But thanks for making me go read that standard again! I see that
%[0-9] and %[^0-9] are implementation defined so I will re-write this
as " 1%[^0123456789]". Shame.
>/* This is the simple stack almost straight out of the original */

#define MAXVAL 100

static int sp = 0; /* Next free stack position. */
static double val[MAXVAL];

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

You add static as a storage class modifier.
Yes. Is that a problem?
You change the function from
void to double.
I changed it *back*. My K&R has it return the stacked value and I
thought that was better than the change to void by whoever. K&R's
main uses this fact to print push(pop()).
That would mean that the 101st value gets returned.
I don't get this. Pushing 101 consecutive values causes an error
message to be output, the stack to be cleared, and zero to be
returned. The 101st value is not returned.
I'm
not sure that pulling the plug on the whole stack is the best way to deal
with that.
You mean you don't like the clear when the stack overflows? Fine. I
was following K&R so you could see what I had changed and not have to
worry too much about other changes.
>double pop(void)
{
if (sp 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

I thought mdh made an error by returning a decimal zero, but that's what
K&R has. Why don't you?
Sorry, what is a decimal zero? Do you think 0 is not a good return
for a function returning a double?
>void clear(void)
{
sp = 0;
}

Can you speak to the static nature of sp and this method of clearing the
stack?
I prefer sp and val (the stack) to be static because there can be on
only one of each (they are not passed as parameters to the stack
operations) and I'd rather prevent accidental access from other parts
of the program. The functions can, quite reasonably, be used by other
parts of the program -- even other compilation units so the functions
were left with external linkage.

As to the clearing method, how else could it be done?

--
Ben.
Sep 17 '08 #50

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: William C. White | last post by:
Does anyone know of a way to use PHP /w Authorize.net AIM without using cURL? Our website is hosted on a shared drive and the webhost company doesn't installed additional software (such as cURL)...
2
by: Albert Ahtenberg | last post by:
Hello, I don't know if it is only me but I was sure that header("Location:url") redirects the browser instantly to URL, or at least stops the execution of the code. But appearantely it continues...
3
by: James | last post by:
Hi, I have a form with 2 fields. 'A' 'B' The user completes one of the fields and the form is submitted. On the results page I want to run a query, but this will change subject to which...
0
by: Ollivier Robert | last post by:
Hello, I'm trying to link PHP with Oracle 9.2.0/OCI8 with gcc 3.2.3 on a Solaris9 system. The link succeeds but everytime I try to run php, I get a SEGV from inside the libcnltsh.so library. ...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.