stream io in c

R

Ron Ford

Can you spot the mistake in this printf call?

I think I got it:

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="chars.256";


unsigned char c;



if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", name);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= UCHAR_MAX; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars4.c

I created a problem for myself that I don't get. I checked that UCHAR_MAX
was indeed 255 on my machine, but the subtitution for 255 causes this
warning and the program to hang:


C:\MinGW\source>gcc -o chars -std=c99 -pedantic -Wall -W mkchars3.c
mkchars3.c: In function 'main':
mkchars3.c:26: warning: comparison is always true due to limited range of
data type

I think I get it. 255 ++ = 0 ?
 
R

Ron Ford

Compared to the previous version you posted, you added
"#include <stdlib.h>", you changed the file name from "text58.txt" to
"text62.txt", and you changed "return 1;" to "return EXIT_FAILURE;".

I also changed the goocher line, which is the all-important commented-out
line that one pastes into the command line.
Oh, and you didn't mention what changes you had made; I had to save
both versions and compare them myself.

You ignored the vast majority of santosh's excellent advice.

I don't know that I ignored it so much as I hadn't gotten to it.
Specifically:


You're still trying to print a FILE* value using "%s".

You're still using a misleading file name.

I think I got these.
Your indentation is still non-existent.

The indent program is quite a package:
http://i37.tinypic.com/2wntwuw.jpg

Is anyone aware of a tutorial on how to build such things?
 
B

Ben Bacarisse

Ron Ford said:
I think I got it:

Not quite...
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(void)
{
FILE *fp;
char name[]="chars.256";

unsigned char c;

You had an int here before and that was correct.
if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", name);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= UCHAR_MAX; c ++) {

See below...
putc(c, fp);
}
fclose(fp);

I'd put this in the else clause. It is a bug "waiting to happen".
Though correct at the moment (because you return when the open fails)
it will go wrong if you change that. It is safer to get into the
habit of only closing files you know you opened.
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars4.c

I created a problem for myself that I don't get. I checked that UCHAR_MAX
was indeed 255 on my machine, but the subtitution for 255 causes this
warning and the program to hang:

I get the same behaviour with both 255 and UCHAR_MAX. I can't see how
it could matter which you use. Did you change c's type at about that
time?
 
R

Ron Ford

No, the original is correct. He wants to write out the entire
range available to the char type.

Nick might be sensing the rpoblem I ran into when I turned the looping
variable to type unsigned char. You want to have a data type that will
exceed that of the greatest char so as to break the loop. Before I changed
c back to an int, char.256 was 18 megs in size.

I think this version addresses all criticisms:

//mkchars.c:

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

int main(void)
{
FILE *fp;
char name[]="chars.256";
int c;

if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", name);
return EXIT_FAILURE;
}
else
{
printf("creating %s\n", name);
for (c = 0; c <= UCHAR_MAX; c ++) {
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars4.c

Thanks all for comments.
 
V

vippstar

Ron Ford said:
I think I got it:

Not quite...
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(void)
{
FILE *fp;
char name[]="chars.256";
unsigned char c;

You had an int here before and that was correct.

Not really. To really print every single unsigned char value, try
something like this:
unsigned char c = 0;
while(c++) putchar(c-1);

UCHAR_MAX might be the largest value in the implementation. <= is
meaningless...
 
B

Ben Bacarisse

Ron Ford said:
I think I got it:

Not quite...
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(void)
{
FILE *fp;
char name[]="chars.256";
unsigned char c;

You had an int here before and that was correct.

Not really. To really print every single unsigned char value, try
something like this:
unsigned char c = 0;
while(c++) putchar(c-1);

Eh? That's a no-op.
UCHAR_MAX might be the largest value in the implementation. <= is
meaningless...

I don't understand this point.
 
V

vippstar

I think I got it:
Not quite...
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(void)
{
FILE *fp;
char name[]="chars.256";
unsigned char c;
You had an int here before and that was correct.
Not really. To really print every single unsigned char value, try
something like this:
unsigned char c = 0;
while(c++) putchar(c-1);

Eh? That's a no-op.

Whoops :p.
Alright, fix:

unsigned char c = 0;
for(putchar(c++); c; putchar(c++));
I don't understand this point.

You said unsigned int is fine. (or did you say int is fine? even
worse!)
Later in the code there is,
for (c = 0; c <= UCHAR_MAX; c ++) {
Whatever type 'c' is, this loop can run forever, and that was my
point.
 
B

Ben Bacarisse

You said unsigned int is fine. (or did you say int is fine? even
worse!)
Later in the code there is,
for (c = 0; c <= UCHAR_MAX; c ++) {
Whatever type 'c' is, this loop can run forever, and that was my
point.

I see what you mean, but that is not a practical concern in an output
loop. If UCHAR_MAX is as large the max. for any type one can give c
then the loop may as well run forever!
 
B

Barry Schwarz

Can you spot the mistake in this printf call?

I think I got it:

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="chars.256";


unsigned char c;



if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", name);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= UCHAR_MAX; c ++) {

You did better when c was an int. As it stands now, this loop can
never terminate. When c is incremented to the value UCHAR_MAX, the
loop block will execute and c will be incremented again. At this
time, it will wrap to 0 and the loop will continue. Repeat ad
infinitum.
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars4.c

I created a problem for myself that I don't get. I checked that UCHAR_MAX
was indeed 255 on my machine, but the subtitution for 255 causes this
warning and the program to hang:


C:\MinGW\source>gcc -o chars -std=c99 -pedantic -Wall -W mkchars3.c
mkchars3.c: In function 'main':
mkchars3.c:26: warning: comparison is always true due to limited range of
data type

The warning should be the same whether you code the 255 or use the
macro. The value of an unsigned char can never exceed UCHAR_MAX.
I think I get it. 255 ++ = 0 ?

Not quite. 255 will always have type int and incrementing it will
yield 256. (The macro UCHAR_MAX must also resolve to an int so it is
not the specific value but the type of the value.) However, when the
unsigned char c contains the value UCHAR_MAX, c++ will result in c
having the value 0.
 
N

Nick Keighley

Nick Keighleywrote:

... snip ...



No, the original is correct.  He wants to write out the entire
range available to the char type.

yes. I was mistaken.
 
K

Keith Thompson

Ron Ford said:
Can you spot the mistake in this printf call?

I think I got it:
Better.

//mkchars.c:



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

int main(void)
{
FILE *fp;
char name[]="chars.256";


unsigned char c;



if ((fp = fopen(name, "wb")) == NULL)
{
printf("can't open %s\n", name);

I'd change this to:

fprintf(stderr, "can't open %s\n", name);
return EXIT_FAILURE;
}
else
{
for (c = 0; c <= UCHAR_MAX; c ++) {

As others have pointed out, this loop never terminates. c is of type
unsigned char; it's *always" <= UCHAR_MAX, no matter what you do with
it.
putc(c, fp);
}
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars4.c

I created a problem for myself that I don't get. I checked that UCHAR_MAX
was indeed 255 on my machine, but the subtitution for 255 causes this
warning and the program to hang:


C:\MinGW\source>gcc -o chars -std=c99 -pedantic -Wall -W mkchars3.c
mkchars3.c: In function 'main':
mkchars3.c:26: warning: comparison is always true due to limited range of
data type

I think I get it. 255 ++ = 0 ?

Um, no. 255++ is an error (specifically a constraint violation).

Remember, the "++" operator (prefix or postfix) does two things: it
modifies its argument, and it yields a value (either the previous
value or the modified value depending on where you put the "++").
You can't modify 255, so 255++ doesn't make any sense.

New C programmers are often fascinated by C's ++ operator. It's such
a cool and terse way to specify that you want to increment something.
But "x++" doesn't mean "x+1"; it's more complex than that. If you
want the incremented value of x without modifying x as a side effect,
just write "x+1". (And if you replace x with a constant 255,
you *can't* modify it.)

So what you're really asking is whether 255 + 1 == 0.

As stated, no, but there's a true statement hiding in there.

*If* UCHAR_MAX is 255 (as it very commonly is), and if you have an
object of type unsigned char with the value 255, then incrementing
that object will cause it to have the value 0.

For all unsigned types, arithmetic is performed in a wraparound
manner. So UINT_MAX + 1U == 0U, and ULONG_MAX + 1UL == 0UL.
Conversely, 0U - 1U == UINT_MAX.

And in many cases you still have to worry about promoitions and other
implicit conversions. For example, consider this:

#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned char c = UCHAR_MAX;
unsigned char c1 = c + 1;
int i1 = c + 1;

printf("c1 = %d\n", (int)c1);
printf("i1 = %d\n", i1);
return 0;
}

c1 gets the value 0; i1 gets the value 256. That's because, in the
expression c + 1, the value of c is implicitly promoted to int; adding
1 to an int value of 255 doesn't overflow or wrap around, it just
yields 256.

And although the initialization of c1 to c + 1 *acts* like it's adding
1 to an unsigned char value of 255, wrapping around, and yielding 0,
what really happens is that the value of c is promoted to int, the
expression c + 1 yields the int value 256, and that value is then
*converted* to unsigned char, yielding 0. The final result is the
same, but we took a detour from unsigned char to int and back to
unsigned char.

For integers, these implicit promotions only occur (and only in some
circumstances) for types narrower than int and unsigned int.
 
R

Ron Ford


[snipped and re-ordered]
Um, no. 255++ is an error (specifically a constraint violation).

Remember, the "++" operator (prefix or postfix) does two things: it
modifies its argument, and it yields a value (either the previous
value or the modified value depending on where you put the "++").
You can't modify 255, so 255++ doesn't make any sense.

New C programmers are often fascinated by C's ++ operator. It's such
a cool and terse way to specify that you want to increment something.
But "x++" doesn't mean "x+1"; it's more complex than that. If you
want the incremented value of x without modifying x as a side effect,
just write "x+1". (And if you replace x with a constant 255,
you *can't* modify it.)

So what you're really asking is whether 255 + 1 == 0.

As stated, no, but there's a true statement hiding in there.

*If* UCHAR_MAX is 255 (as it very commonly is), and if you have an
object of type unsigned char with the value 255, then incrementing
that object will cause it to have the value 0.

For all unsigned types, arithmetic is performed in a wraparound
manner. So UINT_MAX + 1U == 0U, and ULONG_MAX + 1UL == 0UL.
Conversely, 0U - 1U == UINT_MAX.

And in many cases you still have to worry about promoitions and other
implicit conversions. For example, consider this:

#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned char c = UCHAR_MAX;
unsigned char c1 = c + 1;
int i1 = c + 1;

printf("c1 = %d\n", (int)c1);
printf("i1 = %d\n", i1);
return 0;
}

c1 gets the value 0; i1 gets the value 256. That's because, in the
expression c + 1, the value of c is implicitly promoted to int; adding
1 to an int value of 255 doesn't overflow or wrap around, it just
yields 256.

And although the initialization of c1 to c + 1 *acts* like it's adding
1 to an unsigned char value of 255, wrapping around, and yielding 0,
what really happens is that the value of c is promoted to int, the
expression c + 1 yields the int value 256, and that value is then
*converted* to unsigned char, yielding 0. The final result is the
same, but we took a detour from unsigned char to int and back to
unsigned char.

For integers, these implicit promotions only occur (and only in some
circumstances) for types narrower than int and unsigned int.

I'm gonna need a little time to go through this more carefully.
I'd change this to:

fprintf(stderr, "can't open %s\n", name);

This change highlights the use of the second line of what I call the
goocher: the commented-out command lines. I would definitely expect
information like this to come from stderr.

//mkchars.c:

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

int main(void)
{
FILE *fp;
char name[]="chars.256";
int c;

if ((fp = fopen(name, "wb")) == NULL)
{
fprintf(stderr, "couldn't open %s\n", name);
return EXIT_FAILURE;
}
else
{
printf("creating %s\n", name);
for (c = 0; c <= UCHAR_MAX; c ++) {
putc(c, fp);
}
fprintf(stderr, "did open %s\n", name);
fclose(fp);
}
return 0;
}
// gcc -o chars -std=c99 -pedantic -Wall -W mkchars5.c
// chars >text55.txt 2>text56.txt

The first two commands were without the fprintf's:

C:\MinGW\source> gcc -o chars -std=c99 -pedantic -Wall -W mkchars5.c

C:\MinGW\source>chars
creating chars.256

C:\MinGW\source>gcc -o chars -std=c99 -pedantic -Wall -W mkchars5.c

C:\MinGW\source>chars
creating chars.256
did open chars.256

C:\MinGW\source>chars >text55.txt 2>text56.txt

C:\MinGW\source>


So it is that text55 has the "creating" and text56 has the "did open" line.
I like the reassurance from stderr that there was no error.
 
S

santosh

Ron Ford wrote:

<snip>

[code indentation corrected]
//mkchars.c:

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

int main(void)
{
FILE *fp;
char name[]="chars.256";
int c;

if ((fp = fopen(name, "wb")) == NULL)
{
fprintf(stderr, "couldn't open %s\n", name);
return EXIT_FAILURE;
}
else
{
printf("creating %s\n", name);
for (c = 0; c <= UCHAR_MAX; c ++) {
putc(c, fp);
}
fprintf(stderr, "did open %s\n", name);

Typically only error messages are sent to stderr. A normal program
message is usually sent to stdout.
fclose(fp);
}
return 0;
}

BTW, I notice that you seem to have just as much problem with consistent
formatting of your code that Bill C has.

If you use an editor like Vim or Emacs it will automatically do most of
the indenting for you. Inconsistent indentation and formatting makes
your code difficult to read, when as it is, reading properly indented
code is difficult enough.

<snip>
 
C

CBFalconer

santosh said:
Ron Ford wrote:

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

int main(void) { /* code tautened - cbf */
FILE *fp;
char name[]="chars.256";
int c;

if ((fp = fopen(name, "wb")) == NULL) {
fprintf(stderr, "couldn't open %s\n", name);
return EXIT_FAILURE;
}
else {
printf("creating %s\n", name);
for (c = 0; c <= UCHAR_MAX; c ++) {
putc(c, fp);
}
fprintf(stderr, "did open %s\n", name);

Typically only error messages are sent to stderr. A normal program
message is usually sent to stdout.

However here he is keeping track of file statuses on stderr, so I
think the file choice is quite reasonable. It doesn't disturb the
real action, and the actual output could be to stdout.
 
C

CBFalconer

santosh said:
It isn't an endless loop, but it does potentially invoke
undefined behaviour if UCHAR_MAX > INT_MAX.

I don't believe that is allowable. == yes.
 
K

Keith Thompson

CBFalconer said:
I don't believe that is allowable. == yes.

I believe it is.

Consider CHAR_BIT==32, sizeof(int)==1, 2's-complement, no padding
bits. Then UCHAR_MAX==2**32-1, and INT_MAX==2**31-1.

It causes problems for stdio, but (a) it's not clear that's enough to
make the (hypothetical) implementation non-conforming, and (b) even if
it is, we can assume it's freestanding.
 
F

Flash Gordon

Keith Thompson wrote, On 05/08/08 00:52:
I believe it is.

Consider CHAR_BIT==32, sizeof(int)==1, 2's-complement, no padding
bits. Then UCHAR_MAX==2**32-1, and INT_MAX==2**31-1.

It causes problems for stdio, but (a) it's not clear that's enough to
make the (hypothetical) implementation non-conforming,

Note that there are a lot of *real* implementations where sizeof(int)==1
and CHAR_BIT is 16 or greater (including ones where it is 32).
and (b) even if
it is, we can assume it's freestanding.

All the implementations I know of are freestanding but I don't know all
implementations.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top