fgets question

  • Thread starter Bill Cunningham
  • Start date
B

Bill Cunningham

I was talking with someone about fgets and he said that fgets puts the
\n in a string but not \0. I decided to test this assumption because my
documentation didn't say if fgets put \0 after a string literal. Strlen was
what I used to decide the \0 was not added. How can it be added for a
string? My code.

int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);
printf ("%i\n", strlen(input));
}

One more character than what was typed printf reported.

Bill
 
B

Ben Bacarisse

Bill Cunningham said:
I was talking with someone about fgets and he said that fgets puts the
\n in a string but not \0.

Where do you meet people to chat about C?
I decided to test this assumption because my
documentation didn't say if fgets put \0 after a string literal. Strlen was
what I used to decide the \0 was not added.

What made you think that? There must be a null byte or strlen won't
work.
How can it be added for a
string?

It is there already. If fgets runs out of room it will stop one short
just so it can always put the terminating null in the buffer. You
may not get a '\n' but you will always get a 0.
My code.

int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);
printf ("%i\n", strlen(input));

Better is:
printf ("%i\n", (int)strlen(input));

since %i needs an int not a size_t. Alternatively use "%zu" as the
format since you seem to be using somthing close to C99.
}

One more character than what was typed printf reported.

The '\n' is a character. If you type abc and hit return, fgets put
five things into the input array: 'a', 'b', 'c', '\n' and '\0'. This
is a four character string and strlen reports 4.
 
B

Bill Cunningham

Where do you meet people to chat about C?


On a yahoogroup called c-prog. It's for C++ too.
I decided to test this assumption because my
documentation didn't say if fgets put \0 after a string literal. Strlen
was
what I used to decide the \0 was not added.

What made you think that? There must be a null byte or strlen won't
work.
How can it be added for a
string?

It is there already. If fgets runs out of room it will stop one short
just so it can always put the terminating null in the buffer. You
may not get a '\n' but you will always get a 0.
My code.

int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);
printf ("%i\n", strlen(input));

Better is:
printf ("%i\n", (int)strlen(input));

Oh that looks like a cast. What are you asking printf to do?
 
B

Bill Cunningham

Jack Klein said:
Who was this person, and what credentials did he have to make you
think he was right?

More importantly, what does a string literal have to do with anything?
fgets() has nothing at all to do with string literals. It reads input
from a stream. String literals are quoted strings in your source
code, not input read from a stream.

OK I see then.
Strlen was
what I used to decide the \0 was not added. How can it be added for a
string? My code.

I surely don't understand what you were trying to do above. If you
had an array of characters that does not contain at least one '\0'
within the bounds of the array, then it is not a string. Calling
strlen() on an array of characters that is not a string, due to the
lack of a '\0', produces undefined behavior. strlen() is for strings,
not arbitrary arrays of garbage characters.
int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);

In this call, fgets() will accept up to 9 characters from stdin. Fewer
if it receives a '\n' before the ninth character.

It will store these characters into consecutive elements of the array.
Regardless of whether it stores 9 or fewer characters in the array, it
will place a '\0' into the array after the last character it stores.
[snip]

So is that extra element that isn't reported then where \0 goes ?

Bill
 
K

Keith Thompson

Bill Cunningham said:
I was talking with someone about fgets and he said that fgets puts the
\n in a string but not \0. I decided to test this assumption because my
documentation didn't say if fgets put \0 after a string literal. Strlen was
what I used to decide the \0 was not added. How can it be added for a
string? My code.

int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);
printf ("%i\n", strlen(input));
}

You had "#include said:
One more character than what was typed printf reported.

I doubt that. For example, if you typed "abc" you almost certainly
typed 4 character: 'a', 'b', 'c', and the enter key, and strlen(input)
should have given you 4.
 
K

Keith Thompson

Bill Cunningham said:
Jack Klein said:
[...]
int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);

In this call, fgets() will accept up to 9 characters from stdin. Fewer
if it receives a '\n' before the ninth character.

It will store these characters into consecutive elements of the array.
Regardless of whether it stores 9 or fewer characters in the array, it
will place a '\0' into the array after the last character it stores.
[snip]

So is that extra element that isn't reported then where \0 goes ?

The '\0' goes immediately after the last stored character from the
input, as Jack just told you.

If you typed 'a', 'b', 'c, and <return> (4 character), then fgets()
will store a total of *five* characters in your array: 'a', 'b', 'c',
'\n', and '\0'.

strlen()'s result is the length of the string *excluding* the
terminating '\0' (in this case, 4).
 
R

Richard

Bill Cunningham said:
Oh that looks like a cast. What are you asking printf to do?

Why not look up the documentation for printf and work it out yourself?
 
B

Ben Bacarisse

Bill Cunningham said:
On a yahoogroup called c-prog. It's for C++ too.

Ah. I asked because if you could talk to someone face-to-face about C
I think you'd make better progress. Online chat is not at all the same.
 
B

Bill Cunningham

Why not look up the documentation for printf and work it out yourself?

I'm pretty familiar with printf. There are things though I've seen I've
wondered about like [] in the first parameter of printf for example. I've
not seen those in the docs. And I have some pretty though stuff about
printf.

Bill
 
R

Richard Tobin

Bill Cunningham said:
I'm pretty familiar with printf. There are things though I've seen I've
wondered about like [] in the first parameter of printf for example. I've
not seen those in the docs.

What sort of "docs" are you reading? You can't expect to write C
without a proper reference, and any "doc" that doesn't explain all the
conversion specifiers is not a proper reference. Get K&R, or the
standard. Even the unix man pages tell you everything you need to
know about printf.
And I have some pretty though stuff about printf.

No you don't.

-- Richard
 
K

Keith Thompson

CBFalconer said:
No, it is a five char array, which will fit in a five (or longer)
char array, and it holds a 4 char string.

It depends on what you mean by "a 4 char string".

A "string" is, by definition,

a contiguous sequence of characters terminated by and including
the first null character.

The "length of a string" is, by definition:

the number of bytes preceding the null character

(Definitions are from C99 7.1.1p1.)

To emphasize the point, the terminating '\0' is part of the string.

So the string in question consists of exactly 5 characters, and has a
length of 4.
 
B

Ben Bacarisse

CBFalconer said:
No, it is a five char array, which will fit in a five (or longer)
char array, and it holds a 4 char string.

First, the array is (was)q of size 10. There is no "five char array"
anywhere here.

Second, I did not originally answer pete because, on balance, I think
he has a point. It is certainly valid to look at it as a five
character string (of length four). Equally, the only time this
phrasing is used in the standard, the string "a//b" is described as
being a four-character string. The only thing I am sure about is that
I don't feel strongly enough about to argue either side. In informal
postings I suspect people will continue to use both meanings and
corrections (both ways) will continue to be posted.
 
R

Richard

Bill Cunningham said:
I'm pretty familiar with printf. There are things though I've seen
I've

No you're not.
wondered about like [] in the first parameter of printf for
example. I've

Then read the bloody manual/m,an page.
not seen those in the docs. And I have some pretty though stuff about
printf.

You obviously do not. Thats it. You're trolling. No one can be this
stupid surely?
 
K

Keith Thompson

Ben Bacarisse said:
First, the array is (was)q of size 10. There is no "five char array"
anywhere here.

Second, I did not originally answer pete because, on balance, I think
he has a point. It is certainly valid to look at it as a five
character string (of length four). Equally, the only time this
phrasing is used in the standard, the string "a//b" is described as
being a four-character string. The only thing I am sure about is that
I don't feel strongly enough about to argue either side. In informal
postings I suspect people will continue to use both meanings and
corrections (both ways) will continue to be posted.

As long as we're being picky, "a//b" is referred to as a
"four-character string literal", not as a four-character string.
Strings and string literals are, of course, two very different things.
 
K

Keith Thompson

Richard Heathfield said:
Keith Thompson said:



Oooooh, I'm not so sure. There is certainly a distinction, but it's not all
that great a distinction. After all, a string literal "is-a" string (to
steal a Significant Hyphenated Phrase from a Certain Other Language),
although to be strictly accurate it is /normally/ a string - i.e. it is
normally a contiguous sequence of characters, terminated by the first null
character. The waters are muddied by the fact that "foo\0bar" is a string
literal, all the way up to the /second/ null character. That's the first
difference. The second is that you can't (legally) write to a string
literal.

No doubt I've missed one or two other differences, but they are otherwise
very similar to strings. You can do pretty much anything with a string
literal that you can do with a string tucked into a static const array of
char. (The reverse isn't quite as true - you can't, for example,
initialise an array using a static const array of char, but you can
initialise an array using a string literal.)

So yes, there /are/ differences, but there are very many similarities too.

By "string literal", you apparently mean the associated anonymous
static array that exists during program execution. What I mean by
"string literal" is a lexical element that exists in a C source file.
 
R

rahul

I was talking with someone about fgets and he said that fgets puts the
\n in a string but not \0. I decided to test this assumption because my
documentation didn't say if fgets put \0 after a string literal. Strlen was
what I used to decide the \0 was not added. How can it be added for a
string? My code.

int main (void) {
char input[10];
fgets (input, sizeof(input), stdin);
printf ("%i\n", strlen(input));

}

One more character than what was typed printf reported.

Bill

Its just that fgets retains the newline character typed. The null
character comes after the newline.
If you say fgets(buf, sizeof buf - 1, stdin) and enter "a" and
terminate the input with enter, buf will
contain "a\n\0". In contrast, if you say gets(buf), buf contains "a
\0".
P.S. - Please don't tell me gets function is dangerous and should not
be used.
 
B

Ben Bacarisse

Richard Heathfield said:
Keith Thompson said:



The Standard itself can't seem to make up its mind. At one point, it
mentions that string literals are terminated by a '\0', which clearly
isn't true if they're lexical elements: "foo"'\0' is not how we write a
string literal, is it? The terminator thing only makes sense as a runtime
concept or at least as an under-the-hood compiler thing.

But then the Standard goes on to talk about string literals as one of the
six kinds of token - and tokens are clearly lexical elements.

There might be an interesting discussion here, but it's time to go do
things...

And it does relate to the original question. The standard describes
"a//b" as a "four-character string literal" and it really can't say
anything else. While:

char a[] = "a//b";

initialises an object of size 5, one can't say anything more about
"a//b" than that is has four characters. It might, for example, be
followed by another string literal: "a//b" "b//a" might end up needing
9 bytes to store the resulting array but, at the lexical level, each
literal has only four characters.

I still contend that informally talking about "abc\n" as a
four-character string is acceptable, provided (as was the case in my
original post) that what is or is not happening with the null byte is
made clear. I agree it is /better/ to describe it as a five-character
string (whose length is four) but I, personally, would not want to
correct the first usage unless is was the cause of some ambiguity.
 
K

Keith Thompson

Ben Bacarisse said:
And it does relate to the original question. The standard describes
"a//b" as a "four-character string literal" and it really can't say
anything else. While:

char a[] = "a//b";

initialises an object of size 5, one can't say anything more about
"a//b" than that is has four characters. It might, for example, be
followed by another string literal: "a//b" "b//a" might end up needing
9 bytes to store the resulting array but, at the lexical level, each
literal has only four characters.

Actually the literal has 6 characters, but it's sufficiently obvious
from context that it's talking about the characters between the
quotation marks.
I still contend that informally talking about "abc\n" as a
four-character string is acceptable, provided (as was the case in my
original post) that what is or is not happening with the null byte is
made clear. I agree it is /better/ to describe it as a five-character
string (whose length is four) but I, personally, would not want to
correct the first usage unless is was the cause of some ambiguity.

I agree, mostly. I wouldn't actually call it a five-character string
unless I was making a very specific point; it's too likely to cause
confusion. Instead, I'd call it a string of length 4, which is
simultaneously clear *and* correct.
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top