2

I have the following program for books record and I want to sort the records on name of book. the code isn't showing any error but it's not sorting all the records.

#include "stdio.h"
#include "string.h"
#define SIZE 5

struct books{                                      //define struct
        char name[100],author[100];
        int year,copies;
    };

struct books book1[SIZE],book2[SIZE],*pointer;          //define struct vars

void sort(struct books *,int);                      //define sort func

main()
{
    int i;
    char c;

for(i=0;i<SIZE;i++)             //scanning values
{
    gets(book1[i].name);
    gets(book1[i].author);
    scanf("%d%d",&book1[i].year,&book1[i].copies);
    while((c = getchar()) != '\n' && c != EOF);
}
pointer=book1;
sort(pointer,SIZE);                 //sort call

i=0;                        //printing values
while(i<SIZE)
{
    printf("##########################################################################\n");
    printf("Book: %s\nAuthor: %s\nYear of Publication: %d\nNo of Copies: %d\n",book1[i].name,book1[i].author,book1[i].year,book1[i].copies);
    printf("##########################################################################\n");
    i++;
}
}

void sort(struct books *pointer,int n)
{
    int i,j,sorted=0;
    struct books temp;
for(i=0;(i<n-1)&&(sorted==0);i++)       //bubble sort on the book name
{
    sorted=1;
    for(j=0;j<n-i-1;j++)
    {
        if(strcmp((*pointer).name,(*(pointer+1)).name)>0)
        {
            //copy to temp val
            strcpy(temp.name,(*pointer).name);
            strcpy(temp.author,(*pointer).author);
            temp.year=(*pointer).year;
            temp.copies=(*pointer).copies;

            //copy next val
            strcpy((*pointer).name,(*(pointer+1)).name);
            strcpy((*pointer).author,(*(pointer+1)).author);
            (*pointer).year=(*(pointer+1)).year;
            (*pointer).copies=(*(pointer+1)).copies;

            //copy back temp val
            strcpy((*(pointer+1)).name,temp.name);
            strcpy((*(pointer+1)).author,temp.author);
            (*(pointer+1)).year=temp.year;
            (*(pointer+1)).copies=temp.copies;

            sorted=0;
        }
                *pointer++;
    }
}
}

My Imput

The C Programming Language
X Y Z
1934
56
Inferno
Dan Brown
1993
453
harry Potter and the soccers stone
J K Rowling
2012
150
Ruby On Rails
jim aurther nil
2004
130
Learn Python Easy Way
gmaps4rails
1967
100  

And the output

##########################################################################
Book: Inferno
Author: Dan Brown
Year of Publication: 1993
No of Copies: 453
##########################################################################
##########################################################################
Book: The C Programming Language
Author: X Y Z
Year of Publication: 1934
No of Copies: 56
##########################################################################
##########################################################################
Book: Ruby On Rails
Author: jim aurther nil
Year of Publication: 2004
No of Copies: 130
##########################################################################
##########################################################################
Book: Learn Python Easy Way
Author: gmaps4rails
Year of Publication: 1967
No of Copies: 100
##########################################################################
##########################################################################
Book: 
Author: 
Year of Publication: 0
No of Copies: 0
##########################################################################

We can see the above sorting is wrong? What I'm I doing wrong?

  • You are supposed to run the outer loop backwards, i. e. from n - 1 to 0. –  Oct 23 '13 at 19:28
  • @H2CO3 backwards? why so? –  Oct 23 '13 at 19:30
  • 3
    By the way, `*(pointer+1)` and `(*pointer).name` are **horrible,** please do **NOT** use them. Much readable, equivalent alternatives are `pointer[1]` and `pointer->name`. Also, proper spacing and indentation would largely boost perceptibility. Furthermore, `main()` returns `int`, and compiling with warnings enabled reveals that `*pointer++` doesn't do what you think it does. In addition to all this, using `gets()` is an extremely bad idea because it imposes security problems -- it should be avoided altogether, and `fgets()` shall always be preferred over it. –  Oct 23 '13 at 19:30
  • because else the first run of the loop doesn't have the chance to transfer the - potentially "biggest" - first element to its proper place (to the last position). –  Oct 23 '13 at 19:31
  • @H2CO3 unless you're running a bubble-*down*, which imho is actually easier to understand. There are several ways to do this, but the OP is unfortunately doing *none* of them. – WhozCraig Oct 23 '13 at 19:34
  • @WhozCraig Yes, of course, the algorithm is symmetric, but I'm right that it's one of the several semantical errors in the code, no? –  Oct 23 '13 at 19:35
  • 2
    @xmpirate Also, don't `#include "stdio.h"` and other library headers. The convention (which is safer) is to `#include `. Additionally (how many linking words do we have in English?) use `fgets()` instead of `scanf()`, because `scanf()` is evil, it's counter-intuitive, it doesn't work the way you think it works, and it's insecure. –  Oct 23 '13 at 19:37
  • @H2CO3 Yes, the OP can use arr[n-1-i], but its much easier to just change the outer loop, or do a bubble-down approach instead. The bubble-down has the added benefit that you can use unsigned indexes without having to worry about underflowing the `i` index. – WhozCraig Oct 23 '13 at 19:40
  • @WhozCraig Was I suggesting anything else than changing the outer loop? –  Oct 23 '13 at 19:43
  • @H2CO3 Can u explain why `supposed to run the outer loop backwards`? Couldn't get it to work in my code. –  Oct 23 '13 at 19:43
  • @xmpirate I have just explained that. –  Oct 23 '13 at 19:45
  • 1
    @H2CO3 No, you hit it right. It can be done with a decrementing outer loop as you described it. The outer loop, `i`, should run (n-1) down-to 1, the inner loop, `j`, from 0 up-to (i-1). I prefer a bubble-down, where the outer loop runs from 0..(n-1), the inner loop ((i+1)..(n-i-1). Coding it with pointers make the latter much more natural to envision (at least for me). – WhozCraig Oct 23 '13 at 19:46
  • @WhozCraig Yup, that's exact. :) –  Oct 23 '13 at 19:47
  • @H2CO3 that worked! you should post it as an answer. :) –  Oct 23 '13 at 19:52
  • @xmpirate There are more errors in your code... –  Oct 23 '13 at 19:54
  • I modified all those. except the `gets()` one. Well these won't be called as errors I guess. They are just a bad way of coding or not knowing the language very well. :/ –  Oct 23 '13 at 19:55
  • @H2CO3 and why I used `get()` is mentioned [here](http://stackoverflow.com/q/19376077/2675010) –  Oct 23 '13 at 19:56
  • @xmpirate No. I mean **semantic errors.** Your program has undefined behavior as-is, one reason is the erroneous increment of the pointer. From that question, I can't really get why you are using `gets()` instead of `fgets()`. –  Oct 23 '13 at 19:57
  • I change that to `pointer[j]` as you mentioned. –  Oct 23 '13 at 19:58
  • @H2CO3 And for what its worth, the bubble down approach is the *only* one that works remotely well if [bubble-sorting a forward-only linked list](http://stackoverflow.com/questions/19522121/how-to-sort-a-linked-list-using-bubble-sort/19524059#19524059). (just happened to have answered that question a day ago). If you think pointer-math in this question is tedious, check *that* answer out, which does it using only two pointers-to-pointers and *no* others. – WhozCraig Oct 23 '13 at 19:58
  • @WhozCraig That's cumbersome :/ –  Oct 23 '13 at 20:03

1 Answers1

2

There are numerous problems with the code. The most blatant semantical error is that you are messing up the pointers (you increment the pointer in the inner loop but you never re-set it), so the code will invoke undefined behavior upon the second iteration by reading from and writing to unallocated memory.

The second problem is that you've got the bubble sort algorithm wrong -- if you are doing a bubble-up, then you have to run the outer loop from n - 1 to 0 downwards, else the first iteration won't have the chance to move the first (and potentially greatest) element in place. Subsequent iterations of the outer loop inherit the same behavior.

The rest of the problems are related to readability, style, design and safety. One is that you wrote (*pointer).member which should for the love of God be written as pointer->member. Another, similar one is (*(pointer + index)).member... That could just be pointer[index].member. The lack of proper formatting, indentation and whitespace is the third problem, using #include "" instead of #include <> for standard library headers is the fourth one.

Using gets() is a bad idea too, since it doesn't let you specify a buffer size which leads to potential buffer overflows. fgets() should always be preferred instead.

Code duplication and redundancy is bad -- you should always attempt procedural decomposition (this is the almost-blasphemous "refactoring" thing...) by pulling repetitive tasks (such as copying a books structure) out to their own functions. Naming variables is another important piece of design. Don't call something a pointer, that's not helpful to the reader (while reading your code, I was wondering, for a quite long time, as to what that pointer is a pointer to...)

You should also avoid global variables when possible. In your case, there is absolutely no need for any global variables whatsoever, so nothing really justifies their use. Another piece of good practice is to declare private helper functions as static so you decrease the risk of name collision.

The last fine point is: don't abuse comments. A line like

sort(books, SIZE); // call sort function

is not helpful. What else could sort(books, SIZE) possibly mean (in a well-designed code base anyway) than sorting the books array?

All in all, this is how I would have written the code:

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

#define SIZE 5

struct book {
    char title[100];
    char author[100];
    int year;
    int copies;
};

static void sort(struct book *books, int n);

int main()
{
    int i;
    int c; // EOF is out-of-range for `char`, this MUST be an int
    struct book books[SIZE];

    for (i = 0; i < SIZE; i++) {
        fgets(books[i].title, sizeof books[i].title, stdin);
        fgets(books[i].author, sizeof books[i].author, stdin);
        scanf("%d%d", &books[i].year, &books[i].copies);
        while ((c = getchar()) != '\n' && c != EOF);
    }

    sort(books, SIZE);

    for (i = 0; i < SIZE; i++) {
        printf("##########################################################################\n");
        printf("Book: %s\nAuthor: %s\nYear of Publication: %d\nNo of Copies: %d\n", books[i].title, books[i].author, books[i].year, books[i].copies);
        printf("##########################################################################\n");
    }
}

static void swap(struct book *a, struct book *b)
{
    struct book tmp = *a;
    *a = *b;
    *b = tmp;
}

static void sort(struct book *books, int n)
{
    int i, j;
    for (i = n - 1; i >= 0; i--) {
        for (j = 0; j < i; j++) {
            if (strcmp(books[j].title, books[j + 1].title) > 0) {
                swap(&books[j], &books[j + 1]);
            }
        }
    }
}
  • 2
    +1 And note as written the outer loop will not play nicely if handed a list of length zero *and* the magnitudes and local indexes are as usually presented: *unsigned* . For that the loop can be restructured by initializing `unsigned int i=n;`, then using `while(i-- != 0)` for the outer loop construct. Or simply use `for (i=n; i-- != 0;)`. Both ways eliminate problems of underflow with unsigned indexes. It is minor, but still worth noting. Regardless, This is a terrific answer, goes above and beyond the OP's original question to bring forth additional flaws, and I'd up-vote it again if I could. – WhozCraig Oct 23 '13 at 21:25
  • @WhozCraig That's a fair point; we could then even point out that `size_t` would be more appropriate instead of plain old `int`. And thank you for your support! :-) –  Oct 23 '13 at 21:31
  • @WhozCraig Also, having a second look at the code, the entire `dup()` function is completely superfluous. `swap()` could just be composed of three simple assignments. –  Oct 23 '13 at 21:47
  • @H2CO3 I appreciate your answer but I couldn't understand why there is `bubble-down` rather than the `bubble-up`? Can u explain it with an example. –  Oct 26 '13 at 16:09
  • @xmpirate There isn't -- your approach is a bubble-up algorithm, and I respected that. –  Oct 26 '13 at 16:14
  • you mentioned in your first comment of this post that `You are supposed to run the outer loop backwards`. Also the sort function doesn't work properly with `bubble-up` –  Oct 26 '13 at 16:37
  • @xmpirate It does work with bubble-up (as I just mentioned, the code uses that approach). I don't understand what your problem is. –  Oct 26 '13 at 16:38