1

I noticed a program is looping with this code:

char line[100];

while (gets(line), strlen(line)){//some stuff}

And the program loops until I hit enter without writing in the line... How does this work?

I already know gets() stores the current line and strlen() returns the length of the string but... What makes this while loop? What are the functions of the gets() and strlen() in the while? Why is there a comma "," ?

toblerone_country
  • 577
  • 14
  • 26
  • 5
    [Please not use gets](http://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used).Instead use `fgets` – Jayesh Bhoi Aug 30 '14 at 04:17
  • 3
    @Jayesh Thanks, I will keep it in mind but, I don't want to be rude but... That's doesn't helps/answers my question... –  Aug 30 '14 at 04:27
  • 2
    That's why it was a comment, not an answer. It may not help with *this* question, but you do need to know that `gets` is dangerous. Don't complain when someone gives you useful information. – Keith Thompson Aug 30 '14 at 04:30
  • @KeithThompson I know it, but if your program is well made and just accepts the certain input and is fail test proved, it will work. Thanks –  Aug 30 '14 at 04:32
  • 1
    What does "and is fail test proved" mean? `gets` cannot be used safely; you can't prevent input that will cause `gets` to write past the end of your array, no matter how big it is. If you avoid `gets`, you can write programs that won't crash even if your cat sits on your keyboard. – Keith Thompson Aug 30 '14 at 04:42
  • No program using `gets()` is ever 100% safe. Pretend `gets()` will simply call `abort()`. Better yet, link every program of yours with a library that contains a version of `gets()` that _does_ call `abort()`. Train yourself to rigorously ignore the presence of `gets()` and never use it in code you write, and weed it out of any code you come across that you didn't write but is misguided enough to use it. – Jonathan Leffler Aug 30 '14 at 04:43
  • 1
    @JonathanLeffler: `gets` is either perfectly fine or it does something much, much worse to your program than `abort`. I understand why you're dispensing this sort of advice to neophytes, but I feel it's more effective to use the term "buffer overflow" to describe the problem and another term "really really bad" to describe its implications. Like I said, sometimes `gets` is the laziest tool for the job. – tmyklebu Aug 30 '14 at 05:49
  • 1
    @tmyklebu: and like I said, you should never use `gets()` for any purpose whatsoever. – Jonathan Leffler Aug 30 '14 at 05:51
  • 1
    @JonathanLeffler: Yeah, no. – tmyklebu Aug 30 '14 at 05:52

4 Answers4

3

First, I need to point out that the gets function is so dangerous that it's been removed from the language. You should never use it.

In your case, since your line array is 100 elements long, it won't actually cause any problems as long as each input line is short enough to fit. You can use gets "safely", but only if you have complete control over what input your program will receive. In practical terms, you hardly ever have such control. If you enter 120 characters on a line, your program will clobber memory outside the array, with arbitrarily bad results.

The fgets() function is safer, since it lets you specify a maximum length. It's also a bit harder to use; for one thing, it leaves the newline '\n' in the input array.


Now on to the question you actually asked.

char line[100];
while (gets(line), strlen(line)){//some stuff}

This uses the comma operator. The comma operator evaluates both of its operands in order (in this case, gets(line) and strlen(line) and then yields the result of its right operand.

gets(line) returns its argument, a pointer to the first character of line, or a null pointer if it failed. That result is discarded. (It still reads input data into line.)

The right operand of the comma operator is the length of the line you just read.

A condition in a while or if statement can be of any scalar type (integer, floating-point, or pointer). The condition is false if the value is equal to 0, true if it's anything else.

So your while loop will continue to execute as long as the length of the line you just read is non-zero. In other words, the loop will repeatedly read lines of input, stopping when it sees an empty line.

It's important to understand that not all commas are comma operators. For example, if a function takes two or more arguments, those arguments are separated by commas; that's part of the syntax of a function call, not the comma operator.


(Did I mention that you shouldn't use gets?)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

In the C and C++ programming languages, the comma operator (represented by the token ,) is a binary operator that evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type). Comma operator - wikipedia

Basically the comma operator evaluates the first operand gets(line), stores its result in line, and evaluates the second operand strlen(line), and returns the value. Every time you type anything at all and hit enter, strlen evaluates it as non-zero (true). If you don't type anything at all and hit enter, strlen evaluates it as zero and thus exits the while loop.

toblerone_country
  • 577
  • 14
  • 26
0

The comma operator (not to be confused with the comma that separates arguments in a function's argument list) evaluates the LHS and ignores the value, creates a sequence point, then evaluates the RHS.

In context, that means that gets() is called and the return value is thrown away. Then strlen() on the string is executed.

The condition is unreliable. First, it should not be using gets() at all; never. Never ever use gets(). Not even in throw-away code. Second, if you get EOF after a complete line, it is quite possible that the value in line is not changed by the calls to gets() that report EOF but are ignored.

You should be using:

while (fgets(line, sizeof(line), stdin) != 0 && strlen(line) > 1)
    …

The > 1 part deals with the difference between fgets() and gets() — that gets() removes a newline and fgets() does not.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Neh, `gets` is fine in throwaway code. Sometimes it's the laziest tool for the job. – tmyklebu Aug 30 '14 at 05:46
  • Don't apply for a job with me — that is the wrong attitude. Don't use bad tools for any purpose, and `gets()` is a very bad tool. – Jonathan Leffler Aug 30 '14 at 05:47
  • It's not "the wrong" attitude. Sometimes input sanitation just doesn't matter at all. – tmyklebu Aug 30 '14 at 05:51
  • We're going to have to agree to disagree. I recommend never suggesting the use of `gets()` where I can down-vote the suggestion, because I will down-vote any suggestion to use `gets()` submitted by anyone at all for any purpose at all. (It's nothing to do with you per se; it is the advice that I consider awful that gets the down-vote). Suggestions to use `gets_s()`, `fgets()` and variations like that are fine; raw `gets()` is a down-vote if I spot it. – Jonathan Leffler Aug 30 '14 at 05:53
  • @JonathanLeffler: `int main() { int p[2]; if (pipe(p)) abort(); if (dup2(p[0], 0)) abort(); close(p[1]); char buf[1]; printf("%i\n", gets(buf)); }` always prints 0. – tmyklebu Aug 30 '14 at 06:03
  • OK: clever, but not useful. I will accept that I've slightly over-stated my claim, but the tenor of the claim is still valid. The code still has undefined behaviour since you're printing a `char *` as if it was an `int`, but that too is quibbling. – Jonathan Leffler Aug 30 '14 at 06:05
  • @JonathanLeffler: Oops. Yeah, that's an error. Your point about "...under the assumption that the user of the program won't provide overly long lines" highlights exactly the sort of assumption you can often make when you're writing throwaway code, though. – tmyklebu Aug 30 '14 at 07:11
0

The while loop will continue on the condition that strlen(line) does not return 0, which occurs when gets(line) reads an empty string from STDIN.

The gets() function populates the "line" array with the input from standard input -- probably the console in your case. The return value of gets() is discarded due to the comma operator gets() reference

The strlen() function counts the number of characters in the "line" array, which will be greater than 0 for non-empty strings and 0 for an empty string.

Due to the comma operator, both expressions are evaluated in order, but only the return value of the rightmost expression (the strlen()) is used. To evaluate the while condition, a value of 0 means false (do not continue loop), and any non-zero values mean true (continue loop) comma operator reference

Joël Salamin
  • 3,538
  • 3
  • 22
  • 33
notchahm
  • 1
  • 2