Home > Software engineering >  K&R 5.11 Pointers to Functions - The sorting program crashes after the first line of input
K&R 5.11 Pointers to Functions - The sorting program crashes after the first line of input

Time:01-17

I'm having some issues with this section of the book about pointers and memory management.

My program crashes after the first line of input and I can't understand where is the issue.

I tried to write and execute the code that is shown on the book - should be at page 120, the section around pointers to functions. Basically I give some input lines and they are sorted, either numerically (if I specify it with -n) or lexicographically. I don't have issue with the understanding of the pointers to function and how they work (maybe a bit), but with some unexpected crash that occurs after my first line of input. Basically when I execute the code and type something, it stops for a while and then terminates.

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

#define MAXLEN      1000
#define MAXLINES    5000

char *lineptr[MAXLINES];

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);

void myqsort(void *lineptr[], int left, int right,
           int (*comp)(void *, void *));

int numcmp(char *, char *);

/* sort input lines */
int main(int argc, char *argv[])
{
    int nlines;         /* number of input lines read */
    int numeric = 0;    /* 1 if numeric sort */

    if (argc > 1 && !strcmp(argv[1], "-n"))
        numeric = 1;

    if ((nlines = readlines(lineptr, MAXLINES)) > 0)
    {
        if (numeric)
            myqsort((void **) lineptr, 0, nlines - 1,
                  (int (*)(void *, void *))(numcmp));
        else
            myqsort((void **) lineptr, 0, nlines - 1,
                  (int (*)(void *, void *))(strcmp));

        writelines(lineptr, nlines);

        return 0;
    }
    else {
        printf("input too big to sort\n");
        return 1;
    }
}

/* get the input line */
int getline(char *line, int max)
{
    int c, i;

    for (i = 0; i < max - 1 && (c = getchar()) != EOF && c != '\n'; i  )
        line[i] = c;

    if (c == '\n')
        line[i  ] = '\n';

    line[i] = '\0';

    return i;
}

/* swap: interchange v[i] and v[j] */
void swap(void *v[], int i, int j)
{
    void *temp;

    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];
    int getline(char *line, int max);

    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
    {
        p = (char*) calloc(len, sizeof(char*));

        if ((nlines >= maxlines) || (p == NULL))
            return -1;

        if (strcmp(lineptr[nlines], "quit") == 0) {
            free(p);
            return 0;
        }

        line[len - 1] = '\0';   /* delete newline */

        strncpy(p, line, len);
        /* printf("%s\n", p); used for debugging */
        strncpy(lineptr[nlines  ], p, len);
        
        free(p);
    }

    return nlines;
}

/* writelines: write output lines */
void writelines(char *lineptr[], int nlines)
{
    int i;

    for (i = 0; i < nlines; i  )
        printf("%s\n", lineptr[i]);
}

/* myqsort: sort v[left]...v[right] into increasing order */
void myqsort(void *v[], int left, int right,
           int (*comp)(void *, void *))
{
    int i, last;
    void swap(void *v[], int i, int j);

    /* return if there are less than 2 elements */
    if (left >= right)
        return;

    swap(v, left, (left   right) / 2);
    last = left;

    for (i = left   1; i <= right; i  ) {
        if ((*comp)(v[i], v[left]) < 0)
            swap(v,   last, i);
    }

    swap(v, left, last);
    myqsort(v, left, last - 1, comp);
    myqsort(v, last   1, right, comp);
}

/* numcmp: compare s1 and s2 numerically */
int numcmp(char *s1, char *s2)
{
    double v1, v2;

    v1 = strtod(s1, NULL);
    v2 = strtod(s2, NULL);

    if (v1 < v2)
        return -1;

    if (v1 > v2)
        return 1;

    return 0;
}

I think the issues reside around the readlines function and this may be a problem of memory management, but I don't understand it at all. It's been days and this is still causing me struggles, help me please. Thank you in advance.

EDIT: I tried to run gdb and this is what happened:

Reading symbols from sort.exe...
(gdb) r
Starting program: ...\sort.exe
[New Thread 3444.0x4f8c]
test

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffde739c160 in ucrtbase!strcmp () from C:\WINDOWS\System32\ucrtbase.dll
(gdb) backtrace
#0  0x00007ffde739c160 in ucrtbase!strcmp () from C:\WINDOWS\System32\ucrtbase.dll
#1  0x00007ff631a11a4f in readlines ()
#2  0x00007ff631a1182a in main ()
(gdb)

CodePudding user response:

Uninitialized variables:

In the readlines functions:

while ((len = getline(line, MAXLEN)) > 0)
    {
        p = (char*) calloc(len, sizeof(char*));

        if ((nlines >= maxlines) || (p == NULL))
            return -1;

        if (strcmp(lineptr[nlines], "quit") == 0) {
            free(p);
            return 0;
        }

lineptr is used uninitialized. It might be pointing to anything in memory, and as such it's contents are indeterminate and your code invokes undefined behaviour.

The original K&R code first copies the contents of line to the pointer returned by calloc (or the memory allocator they wrote), and then it's address is assigned to the -ith element of lineptr on each iteration.

To cast or not to cast:

The memory allocation functions return a generic void pointer, or void * that is automatically promoted to the correct type. There's no need to cast the result of malloc and family.

A void pointer can be converted to/from any other pointer type without an explicit cast (C11 6.3.2.3 and 6.5.16.1).

  • Related