No, I think it is clean.
Note that since the book was written, POSIX has standardized a getline()
function with a completely different interface; this can cause some grief, but it is fixable by renaming the function from K&R.
The code is:
int getline(char s[],int lim)
{
int c, i;
for (i = 0; i < lim-1 && (c=getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
Let's consider 2 cases:
- 998 characters followed by newline.
- 999 characters followed by newline.
In the first case, when the character before the newline is read, i
is 997, which is less than 999 (lim-1
), so the getchar()
is executed, the character is neither EOF nor newline, and s[997]
is assigned, and i
is incremented to 998. Since i
is still less than 999, the newline is read, and the loop is terminated. Because c
is the newline, s[998]
is given the newline and i
is incremented to 999. Then the assignment s[i] = '\0';
writes to element 999, which is safe.
The analysis in the second case is similar. When the character before the newline is read, i
is 998, which is less than 999, so getchar()
is executed, the character is neither EOF nor newline, so s[998]
is assigned, and i
is incremented to 999. Since i
is no longer less than 999, the loop exits without reading the newline; since c
is not a newline, the body of the if
after the loop is not executed; then the null is written to s[999]
, which is safe.
If EOF is detected before the newline (so the file doesn't end with a newline and technically isn't a text file according to the C standard), the loop is safely broken without overflowing the buffer.
Is there a case that isn't covered?
This is called testing the boundary conditions. It is important to test just below a limit (to make sure it works OK) and at the limit (to ensure it handles that OK). Most of the time, the algorithm doesn't need more than one test just below and one test at the limit; sometimes, if the algorithm handles several numbers either side of a limit (e.g. average of 3 cells), then you have to do more testing at the upper boundary. Lower boundary testing is also important — testing for 0, 1, 2, ... is very valuable.