Тема: Приклад 5.11 із книги КіР

Автори підручника розраховують на те, що читач при виконанні коду буде складати його на основі уже напрацьованого на протязі читання книги.
Ось те що вийшло у мене:

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

#define MAXLINES 5000
char *lineptr[MAXLINES];

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(void *v[], int left, int right, int (*comp)(void *, void *));
int numcmp(const char *, const char *);

int main(int argc, char *argv[])
{
    int nlines;
    int numeric = 0;

    if(argc > 1 && strcmp(argv[1], "-n") == 0)
        numeric = 1;
    if((nlines = readlines(lineptr, MAXLINES)) >= 0){
        qsort( (void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : strcmp) );
        writelines(lineptr, nlines);
        return 0;
    } else {
        printf("Too much lines\n");
        return 1;
    }
}

#include <stdlib.h>
#define MAXLEN 1000 /* max length of any input line */

int getline(char *, int);
char *alloc(int);

/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines){
    int len, nlines;
    char *p, line[MAXLEN];
    
    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
        if (nlines >= maxlines || (p = alloc(len)) == NULL)
            return -1;
        else {
            line[len-1] = '\0'; /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    return nlines;
}

int getline(char *s, int max){
    int i=0;
    while((*s = getchar()) != '\n' && *s != EOF && i < max-1){
        s++;
        i++;
    }
    if(*s == EOF)
        return 0;
    if(*s == '\n')
        *s++ = '\n';
    i++;
    *s = '\0';
    return i;
}

/* writelines:  write output lines */
void writelines(char *lineptr[], int nlines){
       while (nlines-- > 0)
           printf("%s\n", *lineptr++);
}

int numcmp(const char *s1, const char *s2){
    double v1, v2;

    v1 = atof(s1);
    v2 = atof(s2);
    if(v1 < v2)
        return -1;
    else if(v1 > v2)
        return 1;
    else
        return 0;
}

#define ALLOCSIZE 10000 /* size of available space */
static char allocbuf[ALLOCSIZE]; /* storage for alloc */
static char *allocp = allocbuf; /* next free position */

char *alloc(int n) /* return pointer to n characters */
{
    if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */
        allocp += n;
        return allocp - n; /* old p */
    } else /* not enough room */
    return 0;
} 

Проблема полягає в тому, що ми опрацьовуємо функцію qsort, тому прописуємо її власноруч, однак вона міститься в заголовковій бібліотеці <stdlib.h>, і визначена вона в ній інакше, тож компілятор реагує на це

C:\Program Files\PellesC\Include\stdlib.h(94): error #2120: Redeclaration of 'qsort', previously declared at C:\Users\YaR\Desktop\Creative\Programming\C\Kernighan\5.11example\main.c(9); expected 'void __cdecl function(void * *, int, int, int __cdecl (*)(void *, void *))' but found 'void __cdecl function(void *, unsigned int, unsigned int, int __cdecl (*)(const void *, const void *))'.

Ця бібліотека потрібна для того, щоб використати функцію atof(), так, її можна написати вручну, проте цікаво чи є якісь інші способи вирішення цієї проблеми?

Білий Лунь

2

Re: Приклад 5.11 із книги КіР

Покажіть повний код, який не збирається. Навскидку важко вирішити.

I belong to the Dead Generation.

3

Re: Приклад 5.11 із книги КіР

Та в повідомленні вище весь він і викладений, можна просто компілювати так:

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

#define MAXLINES 5000
char *lineptr[MAXLINES];

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(void *v[], int left, int right, int (*comp)(void *, void *));
int numcmp(const char *, const char *);

int main(int argc, char *argv[])
{
    int nlines;
    int numeric = 0;

    if(argc > 1 && strcmp(argv[1], "-n") == 0)
        numeric = 1;
    if((nlines = readlines(lineptr, MAXLINES)) >= 0){
        qsort( (void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : strcmp) );
        writelines(lineptr, nlines);
        return 0;
    } else {
        printf("Too much lines\n");
        return 1;
    }
}

І отримати помилку:

C:\Users\YaR\Desktop\Creative\Programming\C\Kernighan\123123121\main.c(10): error #2120: Redeclaration of 'qsort', previously declared at C:\Program Files\PellesC\Include\stdlib.h(94); expected 'void __cdecl function(void *, unsigned int, unsigned int, int __cdecl (*)(const void *, const void *))' but found 'void __cdecl function(void * *, int, int, int __cdecl (*)(void *, void *))'.

Білий Лунь

4

Re: Приклад 5.11 із книги КіР

Ввести в оману компілятор не вдалося, тому пропоную альтернативу:
Замість

    #include <stdlib.h>
//...
    char s[10];
//...
    atof(s);

вчинити

    #include <stdio.h>
//...
    float f;
    double d;
    char s[10], s2[10];
//...

    (sscanf(s, "%f", &f) == 1); /* функція поверне кількість успішно розібраних змінних */
    sscanf(s2, "%lf", &d); /* аналог для double: відмінність у розмірах типів даних */

З.І: ще лінкар буде лаятися, бо в оголошених вами трьох функцій нема реалізацій.

I belong to the Dead Generation.
Подякували: Ярослав1

5 Востаннє редагувалося Ярослав (26.03.2013 16:18:49)

Re: Приклад 5.11 із книги КіР

Програма має порівнювати два рядки які заздалегідь вводяться користувачем по принципу: ввів, "натиснув Enter", побачив результат. А ваш варіант, я так розумію, пропонує робити ввід після введення всіх рядків, це не зовсім те що треба.
Що треба:
Ввід:

112341234цйваіфппівапві
123423423423421413
34543543543543543
ewqerqrewrqwe12341234
1234qer12341234
asddsfasfadsfasadf
Ctrl + Z Enter

Програма при numeric = 0
Порівнює всі рядки і видає наступний результат:

112341234цйваіфппівапві
123423423423421413
1234qer12341234
34543543543543543
asddsfasfadsfasadf
ewqerqrewrqwe12341234

Програма при numeric = 1

112341234цйваіфппівапві
ewqerqrewrqwe12341234
1234qer12341234
123423423423421413
34543543543543543
asddsfasfadsfasadf

Білий Лунь

6

Re: Приклад 5.11 із книги КіР

keithfay написав:

Програма має порівнювати два рядки які заздалегідь вводяться користувачем по принципу: ввів, "натиснув Enter", побачив результат. А ваш варіант, я так розумію, пропонує робити ввід після введення всіх рядків, це не зовсім те що треба.

Я, власне, навів спосіб заміни функції atof(). sscanf() працює аналогічно до scanf(), просто читає з текстового рядка.

А для чого вам atof() взагалі? Підозрюю, що десь у numcmp() для виявлення цифр. Однак це можна зробити іншими шляхами: наприклад, аналізом ASCII-кодів (48-57 є цифри).

I belong to the Dead Generation.

7

Re: Приклад 5.11 із книги КіР

Я ж про те і пишу. Там десь у перших розділах книги були вправи на те щоб посимвольно аналізувати рядок, але в цьому прикладі код я взяв із цієї вправи, там використовується функція atof(). Значить у авторів цей код працював, потрібно розібратись із цим конфліктом.

Білий Лунь
Подякували: Bartash1

8 Востаннє редагувалося Bartash (27.03.2013 02:44:27)

Re: Приклад 5.11 із книги КіР

Винесіть реалізацію numcmp() в інший .c-файл. Там підключення stdlib.h не має викликати конфлікту імен.

Суть у чому: atof() потрібна лише всередині numcmp(), однак там, де ця numcmp() викликатиметься, по тангенсу, яке у функції тіло - там використовується лише її прототип. Тому є сенс винести прототип функції numcmp() в окремий .h-файл, реалізацію - в окремий .c-файл (там же і stdlib.h доєднуєте), а у файлі, де функцію треба використати (себто той, де main() ) - просто підключаєте заголовок із прототипом numcmp(). :)

I belong to the Dead Generation.
Подякували: Ярослав1

9

Re: Приклад 5.11 із книги КіР

Точно, це вихід, дякую.

Білий Лунь

10 Востаннє редагувалося Ярослав (31.03.2013 10:28:42)

Re: Приклад 5.11 із книги КіР

Скомпілювалось:
main.c

#include <stdio.h>
#include <string.h>
#include "numcmp.h"

#define MAXLINES 5000
char *lineptr[MAXLINES];

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(void *v[], int left, int right, int (*comp)(void *, void *));


int main(int argc, char *argv[])
{
    int nlines;
    int numeric = 0;

    if(argc > 1 && strcmp(argv[1], "-n") == 0)
        numeric = 1;
    if((nlines = readlines(lineptr, MAXLINES)) >= 0){
        qsort( (void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : strcmp) );
        writelines(lineptr, nlines);
        return 0;
    } else {
        printf("Too much lines\n");
        return 1;
    }
}


#define MAXLEN 1000 /* max length of any input line */

int getline(char *, int);
char *alloc(int);

/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines){
    int len, nlines;
    char *p, line[MAXLEN];
    
    nlines = 0;
    while ((len = getline(line, MAXLEN)) > 0)
        if (nlines >= maxlines || (p = alloc(len)) == NULL)
            return -1;
        else {
            line[len-1] = '\0'; /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    return nlines;
}

int getline(char *s, int max){
    int i=0;
    while((*s = getchar()) != '\n' && *s != EOF && i < max-1){
        s++;
        i++;
    }
    if(*s == EOF)
        return 0;
    if(*s == '\n')
        *s++ = '\n';
    i++;
    *s = '\0';
    return i;
}

/* writelines:  write output lines */
void writelines(char *lineptr[], int nlines){
       while (nlines-- > 0)
           printf("%s\n", *lineptr++);
}

#define ALLOCSIZE 10000 /* size of available space */
static char allocbuf[ALLOCSIZE]; /* storage for alloc */
static char *allocp = allocbuf; /* next free position */

char *alloc(int n) /* return pointer to n characters */
{
    if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */
        allocp += n;
        return allocp - n; /* old p */
    } else /* not enough room */
    return 0;
} 

numcmp.c

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

int numcmp(const char *s1, const char *s2){
    double v1, v2;

    v1 = atof(s1);
    v2 = atof(s2);
    if(v1 < v2)
        return -1;
    else if(v1 > v2)
        return 1;
    else
        return 0;
}

numcmp.h

int numcmp(const char *, const char *);
Білий Лунь
Подякували: Bartash1

11

Re: Приклад 5.11 із книги КіР

Спав на думку ще один варіант numcmp(): страшніший на вигляд, проте трохи коротший.

int numcmp(const char *s1, const char *s2)
{
    return (atof(s1) < atof(s2))?(-1):(atof(s1) > atof(s2));
}

Щодо ефективності цього (прямого atof() у тернарному операторі) порівняно зі створенням double-змінних: не можу наразі порівняти, бракує знань. Імовірно, отакий варіант буде швидшим:

int numcmp(const char *s1, const char *s2)
{
    register float v1 = atof(s1), v2 = atof(s2);
    return (v1 < v2)?(-1):(v1 > v2);
}
I belong to the Dead Generation.
Подякували: Ярослав1

12

Re: Приклад 5.11 із книги КіР

Bartash написав:

Спав на думку ще один варіант numcmp(): страшніший на вигляд, проте трохи коротший.

int numcmp(const char *s1, const char *s2)
{
    return (atof(s1) < atof(s2))?(-1):(atof(s1) > atof(s2));
}

Щодо ефективності цього (прямого atof() у тернарному операторі) порівняно зі створенням double-змінних: не можу наразі порівняти, бракує знань. Імовірно, отакий варіант буде швидшим:

int numcmp(const char *s1, const char *s2)
{
    register float v1 = atof(s1), v2 = atof(s2);
    return (v1 < v2)?(-1):(v1 > v2);
}

До мене тільки зараз дійшло, що можна було просто замінити ім'я qsort на щось інше :)

Білий Лунь

13

Re: Приклад 5.11 із книги КіР

keithfay написав:

До мене тільки зараз дійшло, що можна було просто замінити ім'я qsort на щось інше :)

Тоді б ускладнилася реалізація такого паттерну. :)

I belong to the Dead Generation.