printig effort

  • Thread starter Bill Cunningham
  • Start date
B

Bill Cunningham

I have patched together a print routine for my oscillator program and
one problem after another. This will not complie. But it has fewer errors
than it did.

#include <stdio.h>
#include <stdlib.h>

void
prn (double input)
{
FILE *fp;
double *inpt;
inpt = &input;
fopen ("data", "wt");
if ((fwrite (inpt, sizeof (size_t), 1, fp)) != EOF);
if ((fclose (fp)) != EOF);
}

int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.
Whew.

Bill
 
B

Barry Schwarz

I have patched together a print routine for my oscillator program and
one problem after another. This will not complie. But it has fewer errors
than it did.

#include <stdio.h>
#include <stdlib.h>

void
prn (double input)
{
FILE *fp;
double *inpt;
inpt = &input;
fopen ("data", "wt");

You are opening the file in text mode.
if ((fwrite (inpt, sizeof (size_t), 1, fp)) != EOF);

But you are writing a binary value to it.

Furthermore, fwrite returns a size_t which is guaranteed to be
unsigned. EOF, on the other hand, is guaranteed to be negative.
Attempting to compare the two is not what you want to do.

And yet, since the semicolon represents an empty statement, it doesn't
matter whether the if evaluates to true or false. If it is true, you
do nothing. If it is false, you skip the empty statement and still do
nothing.
if ((fclose (fp)) != EOF);
Ditto.

}

int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);

prn takes a double. argv[1] is a pointer. Why are you passing
argv[1] when you went to all the trouble to convert the value it
points at to a double in input?
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.

No you didn't.


Remove del for email
 
B

Bill Cunningham

Barry Schwarz said:
You are opening the file in text mode.


But you are writing a binary value to it.

I've never really written to text mode before. I don't know what to do.
Furthermore, fwrite returns a size_t which is guaranteed to be
unsigned. EOF, on the other hand, is guaranteed to be negative.
Attempting to compare the two is not what you want to do.

I must want to do something like this.

size_t t;
if ((t=fwrite ( inpt,sizeof(size_t),1,fp))==NULL)
puts("error");

And yet, since the semicolon represents an empty statement, it doesn't
matter whether the if evaluates to true or false. If it is true, you
do nothing. If it is false, you skip the empty statement and still do
nothing.
if ((fclose (fp)) != EOF);
Ditto.

}

int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);

prn takes a double. argv[1] is a pointer. Why are you passing
argv[1] when you went to all the trouble to convert the value it
points at to a double in input?
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.

No you didn't.
I didn't? Do I not need the strtod function there then?

I'm so glad somebody out there knows what they're doing because I sure
don't. And I forget too. I've used error checking with fwrite before. But
its been along time.

Thanks much

Bill
 
B

Bill Cunningham

[snip]
prn takes a double. argv[1] is a pointer. Why are you passing
argv[1] when you went to all the trouble to convert the value it
points at to a double in input?
return 0;
}

Do I not want to use strtod to convert argv[1] to a double? What if
someone entered a string? Which is what is taken. Is an error check needed
here?

Bill
 
R

Richard

Bill Cunningham said:
I have patched together a print routine for my oscillator program and
one problem after another. This will not complie. But it has fewer errors
than it did.

#include <stdio.h>
#include <stdlib.h>

void
prn (double input)
{
FILE *fp;
double *inpt;
inpt = &input;
fopen ("data", "wt");
if ((fwrite (inpt, sizeof (size_t), 1, fp)) != EOF);

What the hell is the line above? Do you ever read your own code?
if ((fclose (fp)) != EOF);
Ditto.

}

int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.
Whew.

Bill
 
B

Bartc

Richard Heathfield said:
Barry Schwarz said:


No, he isn't. He is opening the file in "undefined behaviour" mode.
See 4.9.5.3 of C89 or 7.19.5.3(3) of C99.

I wonder how much trouble it would have been for these committees to have
simply allowed "t" as an option? (Especially as this was a microsoft
extension).

Then code would have been self-documenting for those not familiar enough
with '4.9.5.3 of C89 or 7.19.5.3(3) of C99' to know whether the default mode
is text or binary.

(Just a question about text mode -- which I never use so I'm not too
bothered -- does it translate only the expected newline characters of the
host system to \n or will it cope with mixed files?

So if a program was opening text files from Windows, Linux and Mac, say,
would it translate newline characters from all of those successfully into
\n? Ie. just \n and no superfluous \r characters.

If it does, how does it do it? If not (and some brief tests suggest so),
then what actual use is a text mode which doesn't work?)
 
J

Jens Thoms Toerring

Bill Cunningham said:
I have patched together a print routine for my oscillator program and
one problem after another. This will not complie. But it has fewer errors
than it did.
#include <stdio.h>
#include <stdlib.h>
void
prn (double input)
{
FILE *fp;
double *inpt;
inpt = &input;
fopen ("data", "wt");
if ((fwrite (inpt, sizeof (size_t), 1, fp)) != EOF);
if ((fclose (fp)) != EOF);
}
int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);
return 0;
}

Here's a program that does things (hopefully) correctly and does
all the possible checks I could think of.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>

void
prn( double input )
{
FILE *fp;

/* Open file named 'data' for writing in binary mode */

if ( ( fp = fopen( "data", "wb") ) == NULL )
{
fprintf( stderr, "Failed to open file\n" );
exit( EXIT_FAILURE );
}

/* Write one item with size of 'input' into the file */

if ( ( fwrite( &input, sizeof input, 1, fp ) ) != 1 )
{
fprintf( stderr, "Failed to write to file\n" );
exit( EXIT_FAILURE );
}

/* Close the file */

if ( fclose( fp ) != 0 )
{
fprintf( stderr, "Failed to close file\n" );
exit( EXIT_FAILURE );
}
}

int
main ( int argc,
char * argv[ ] )
{
double input;
char *endptr;

/* Check that we've got at least one argument */

if ( argc < 2 )
{
fprintf( stderr, "Missing argument\n" );
return EXIT_FAILURE;
}

/* Try to convert argument to a double */

input = strtod( argv[ 1 ], &endptr );

/* Check if a conversion could be performed */

if ( endptr == argv[ 1 ] )
{
fprintf( stderr, "Invalid argument\n" );
return EXIT_FAILURE;
}

/* Check that the input did consist of a floating point number only */

if ( *endptr != '\0' )
{
fprintf( stderr, "Trailing garbage in argument\n" );
return EXIT_FAILURE;
}

/* Check if correct result would underflow */

if ( input == 0.0 && errno == ERANGE )
{
fprintf( stderr, "Argument too small\n" );
return EXIT_FAILURE;
}

/* Check if correct result would overflow */

if ( ( input == HUGE_VAL || input == -HUGE_VAL ) && errno == ERANGE )
{
fprintf( stderr, "Argument too large\n" );
return EXIT_FAILURE;
}

/* Write result to a file */

prn( input );

return 0;
}
Regards, Jens
 
B

Ben Bacarisse

Bill Cunningham said:
int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);

prn takes a double. argv[1] is a pointer. Why are you passing
argv[1] when you went to all the trouble to convert the value it
points at to a double in input?
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.

No you didn't.
I didn't? Do I not need the strtod function there then?

Your misunderstanding is something of a moving target so maybe it is
best to stick with one thing. Let examine this prn call.

The prn function is declared as taking a single argument of type
double. You call it like this:

prn (argv[1]);

argv is of type char ** so argv[1] is a char *. Your compiler should
be telling you that you are trying to call prn with an incompatible
argument.

When you say "I changed it to a double" presumably you are talking
about the previous line:

input = strtod (argv[1], NULL);

This sets the variable called input to the floating point value
represented by the characters pointed to by argv[1]. If argv[1]
points to "3.14" then input will get the value 3.14. argv[1] is not
changed, and this line has no effect on the following line:

prn (argv[1]);

You probably intended to write

prn (input);

which called prn with argument of the right type and makes use of the
variable you defined and so carefully set.
 
B

Ben Bacarisse

Bartc said:
(Just a question about text mode -- which I never use so I'm not too
bothered

I suspect you do use it. Any program that just uses stdin and stdout
is using text mode. If you don't use it for text files you open
yourself, how do you ensure your programs are portable? It seems odd
to reject a simple and effective mechanism that is guaranteed to be
available.
-- does it translate only the expected newline characters of the
host system to \n or will it cope with mixed files?

So if a program was opening text files from Windows, Linux and Mac, say,
would it translate newline characters from all of those successfully into
\n? Ie. just \n and no superfluous \r characters.

It maps the system's line ending to, and from, '\n'. That is (pretty
much) all.
If it does, how does it do it? If not

No, it just does the local conversion, if required.
(and some brief tests suggest so),
then what actual use is a text mode which doesn't work?)

I'd have thought that was obvious. It solves only one simple problem
-- how to write programs that read and write text files so that they
will work on any system with a correct C implementation. It does not
try to solve the problem of reading foreign files (e.g. Mac Classic
text files that have been byte-copied to a Unix system).
 
K

Keith Thompson

Bartc said:
I wonder how much trouble it would have been for these committees to have
simply allowed "t" as an option? (Especially as this was a microsoft
extension).

It wouldn't have been much trouble at all. But it's even less trouble
to omit the 't' when you write a call to fopen().
Then code would have been self-documenting for those not familiar
enough with '4.9.5.3 of C89 or 7.19.5.3(3) of C99' to know whether
the default mode is text or binary.

I don't think a whole lot of effort should be expended in making C
legible to readers who don't know the language.
(Just a question about text mode -- which I never use so I'm not too
bothered -- does it translate only the expected newline characters of the
host system to \n or will it cope with mixed files?

So if a program was opening text files from Windows, Linux and Mac, say,
would it translate newline characters from all of those successfully into
\n? Ie. just \n and no superfluous \r characters.

If it does, how does it do it? If not (and some brief tests suggest so),
then what actual use is a text mode which doesn't work?)

Text mode typically deals correctly with text files in the format used
by the implementation. Translating foreign files from one text format
to another is beyond the scope of stdio. (You can certainly do it by
*using* stdio, but the conversions aren't built in.)

In fact, as far as the standard is concerned, *anything* might have to
be re-mapped in text mode; there's nothing special about the new-line
character.

C99 7.19.2p2:

A text stream is an ordered sequence of characters composed into
lines, each line consisting of zero or more characters
plus a terminating new-line character. Whether the
last line requires a terminating new-line character is
implementation-defined. Characters may have to be added,
altered, or deleted on input and output to conform to differing
conventions for representing text in the host environment. Thus,
there need not be a oneto- one correspondence between the
characters in a stream and those in the external representation.


Read the rest of 7.19.2 as well.

On most systems you're likely to encounter, mapping between '\n' and
whatever sequence the implementation uses to mark end-of-line is
probably the only non-trivial mapping that's done in binary mode. But
it's theoretically possible that attempting to read a text file in
binary mode won't give you anything sensible; it might not even be
possible to open it.
(Just a question about text mode -- which I never use so I'm not too
bothered -- does it translate only the expected newline characters
of the host system to \n or will it cope with mixed files?

stdio provides a sophisticated mechanism for dealing with whatever
text file format your system happens to provide, so you don't have to
know how text files are represented. Why would you not take advantage
of that?
 
K

Keith Thompson

Bill Cunningham said:
int
main (int argc, char *argv[])
{
double input;
input = strtod (argv[1], NULL);
prn (argv[1]);

[An attribution line was lost somewhere. The above was written by
Bill Cunningham.]
prn takes a double. argv[1] is a pointer. Why are you passing
argv[1] when you went to all the trouble to convert the value it
points at to a double in input?
return 0;
}

in main the argument to prn is incompatible. I changed it to a double.

No you didn't.
I didn't? Do I not need the strtod function there then?

I'm so glad somebody out there knows what they're doing because I sure
don't. And I forget too. I've used error checking with fwrite before. But
its been along time.

Here's something that might or might not help you understand what's
going on.

strtod(argv[1], NULL) "converts" argv[1] (a char* that points to (the
first character of) a string) to a value of type double.

(I put the word "converts" in quotation marks because this isn't a
type conversion of the kind you get with a cast; we're using the word
"converts" in a somewhat looser sense.)

But when we say that it's converted, that doesn't mean that the thing
being converted is itself changed in any way. Conversion doesn't
*change* the thing being converted. It creates a new value that's the
*result* of the conversion, and leaves the original thing alone.

Calling strtod(argv[1], NULL) doesn't change argv[1] in any way.
argv[1] is still a char* pointing to a string. What strtod() does is
create a new result, a value of type double, that didn't exist before.
You then copy that value into the object "input".

double input;
input = strtod(argv[1], NULL);
prn(argv[1]); /* WRONG */

Since prn() expects a double, passing argv[1] to it makes no sense.
It wouldn't make any more or less sense if you deleted the strtod()
call.

What you want to pass to prn() is the *result* of the conversion,
which you've conveniently stored in the variable "input":

double input;
input = strtod(argv[1], NULL);
prn(input); /* RIGHT */

(Or you could replace these 3 lines with:
prn(strtod(argv[1], NULL));
..)
 
R

Richard

Bill Cunningham said:
An attempt at error checking.

Bill

An attempt is the right word. Now, once again, what is the line above
and what do you think it is doing?
 
H

Harald van Dijk

An attempt at error checking.

You check if fwrite fails, and if it does, you do nothing about it and
pretend it succeeded. That is no different from not checking at all. Try
to understand the code you are writing.
 
B

Bill Cunningham

Here's a program that does things (hopefully) correctly and does
all the possible checks I could think of.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>

void
prn( double input )
{
FILE *fp;

/* Open file named 'data' for writing in binary mode */

if ( ( fp = fopen( "data", "wb") ) == NULL )
{
fprintf( stderr, "Failed to open file\n" );
exit( EXIT_FAILURE );
}

/* Write one item with size of 'input' into the file */

if ( ( fwrite( &input, sizeof input, 1, fp ) ) != 1 )
{
fprintf( stderr, "Failed to write to file\n" );
exit( EXIT_FAILURE );
}

/* Close the file */

if ( fclose( fp ) != 0 )
{
fprintf( stderr, "Failed to close file\n" );
exit( EXIT_FAILURE );
}
}

int
main ( int argc,
char * argv[ ] )
{
double input;
char *endptr;

/* Check that we've got at least one argument */

if ( argc < 2 )
{
fprintf( stderr, "Missing argument\n" );
return EXIT_FAILURE;
}

/* Try to convert argument to a double */

input = strtod( argv[ 1 ], &endptr );

/* Check if a conversion could be performed */

if ( endptr == argv[ 1 ] )
{
fprintf( stderr, "Invalid argument\n" );
return EXIT_FAILURE;
}

/* Check that the input did consist of a floating point number only */

if ( *endptr != '\0' )
{
fprintf( stderr, "Trailing garbage in argument\n" );
return EXIT_FAILURE;
}

/* Check if correct result would underflow */

if ( input == 0.0 && errno == ERANGE )
{
fprintf( stderr, "Argument too small\n" );
return EXIT_FAILURE;
}

/* Check if correct result would overflow */

if ( ( input == HUGE_VAL || input == -HUGE_VAL ) && errno == ERANGE )
{
fprintf( stderr, "Argument too large\n" );
return EXIT_FAILURE;
}

/* Write result to a file */

prn( input );

return 0;
}
Regards, Jens
I think alot of what my problem is too is not understanding pointers fully.
When and where to use them. That's where Jen I get lost in your code above
is the pointers. For example,

if ( *endptr != '\0' )
{
fprintf( stderr, "Trailing garbage in argument\n" );
return EXIT_FAILURE;
}

if(*endptr!='\0')

Most of the time when I use pointers I do not use * again in front of the
pointer. Is what you are doing called "dereferencing" ? When to use * and &
too can give me trouble. My understanding is that & assigns an address in
memory to a pointer. Like this,

int *ip;
ip=&word;

Bill
 
R

Richard

Bill Cunningham said:
Here's a program that does things (hopefully) correctly and does
all the possible checks I could think of.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>

void
prn( double input )
{
FILE *fp;

/* Open file named 'data' for writing in binary mode */

if ( ( fp = fopen( "data", "wb") ) == NULL )
{
fprintf( stderr, "Failed to open file\n" );
exit( EXIT_FAILURE );
}

/* Write one item with size of 'input' into the file */

if ( ( fwrite( &input, sizeof input, 1, fp ) ) != 1 )
{
fprintf( stderr, "Failed to write to file\n" );
exit( EXIT_FAILURE );
}

/* Close the file */

if ( fclose( fp ) != 0 )
{
fprintf( stderr, "Failed to close file\n" );
exit( EXIT_FAILURE );
}
}

int
main ( int argc,
char * argv[ ] )
{
double input;
char *endptr;

/* Check that we've got at least one argument */

if ( argc < 2 )
{
fprintf( stderr, "Missing argument\n" );
return EXIT_FAILURE;
}

/* Try to convert argument to a double */

input = strtod( argv[ 1 ], &endptr );

/* Check if a conversion could be performed */

if ( endptr == argv[ 1 ] )
{
fprintf( stderr, "Invalid argument\n" );
return EXIT_FAILURE;
}

/* Check that the input did consist of a floating point number only */

if ( *endptr != '\0' )
{
fprintf( stderr, "Trailing garbage in argument\n" );
return EXIT_FAILURE;
}

/* Check if correct result would underflow */

if ( input == 0.0 && errno == ERANGE )
{
fprintf( stderr, "Argument too small\n" );
return EXIT_FAILURE;
}

/* Check if correct result would overflow */

if ( ( input == HUGE_VAL || input == -HUGE_VAL ) && errno == ERANGE )
{
fprintf( stderr, "Argument too large\n" );
return EXIT_FAILURE;
}

/* Write result to a file */

prn( input );

return 0;
}
Regards, Jens
I think alot of what my problem is too is not understanding pointers
fully.

This would be in addition to not understanding braces either I guess?
When and where to use them. That's where Jen I get lost in your code above
is the pointers. For example,

if ( *endptr != '\0' )

if(*endptr!='\0')

Most of the time when I use pointers I do not use * again in front of
the

Do you know what * does in front of a pointer?
pointer. Is what you are doing called "dereferencing" ? When to use * and &
too can give me trouble. My understanding is that & assigns an address in
memory to a pointer. Like this,

int *ip;
ip=&word;

Use a debugger and play with it.

In the "real world" a pointer holds the address of an object. In other
words it "points to" that object. So yes! You are right. "&" gives the
address of an object so

int * p;
p=&myint;

means assign the address of the object myint to the point p.

Once more : use a debugger. Play with the values.
 
B

Bill Cunningham

[snip]
Use a debugger and play with it.

In the "real world" a pointer holds the address of an object. In other
words it "points to" that object. So yes! You are right. "&" gives the
address of an object so

int * p;
p=&myint;

means assign the address of the object myint to the point p.

Once more : use a debugger. Play with the values.

I have gdb installed but alas I don't know anything about it and this
wouldn't be the place to ask questions.

Bill
 
R

Richard

Bill Cunningham said:
[snip]
Use a debugger and play with it.

In the "real world" a pointer holds the address of an object. In other
words it "points to" that object. So yes! You are right. "&" gives the
address of an object so

int * p;
p=&myint;

means assign the address of the object myint to the point p.

Once more : use a debugger. Play with the values.

I have gdb installed but alas I don't know anything about it and this
wouldn't be the place to ask questions.

Bill

There's a wonderful thing called the internet. On it you will find
something called the world wide web and then Google. Try it.
 
J

Jens Thoms Toerring

Bill Cunningham said:
I think alot of what my problem is too is not understanding pointers fully.
When and where to use them. That's where Jen I get lost in your code above
is the pointers. For example,
if ( *endptr != '\0' )

Most of the time when I use pointers I do not use * again in front of the
pointer. Is what you are doing called "dereferencing" ?

Yes. 'endptr' is a pointer and thus '*endptr' tells me what's
stored at the location in memory where it's pointing to. And
after a call like

input = strtod( argv[ 1 ], &endptr );

and everything went well 'endptr' will point to the trailing
'\0' character of argv[1]. If it doesn't I know that not
everything in argv[1] was something that could be interpreted
as being part of a floating point number. As an example let's
assume that argv[1] is "4.2e1XYZ". After the above call
'endptr' would point to the 'X' in argv[1] since that can't
be a valid part of a floating point number.
When to use * and &
too can give me trouble. My understanding is that & assigns an address in
memory to a pointer. Like this,

Take care: this is no dereference. This the way to define a pointer.
You can only dereference a pointer after it has been defined and
has been assigned a value (with a pointer, pointing to memory that
is yours).
ip=&word;

This is an assignment. It says the compiler to assign the address
of the variable 'word' to 'ip'. If you afterwards dereference 'ip'
you will get the same value as is stored in 'word'. So if you have

int word = 3; /* define an int variable named 'word' and
initialize it to the value 3 */
int *ip = &word; /* define an int pointer variable named 'ip'
and initialize it with the address of the
variable 'word' */

printf( "%d %d\n", word, *ip );

This will print '3' twice since that's the value stored in 'word'
and, of course, at the location 'ip' points to (i.e. the the va-
riable 'word').
Regards, Jens
 

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

Similar Threads

code 50
comparison error 12
code question 74
URGENT 1
sh?tpile of errors 82
seg fault 76
Printing with fprintf on Win XP 4
[C language] Issue in the Lotka-Volterra model. 0

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,132
Latest member
TeresaWcq1
Top