5

AoA,

I've been attempting to debug a problem in my circular linked list for 12hrs now. The function takes in an ADT which has a start and cursor field. The initial dummy cell points to itself. Insert elements. Repeat elements are not allowed.

    int setInsertElementSorted(setADT buffer, setElementT E)
    {
        bool isUnique = true;
        cellT *previous;

        previous = buffer->start;
        buffer->cursor = buffer->start->next;

        while(buffer->cursor != buffer->start){
            if(buffer->cursor->value == E){
                isUnique = false;
            } else if(E < buffer->cursor->value)
                break;
              else {   
                previous = buffer->cursor;
                buffer->cursor = buffer->cursor->next;
            }
        }

        if(isUnique != false){
            cellT *newNode = malloc(sizeof(cellT));
            newNode->value = E;
            previous->next = newNode;
            newNode->next = buffer->cursor;

            buffer->count++;
            return (buffer->count);   
            }
    }

The code takes in a series of integers and then sorts them into the LL parameter. Supposed to be used for a set (hence why no repeat entries).

The output for: 9, 8, 7, 6, 5, 4, 3, 2, 1

is.. 3, 4, 5, 6, 7, 8, 9 (what happened to the first two values?)

When inputting something like: 7, 3, 5, 1, 9, 2

out is only 7, 9 (so it can't handle values separated by more than one.. o.O)

Additional info:

typedef struct cellT {
    int value;
    struct cellT *next;
} cellT;

struct setCDT{
    int count;
    cellT *start;
    cellT *cursor;   
};

setADT setNew()
{
    setADT newNode = malloc(sizeof(struct setCDT));
    newNode->start = newNode->cursor = malloc(sizeof(cellT));
    newNode->start->next = newNode->cursor->next = newNode->start;
    newNode->count = 0;
    return (newNode);
}

setADT is a pointer type to setCDT. setElementT, however, is just a plain and simple int. Sorry for the ambiguity.

Bilal Siddiqui
  • 349
  • 3
  • 17

1 Answers1

4

Some observations:

while(buffer->cursor != buffer->start && buffer->cursor->value < E){
    if(buffer->cursor->value == E) // never true

The value == E inside the first loop is never true since the loop condition has value < E, hence encountering a value equal to E would stop iterating. Change the loop condition to <= E and simply return if a duplicate is found instead of using the flag.

The path where flag == false also does not return a value (although due to the above bug it is not reachable at the moment), and also the memory allocated for newNode leaks if the bug with flag is fixed and E exists in the list already.

The following if seems pointless, and due to no { after else the indentation is very misleading:

if(buffer->cursor != buffer->start){
    newNode->next = buffer->cursor; // would be harmless in both branches
    previous->next = newNode;       // done in both branches
} else                              // always using { } would make this clear
    previous->next = newNode;
    buffer->count++;
    return (buffer->count);

Also, don't typedef setADT as a pointer type, it's just misleading and combined with constructs like New(setADT) is almost certain to cause bugs.

Meanwhile in setNew, since there is only one node, replace newNode->start->next = newNode->cursor->next = newNode->start; with newNode->start->next = newNode->start;

Summary of changes:

int setInsertElementSorted(struct setCDT * const buffer, const int E) {
    cellT *newNode;
    cellT *previous = buffer->start;
    buffer->cursor = previous->next;

    while (buffer->cursor != buffer->start && buffer->cursor->value <= E) {
        if (buffer->cursor->value == E) {
            return buffer->count; // duplicate value
        }
        previous = buffer->cursor;
        buffer->cursor = buffer->cursor->next;
    }
    if ((newNode = malloc(sizeof(*newNode)))) {
        newNode->value = E;
        newNode->next = buffer->cursor;
        previous->next = newNode;
        buffer->count++;
    }
    return buffer->count;
}

If the bug persists, the error is likely to be elsewhere.

Code to test:

int main (int argc, char **argv) {
    struct setCDT *list = setNew();
    for (int i = 1; i < argc; ++i) {
        setInsertElementSorted(list, atoi(argv[i]));
    }
    list->cursor = list->start;
    while ((list->cursor = list->cursor->next) != list->start) {
        (void) printf("%d\n", list->cursor->value);
    }
    return EXIT_SUCCESS;
}
Arkku
  • 41,011
  • 10
  • 62
  • 84
  • Fixed the indentation. srry about that. Also, for the loop, my intention is to stop an insertion if a value repeats. That is why I had the flag. How do I change the conditions appropriately then ? – Bilal Siddiqui Jul 25 '15 at 16:44
  • Don't worry about the memory allocations. New() takes a pointer to the type and allocated memory appropriately. – Bilal Siddiqui Jul 25 '15 at 17:11
  • @BilalSiddiqui …or at least so you believe. =) It's simply bad practice, and for purposes of asking the question either hides the bug (which is inside `New`) or if `New` works correctly leads anyone reading your code astray because it works counter-intuitively. So, when asking the question here, please use `malloc` unless your question is about debugging `New` (in which case include the code for `New`). – Arkku Jul 25 '15 at 17:16
  • 1
    @BilalSiddiqui As for `New` itself, the syntax is horrible: when you do `cellT *cell = New(cellT *)` you are _not_ allocating a new `cellT *`, you are allocating a new `cellT` (without the `*`) and returning a pointer to it, which is stored in `cell`. So if you must use `New`, at least make the syntax `cellT *cell = New(cellT)`. – Arkku Jul 25 '15 at 17:18
  • I'm changing all the allocations. I suppose you're right. Didn't know that about the compiler.. thx. – Bilal Siddiqui Jul 25 '15 at 17:19
  • 1
    @BilalSiddiqui Don't cast the return value of `malloc`, http://stackoverflow.com/questions/1565496/specifically-whats-dangerous-about-casting-the-result-of-malloc – Arkku Jul 25 '15 at 17:20
  • Changing the **newNode->start->next = newNode->cursor->next = newNode->start;** line causes a seg fault. Did all the other changes.. same wrong output. This is just ridiculous. – Bilal Siddiqui Jul 25 '15 at 17:47
  • The bug is elsewhere. Its conclusive now. Regardless, I really appreciate the help. Thanks so much! :) – Bilal Siddiqui Jul 25 '15 at 17:58
  • @BilalSiddiqui I added a `main` function to test population and iteration on the list, it works for me (using only the code from my answer and the `struct` definitions copypasted from the question). One thing to watch out for is that if you want circular iteration, you must skip the `start` node on every round since it does not carry a proper `value`. So whenever you follow a `->next`, you must always check that it's not `start` before you can do anything with it. An alternative would be to have `start` carry a value, then you need to check `count == 0` when inserting and put `E` in `start`. – Arkku Jul 25 '15 at 18:10
  • I went over the code with a friend and apparently the main problem was (1) an extra (brace.... (and (2) some problems in the final if statement). Finally, (3) my print function was wrong too *facedesk*. It was a sick cycle between these three problems. I fixed mainly (1) then broke (2) and vice versa. Unbelievable. – Bilal Siddiqui Aug 04 '15 at 01:18