0

I'm new to C and trying to write a double linked list for in C. I want to jump out of or return null value if the index out of the linked list counts but not exit the whole program. Just to print the error and return Null or something that user will recognise out of scope. I don't know if C can do it. Here is part of my code. free_node function is to return data of the node and release the node space. I just want to know what can I do to this pop function that I can deal with out of scope problem.

Thanks

typedef struct node{
    void *data;
    struct node *next,*prev,*head,*tail;
}Node;

typedef Node *List;

Node pop_node(List plist,long index){
    Node *pnode;
    pnode=direct_to_head(plist)->next;
    if(index>list_count(plist)){
        fprintf(stderr, "index out of link list scope.");
        return;
    }
    while (pnode->next!=NULL && index-->0) {
        pnode=pnode->next;
    }

    return free_node(pnode);
}
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
T.R
  • 126
  • 7

5 Answers5

1

I want to jump out of or return null value ... I don't know if C can do it.

The short answer is No

You - as the designer - have decided that the function pop_node shall return an object of type Node (aka struct node). That means that the code shall always return an object of type Node. C does not allow you to suddenly return another type. Consequently something like return NULL; will not be allowed.

I just want to know what can I do to this pop function that I can deal with out of scope problem.

You could change the function signature to return a pointer to a Node and leave the copying/freeing to the caller. In that case you can use NULL as a value for indicating "no object available".

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

If a function is declared to return void (that is, nothing), it can only return nothing. If a function is declared to return some other type T, that's all it can return.

Consider:

  1. returning a pointer to type Node (which can be NULL)
  2. returning multiple values (as a struct containing them), one being a flag or error code
  3. returning a 2nd value through an additional pointer given to the function (again, one being a flag/code)
  4. using longjmp()

Another variant of option 1 is to define a special global variable of type Node with a suitable/recognizable name, e.g. ErrorNode, and return a pointer to it. The caller can then compare the returned pointer with &ErrorNode to detect the error condition. It doesn't seem special at first sight, but if you end up needing to recognize several distinct error conditions (not too many, though), you may define several such variables (not too many because you'll need to use if/else instead of switch in order to distinguish them).

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • 1
    `longjmp` is definitely the worst solution. I would not mention it at all. – Jabberwocky Feb 25 '19 at 10:24
  • @Jabberwocky Though it exists, but it comes with problems of its own (the volatiles). – Alexey Frunze Feb 25 '19 at 10:25
  • Yes, `longjmp` exists, but it really should only be used in very specific cases, and it's not a beginner's thing. – Jabberwocky Feb 25 '19 at 10:26
  • @Jabberwocky Agreed. That's why it's listed last. – Alexey Frunze Feb 25 '19 at 10:28
  • Hi, may I know what problem longjmp has? I'm interested in it, can you tell me a little? – T.R Feb 26 '19 at 08:11
  • @T.R [setjmp()](https://en.cppreference.com/w/c/program/setjmp) and [longjmp()](https://en.cppreference.com/w/c/program/longjmp) aren't very straightforward to use. It's easy to make a mistake in their use and break the program. Read the descriptions of both functions for the details. I'm not going to restate the same. – Alexey Frunze Feb 26 '19 at 10:36
  • @T.R: I am pretty sure you do not want `longjmp()` & friends. – alk Feb 26 '19 at 16:07
0

Not nice but possible would be to define a Node with sort of a "Invalid-Node" value and in case there is no node to return just return the "Invalid-Node"-node.

const Node InvalidNode = {(void*)-1, 
  (struct node*)-1, (struct node*)-1, (struct node*)-1, (struct node*)-1
};

#define NODE_IS_INVALID(n) ( \
  ((n).data == InvalidNode.data) && \
  ((n).next == InvalidNode.next) && \
  ((n).prev == InvalidNode.prev) && \
  ((n).head == InvalidNode.head) && \
  ((n).tail == InvalidNode.tail) \
)

Then change your function to look like this:

Node pop_node(List plist,long index){
  Node *pnode;
  pnode=direct_to_head(plist)->next;
  if(index>list_count(plist)){
    fprintf(stderr, "index out of link list scope.");
    return InvalidNode;

    ...

And call it like this:

Node n = pop_node(...);
if (NODE_IS_INVALID(n))
{
   /* handle error */
}
else
{
   /* use n here */
}
alk
  • 69,737
  • 10
  • 105
  • 255
0

I have changed my function like this. I think I don't need to return pointer to node, I just need data from it. So I can free node while retrive my data.

That's my whole double-cross link list code:

//
//  double_linklist.c
//  tt
//
//  Created by tarrant on 2019/2/16.
//  Copyright © 2019 tarrant. All rights reserved.
//

#include "double_linklist.h"

static void c_free(void *p){
    free(p);
    p=NULL;
}

static Node *direct_to_index(List plist,long index){
    Node *pnode;
    unsigned int count = list_count(plist);
    if (labs(index) > count+2){
        fprintf(stderr, "index out of scope.");
        return NULL;
    }
    if (index >=0){
        if(plist->head==NULL){
            pnode=plist;
        }
        else
            pnode=plist->head;
        while (true){
            if(--index<0)
                break;
            else
                if (pnode->next!=NULL)
                    pnode=pnode->next;
                else{
                    fprintf(stderr, "invalid node %p.",pnode);
                    return NULL;
                }
        }
    }
    else{
        if(plist->tail==NULL){
            pnode=plist;
        }
        else
            pnode=plist->tail;
        while (true){
            if(++index>=0)
                break;
            else
                if (pnode->prev!=NULL)
                    pnode=pnode->prev;
                else{
                    fprintf(stderr, "invalid node %p.",pnode);
                    return NULL;
            }
        }
    }
     return pnode;
}

static Node *direct_to_head(List plist){
    return direct_to_index(plist, 0);
}

static Node *direct_to_tail(List plist){
    return direct_to_index(plist, -1);
}

void empty_list(List plist){
    Node *tmp,*current;
    plist=direct_to_head(plist);
    current=plist->next;
    while (current->next!=NULL) {
        if(current->data!=NULL){
            c_free(current->data);
        }
        tmp=current;
        current=current->next;
        c_free(tmp);
    }
    current->prev=plist;
    plist->next=current;
}

List init_list(void){
    Node *head,*tail;
    if((head = (Node *)calloc(1, sizeof(Node)))!=NULL){
        tail = (Node *)calloc(1, sizeof(Node));
        head->tail=tail;
        head->data=(unsigned int *)calloc(1, sizeof(unsigned int));
        head->next=tail;
        if(tail!=NULL){
            tail->prev=head;
            tail->data=NULL;
            tail->head=head;
            return head;
        }
    }
    fprintf(stderr, "No space in initing.");
    return NULL;
}


bool isempty_node(const Node *pnode){
    if(pnode->data==NULL)
        return true;
    return false;
}

bool free_node(Node *pnode){
    unsigned int *count;
    Node *next,*prev;
    if(pnode->next==NULL ||pnode->prev==NULL){
        fprintf(stderr, "You are empting head,tail or invaild node.");
        return false;
    }
    count=direct_to_head(pnode)->data;
    next=pnode->next;
    prev=pnode->prev;
    next->prev=prev;
    prev->next=next;
    c_free(pnode);
    c_free(pnode->data);
    --*count;
    return true;
}

void free_all_empty_nodes(List plist){
    Node *phead;
    unsigned int count=0;
    phead=direct_to_head(plist)->next;
    while (phead->next!=NULL) {
        if((phead->data==NULL)&&(free_node(phead)==false))
            fprintf(stderr, "error in empting index %d",count);
        phead=phead->next;
        count++;
    }
}

Node *pop_node(List plist,long index){
    Node *pnode,*next,*prev;
    if (index>=0)
        index++;
    else
        index--;
    pnode=direct_to_index(plist, index);
    next=pnode->next;
    prev=pnode->prev;
    pnode->head=NULL;
    pnode->tail=NULL;
    next->prev=prev;
    prev->next=next;
    return pnode;
}

unsigned int list_count(List plist){
    unsigned int *count;
    Node *phead;
    if(plist->head==NULL){
        phead=plist;
    }
    else
        phead=plist->head;
    count=phead->data;
    return *count;
}

void insert_list(const void *data,List plist,size_t size,long index){
    Node *tmp,*current;
    unsigned int *count;
    if(data==NULL){
        fprintf(stderr, "data is empty.");
        return;
    }
    tmp=(Node *)calloc(1, sizeof(Node));
    tmp->data=(void *)calloc(1, size);
    if(tmp==NULL||tmp->data==NULL){
        fprintf(stderr, "no space for allocation.\n");
        return;
    }
    memcpy(tmp->data,data,size);
    if (index<0)
        index--;
    current=direct_to_index(plist, index);
    tmp->next=current->next;
    current->next->prev=tmp;
    current->next=tmp;
    tmp->prev=current;
    tmp->head=direct_to_head(current);
    tmp->tail=direct_to_tail(current);
    count=direct_to_head(plist)->data;
    ++*count;
}

void append_list(const void *data,List plist,size_t size){
    insert_list(data,plist,size,-1);
}

bool modify_node(Node *node,const void *data,size_t size){
    if((data==NULL)||(node->prev==NULL)||(node->next)==NULL)
        return false;
    free(node->data);
    node->data=(void *)malloc(size);
    memcpy(node->data,data,size);
    return true;
}

bool modify_list(const void *data,List plist,long index,size_t size){
    Node *phead;
    if(data==NULL)
        return false;
    if (index>=0)
        index++;
    else
        index--;
    phead=direct_to_index(plist, index);
    return modify_node(phead,data,size);
}

void traverse_list(const List plist,void (*pfun)(void *pdata),int flag){
    Node *pnode;
    if(flag>=0){
        pnode=direct_to_head(plist)->next;
        while (pnode->next!=NULL) {
            (pfun)(pnode->data);
            pnode=pnode->next;
        }
    }
    else{
        pnode=direct_to_tail(plist)->prev;
        while (pnode->prev!=NULL) {
            (pfun)(pnode->data);
            pnode=pnode->prev;
        }
    }
}
T.R
  • 126
  • 7
  • As `pnode->data` is not of type `Node*` the function should not by typed `Node*` but what `pnode->data` is, namely `void*`. – alk Feb 28 '19 at 10:13
  • What does `c_free()` do? In case if `free()`s what `pnode` points to, then the following `pnode->data;` (tries) to dereference a freed pointer which invoke undefined behaviour. – alk Feb 28 '19 at 10:14
-2

You want to jump within function - Have a look at goto statement. The goto statement is used to jump anywhere within function.

goto lable;

label: statement;

In your case print the error and jump to free_node statement. return keyword in C exit the function with return code.

Practice
  • 1
  • 2
  • please do not recommend the worst operation in existence _(goto)_ - ever. – specializt Feb 25 '19 at 10:02
  • 1
    There are definitely situations where `goto` is appropriate, but I wouldn't just flat-out recommend it for a general "I want to jump out of or return" question. It's a very specific tool for the 0.01% of cases where the other control flow constructs don't offer a more clean solution. – Blaze Feb 25 '19 at 10:19