3
\$\begingroup\$

I need a simple singly-linked list to help implement some memory management functionality. I just finished writing it up and would really like a code review since I haven't written this particular data structure in a long time.

struct pid_node {

  int PID;
  struct pid_node* next; 

};

 struct pid_node* pid_node_create(int PID) {
   struct pid_node* new;
   new = kmalloc(sizeof(struct pid_node));
   if (new == NULL) return NULL;
   new->PID = PID;
   new->next = NULL;
   return new; 
 }

 void add_pid_node(struct pid_node* head, struct pid_node* new) {
   struct pid_node* temp;
   temp = head;
   while(temp->next != NULL) temp = temp->next; 
   temp->next = new; 
 }

 void remove_pid_node(struct pid_node* head, struct pid_node* dead) {
   struct pid_node* temp, other_part_of_list, delete_node;
   temp = head;
   while(temp->next != NULL) {
     if (temp->next == dead) {
       delete_node = temp->next;
       other_part_of_list = temp->next->next;
       temp->next = other_part_of_list;
       kfree(delete_node); //don't leak memory
       return; 
     }
     temp = temp->next; 
   }
   kprintf("Got to end of PID list, didn't remove 'dead'!\n");
 }

 //returns true or false (1 or 0) if a particular PID is within my list
 int query_pid(int PID, struct pid_node* head) {
   struct pid_node* temp;
   temp = head;
   while(temp->next != NULL) {
     if (temp->PID == PID) return 1;
     temp = temp->next;
   }
   return 0; //didn't find it
  }

It doesn't have to be super fancy or anything. It just has to properly carry out the four functions.

\$\endgroup\$
  • 1
    \$\begingroup\$ Since you're using kmalloc, why don't you use klist? \$\endgroup\$ – Quentin Pradet Apr 9 '12 at 22:05
  • 1
    \$\begingroup\$ It is best to make the code compile before submitting it for review (other_part_of_list, delete_node should be pointers). \$\endgroup\$ – William Morris Apr 9 '12 at 22:17
  • \$\begingroup\$ Oh shoot, I meant for them to be pid_node* variables by listing them like that (e.g. int x, y, z). Does that not work for pointers? \$\endgroup\$ – YoungMoney Apr 9 '12 at 22:20
  • \$\begingroup\$ EDITED - added another snippet of code I'm pretty is wrong that I want to implement. \$\endgroup\$ – YoungMoney Apr 10 '12 at 0:19
  • \$\begingroup\$ @YoungMoney See my edit. \$\endgroup\$ – Matt Eckert Apr 10 '12 at 0:49
4
\$\begingroup\$

A couple of conceptual errors:

Pass by value

You can remove the temporary copy of head in your functions. In C, all arguments are passed by value. This means you can operate directly on the head parameter exposed to your function without worrying about the argument in the calling code. Although both point to the same location, each is a different pointer variable with its own address.

Pointer declarations

struct pid_node* temp, other_part_of_list, delete_node;

Remember that the * is not part of the type, but part of the declarator. A clearer way to write declarations involving pointers is to move the * directly in front of the identifier. This follows the C convention that "declaration mimics use." So, the line changes to

struct pid_node *temp, other_part_of_list, delete_node;

Now the error (and fix) is obvious. If there is no * before a variable, its a variable of the type instead of a pointer to the type.

struct pid_node *temp, *other_part_of_list, *delete_node;

Edit: remove head special case

In order to modify the head argument itself, which is a pointer-to-struct, you need to pass a pointer-to-pointer-to-struct or return the new head. For example (pointer-to-pointer-to-struct):

void remove_pid_node(struct pid_node **head, struct pid_node *dead) {
...
    if (*head == dead) {
         temp = *head;
         *head = temp->next; 
         kfree(temp);
         return; 
    }

Then you would call the code like this:

remove_pid_node(&head, head);

Note: don't forget to check for NULL arguments.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the feedback. About the pass-by-value: I understand what you're saying. but there's nothing wrong with what I'm doing right? \$\endgroup\$ – YoungMoney Apr 9 '12 at 23:17
  • \$\begingroup\$ Depends on what you mean by wrong. Technically you can add as many extra variables you want to any function, but it decreases readability. \$\endgroup\$ – Matt Eckert Apr 9 '12 at 23:22
  • \$\begingroup\$ Ah, I just mean functionality-wise. It should work, yes? \$\endgroup\$ – YoungMoney Apr 9 '12 at 23:44
  • 1
    \$\begingroup\$ Regarding operating directly on head, using a temp is quite ok. The compiler will optimise this away anyway. Note that if you use head directly, your code becomes untruthful; variable head will point in sequence to all the members of the list and only at the start truly points to what its name says it points to. This could be considered wrong. Calling it something other than head would cure this. I have worked at places where they would code this function parameter const struct pid_node * const head, (extra const) which prevents such modification, but that is just silly. \$\endgroup\$ – William Morris Apr 10 '12 at 0:57
  • 1
    \$\begingroup\$ Wish I could give you guys more upvotes, code review is great! \$\endgroup\$ – YoungMoney Apr 10 '12 at 1:01
2
\$\begingroup\$

General:

  • Algorithm looks good.

  • Functions normally start with the brace on column 0.

  • Add a typedef so that you can refer to just pid_node *, not struct pid_node *, throughout (except in the struct declaration).

    typedef struct pid_node pid_node;

  • I usually prefer to assign an initial value to variables where they are declared (but that is just a personal preference):

     pid_node* temp = head;
     ...
    
  • variables are not normally capitalized (PID)

add_pid_node:

  • what if head == NULL ?

remove_pid_node

  • best to put each variable definition on its own line.

  • other_part_of_list and delete_node should be pointers.

  • what if head == NULL ?

  • other_part_of_list is not necessary:

    temp->next = temp->next->next;

query_pid:

  • other functions have head first in parameter list, but here not. Inconsistent.

  • what if head == NULL ?

  • head and temp should be const

\$\endgroup\$
  • \$\begingroup\$ Wow that was great! I will definitely do some revisions based on your comments. Thanks a bunch. EDIT - I guess in my head I was implicitly thinking that I would only ever call these functions when I know head is not NULL, but that's definitely not reliable/good practice. \$\endgroup\$ – YoungMoney Apr 9 '12 at 22:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.