1
\$\begingroup\$

I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it. Note that, for the sake of exercise, I don't want to use techniques not introduced yet in the book and I'm compiling with the -ansi flag.

K&R Exercise 1-16

Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text.

Solution

Note the explicit requirement to revise the main routine. I'm assuming that the author expect us to find a way to use the 2 functions as they are to solve the problem. I've seen solutions which change the getline function, but I would say they're wrong even though the resulting program does what it should. Even the solution book revised the getline function, how disappointing.

/* Exercise 1-16. Revise the main routine of the longest-line program so
 * it will correctly print the length of arbitrarily long input lines,
 * and as much as possible of the text. */

#include <stdio.h>
#define MAXLINE 10          /* buffer size */

int getline(char line[], int maxline);
void copy(char to[], char from[]);

/* print the longest input line length and the line itself or the first
 * MAXLINE-1 characters if the line couldn't fit in the buffer */
main()
{
    int len;                /* current line length */
    int lenx;               /* extra length to add for too long lines */
    int max;                /* maximum length seen so far */
    char line[MAXLINE];     /* full line or beginning of line */
    char linex[MAXLINE];    /* overflow buffer */
    char longest[MAXLINE];  /* longest line saved here */

    max = 0;
    while ((len = getline(line, MAXLINE)) > 0) {
        /* If we didn't reach the endl, consume the input until
         * the end is reached while keeping track of the length */
        if (len == MAXLINE-1 && line[MAXLINE-2] != '\n')
            while ((lenx = getline(linex, MAXLINE)) > 0 &&
            (len = len + lenx) && linex[lenx-1] != '\n')
                ;
        if (len > max) {
                max = len;
                copy(longest, line);
        }
    }

    if (max > 0) {  /* there was a line */
        if (max > MAXLINE-1) {
            printf("Longest line length: %d, first ", max);
            printf("%d characters: \n%s\n", MAXLINE-1, longest);
        }
        else
            printf("Longest line length: %d; longest line: \n%s\n", max,
            longest);
    }
    return 0;
}

/* getline:  read a line into s, return length */
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;
}

/* copy:  copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

Original program (from the book)

#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */

int getline(char line[], int maxline);
void copy(char to[], char from[]);

/* print longest input line */
main()
{
    int len;            /* current line length */
    int max;            /* maximum length seen so far */
    char line[MAXLINE];     /* current input line */
    char longest[MAXLINE];  /* longest line saved here */

    max = 0;
    while ((len = getline(line, MAXLINE)) > 0)
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0)    /* there was a line */
        printf("%s", longest);
    return 0;
}

/* getline:  read a line into s, return length */
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;
}

/* copy:  copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}
\$\endgroup\$
2
\$\begingroup\$

lets start with the book you are using is VERY obsolete.

Regarding: main() there are only two valid signatures for main() they are int main( void ) and int main( int argc, char *argv[] )

the header file: stdio.h contains a prototype for the function: getline() that does not match the signature in the posted code, so suggest changing the name of that function to something unique, like myGetline()

when compiling, always enable the warnings,, then fix those warnings. (for gcc, at a minimum use: -Wall -Wextra -Wconversion -pedantic -std=gnu11 ) Note: other compilers will use different options to perform the same functionality.

the logic is copying all line parts, overlaying them. So the only item that will be printed is the last part of the line. Suggest only copying the first part of the line and ignoring the rest.

here is what compiling the posted code causes the compiler to output:

gcc -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c" 

untitled.c:8:5: error: conflicting types for ‘getline’
 int getline(char line[], int maxline);
     ^~~~~~~

In file included from untitled.c:5:0:
/usr/include/stdio.h:616:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^~~~~~~
untitled.c:13:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main()
 ^~~~

untitled.c:49:5: error: conflicting types for ‘getline’
 int getline(char s[], int lim)
     ^~~~~~~

In file included from untitled.c:5:0:
/usr/include/stdio.h:616:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^~~~~~~

untitled.c: In function ‘getline’:
untitled.c:54:16: warning: conversion to ‘char’ from ‘int’ may alter its value [-Wconversion]
         s[i] = c;
                ^

untitled.c:56:16: warning: conversion to ‘char’ from ‘int’ may alter its value [-Wconversion]
         s[i] = c;
                ^

Compilation failed.
\$\endgroup\$
  • \$\begingroup\$ Outdated, sure. But obsolete, I'd have to disagree. It's the path I've chosen to learn C, and after I finish it I'll bring myself up to date with some other book. For now, I'm sticking to it. Thank you for the compiler warnings, I'll be including those further on (but with -ansi) and I'm getting only 3 warnings (main return, and int char conversion). The book is using ANSI C (using -std=c89 or -ansi for that reason), so while I'll remember the C11 comments I think compiling with -std=gnu11 would interfere with my learning process since I'd have to rename functions and who knows what. \$\endgroup\$ – div0man Oct 22 '18 at 8:15
  • \$\begingroup\$ As for the logic, I must give some array to the getline function so I don't see how I could avoid writing into an array on every call (even if calling it just to consume the input stream) while sticking to the exercise requirements and keeping the modification simple. On a second thought, I could make the inner loop call getchar instead. My program also prints the first part (I write the first part into line, and the rest into linex), but now I see how I could optimize it by discarding getline(linex, MAXLINE) and using getchar instead of it to consume input until end of line. \$\endgroup\$ – div0man Oct 22 '18 at 8:26
  • \$\begingroup\$ Adding -std=gnu11 adds absolutely nothing, since that's the default setting of gcc as per version 5 and beyond. Beginners should rather be using -std=c11, since they need to learn the language proper, without relying on non-standard extensions. \$\endgroup\$ – Lundin Oct 22 '18 at 9:24
  • \$\begingroup\$ @div0man It went obsolete in the year 1990. The attempt to update the book to year 1990 standardization of C was not very successful. In addition to various failures to follow the old C90 standard, the book contains numerous cases of bad practice and reliance on poorly-defined behavior. The very first bugs can be found in the classic hello world example, and from there on it only gets worse. You should not learn legacy C, you should learn modern C as used in the real world in the year 2018. Thus compile with -std=c11 and start learning from there. \$\endgroup\$ – Lundin Oct 22 '18 at 9:26
  • 1
    \$\begingroup\$ (Btw most of these compiler errors come from compiling with -std=gnu11 instead of -std=c11. The reason is that POSIX likes to defile standard headers, which is not allowed by the C language.) \$\endgroup\$ – Lundin Oct 22 '18 at 9:32
0
\$\begingroup\$

Program optimized after comments received :)

/* Exercise 1-16. Revise the main routine of the longest-line program so
 * it will correctly print the length of arbitrarily long input lines,
 * and as much as possible of the text. */

/* Some optimization after comments received:
 * https://codereview.stackexchange.com/a/205992/181968 */

#include <stdio.h>
#define MAXLINE 10          /* buffer size */

int getline(char line[], int maxline);
void copy(char to[], char from[]);

/* print the longest input line length and the line itself or the first
 * MAXLINE-1 characters if the line couldn't fit in the buffer */
main()
{
    int len;                /* current line length */
    int max;                /* maximum length seen so far */
    char line[MAXLINE];     /* current [beginning of] line */
    char longest[MAXLINE];  /* [beginning of] longest line saved here */
    int c;

    max = 0;
    while ((len = getline(line, MAXLINE)) > 0) {
        /* If we didn't reach the endl, consume the input until
         * the end is reached while keeping track of the length */
        if (len == MAXLINE-1 && line[MAXLINE-2] != '\n')
            while ((c=getchar())!=EOF && ++len && c!='\n')
                ;
        if (len > max) {
                max = len;
                copy(longest, line);
        }
    }

    if (max > 0) {  /* there was a line */
        if (max > MAXLINE-1) {
            printf("Longest line length: %d, first ", max);
            printf("%d characters: \n%s\n", MAXLINE-1, longest);
        }
        else
            printf("Longest line length: %d; longest line: \n%s\n", max,
            longest);
    }
    return 0;
}

/* getline:  read a line into s, return length */
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;
}

/* copy:  copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.