Removing node from a linked listC function to find and delete a node from a singly linked listDeleting a...

How to calculate partition Start End Sector?

Assigning pointers to atomic type to pointers to non atomic type

Why Is Death Allowed In the Matrix?

What's the point of deactivating Num Lock on login screens?

Test if tikzmark exists on same page

Do I have a twin with permutated remainders?

How much RAM could one put in a typical 80386 setup?

Schoenfled Residua test shows proportionality hazard assumptions holds but Kaplan-Meier plots intersect

Why does Kotter return in Welcome Back Kotter?

Can I make popcorn with any corn?

Why not use SQL instead of GraphQL?

Why are electrically insulating heatsinks so rare? Is it just cost?

Modeling an IP Address

Is it legal for company to use my work email to pretend I still work there?

A newer friend of my brother's gave him a load of baseball cards that are supposedly extremely valuable. Is this a scam?

Is a conference paper whose proceedings will be published in IEEE Xplore counted as a publication?

I'm planning on buying a laser printer but concerned about the life cycle of toner in the machine

What does "Puller Prush Person" mean?

Watching something be written to a file live with tail

Approximately how much travel time was saved by the opening of the Suez Canal in 1869?

Why doesn't Newton's third law mean a person bounces back to where they started when they hit the ground?

Why, historically, did Gödel think CH was false?

Accidentally leaked the solution to an assignment, what to do now? (I'm the prof)

Is it important to consider tone, melody, and musical form while writing a song?



Removing node from a linked list


C function to find and delete a node from a singly linked listDeleting a linked list nodeRemoving duplicates from an unsorted linked listDesigning function prototypes for a singly-linked list API in CLinked List Delete Node FunctionRemoving a link from a linked listSmall implementation of a singly-linked list in C89Converting a 2D Array into a 2D Linked ListRemoving linked list nodesDeleting a node in Linked list






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







2












$begingroup$


I'm implementing the singly linked list data structure and functions to work with it. In particular I'm concerned about prepending and removing nodes. Currently I defined linked list as follows:



typedef struct string_list_node{
int id;
const char *value;
struct string_list_node *next;
} string_list_node_t;

string_list_node_t **string_list_alloc(int id, const char *value){
string_list_node_t *string_list_ptr = malloc(sizeof(*string_list_ptr));
string_list_node_t **list = malloc(sizeof(*list));
string_list_ptr -> id = id;
string_list_ptr -> value = value;
string_list_ptr -> next = NULL;
*list = string_list_ptr;
return list;
}


The thing I'm concerned about is the following implementation of prepend and delete_first_by_id functions:



void prepend(int id, const char *value, string_list_node_t ** list_ptr){
string_list_node_t *node_ptr = *list_ptr;
string_list_node_t *prepended_node = malloc(sizeof(*prepended_node));
prepended_node -> id = id;
prepended_node -> value = value;
prepended_node -> next = node_ptr;
*list_ptr = prepended_node;
}

//returns 0 if not found, 1 if found and list changed
int delete_first_by_id(string_list_node_t **source,
int id,
string_list_node_t ** deleted_value_out){
string_list_node_t *prev = NULL;
string_list_node_t *current = *source;
while(current && current -> id != id){
prev = current;
current = current -> next;
}

if(current){
if(!prev){
*source = current -> next;
*deleted_value_out = current;
} else {
*deleted_value_out = current;
prev -> next = current -> next;
}
return 1;
} else {
return 0;
}
}


Is this code looks ok in terms of sort of common C style? Is the behavior of the code undefined? I created a live example of usage here:



LIVE DEMO










share|improve this question









$endgroup$



















    2












    $begingroup$


    I'm implementing the singly linked list data structure and functions to work with it. In particular I'm concerned about prepending and removing nodes. Currently I defined linked list as follows:



    typedef struct string_list_node{
    int id;
    const char *value;
    struct string_list_node *next;
    } string_list_node_t;

    string_list_node_t **string_list_alloc(int id, const char *value){
    string_list_node_t *string_list_ptr = malloc(sizeof(*string_list_ptr));
    string_list_node_t **list = malloc(sizeof(*list));
    string_list_ptr -> id = id;
    string_list_ptr -> value = value;
    string_list_ptr -> next = NULL;
    *list = string_list_ptr;
    return list;
    }


    The thing I'm concerned about is the following implementation of prepend and delete_first_by_id functions:



    void prepend(int id, const char *value, string_list_node_t ** list_ptr){
    string_list_node_t *node_ptr = *list_ptr;
    string_list_node_t *prepended_node = malloc(sizeof(*prepended_node));
    prepended_node -> id = id;
    prepended_node -> value = value;
    prepended_node -> next = node_ptr;
    *list_ptr = prepended_node;
    }

    //returns 0 if not found, 1 if found and list changed
    int delete_first_by_id(string_list_node_t **source,
    int id,
    string_list_node_t ** deleted_value_out){
    string_list_node_t *prev = NULL;
    string_list_node_t *current = *source;
    while(current && current -> id != id){
    prev = current;
    current = current -> next;
    }

    if(current){
    if(!prev){
    *source = current -> next;
    *deleted_value_out = current;
    } else {
    *deleted_value_out = current;
    prev -> next = current -> next;
    }
    return 1;
    } else {
    return 0;
    }
    }


    Is this code looks ok in terms of sort of common C style? Is the behavior of the code undefined? I created a live example of usage here:



    LIVE DEMO










    share|improve this question









    $endgroup$















      2












      2








      2


      1



      $begingroup$


      I'm implementing the singly linked list data structure and functions to work with it. In particular I'm concerned about prepending and removing nodes. Currently I defined linked list as follows:



      typedef struct string_list_node{
      int id;
      const char *value;
      struct string_list_node *next;
      } string_list_node_t;

      string_list_node_t **string_list_alloc(int id, const char *value){
      string_list_node_t *string_list_ptr = malloc(sizeof(*string_list_ptr));
      string_list_node_t **list = malloc(sizeof(*list));
      string_list_ptr -> id = id;
      string_list_ptr -> value = value;
      string_list_ptr -> next = NULL;
      *list = string_list_ptr;
      return list;
      }


      The thing I'm concerned about is the following implementation of prepend and delete_first_by_id functions:



      void prepend(int id, const char *value, string_list_node_t ** list_ptr){
      string_list_node_t *node_ptr = *list_ptr;
      string_list_node_t *prepended_node = malloc(sizeof(*prepended_node));
      prepended_node -> id = id;
      prepended_node -> value = value;
      prepended_node -> next = node_ptr;
      *list_ptr = prepended_node;
      }

      //returns 0 if not found, 1 if found and list changed
      int delete_first_by_id(string_list_node_t **source,
      int id,
      string_list_node_t ** deleted_value_out){
      string_list_node_t *prev = NULL;
      string_list_node_t *current = *source;
      while(current && current -> id != id){
      prev = current;
      current = current -> next;
      }

      if(current){
      if(!prev){
      *source = current -> next;
      *deleted_value_out = current;
      } else {
      *deleted_value_out = current;
      prev -> next = current -> next;
      }
      return 1;
      } else {
      return 0;
      }
      }


      Is this code looks ok in terms of sort of common C style? Is the behavior of the code undefined? I created a live example of usage here:



      LIVE DEMO










      share|improve this question









      $endgroup$




      I'm implementing the singly linked list data structure and functions to work with it. In particular I'm concerned about prepending and removing nodes. Currently I defined linked list as follows:



      typedef struct string_list_node{
      int id;
      const char *value;
      struct string_list_node *next;
      } string_list_node_t;

      string_list_node_t **string_list_alloc(int id, const char *value){
      string_list_node_t *string_list_ptr = malloc(sizeof(*string_list_ptr));
      string_list_node_t **list = malloc(sizeof(*list));
      string_list_ptr -> id = id;
      string_list_ptr -> value = value;
      string_list_ptr -> next = NULL;
      *list = string_list_ptr;
      return list;
      }


      The thing I'm concerned about is the following implementation of prepend and delete_first_by_id functions:



      void prepend(int id, const char *value, string_list_node_t ** list_ptr){
      string_list_node_t *node_ptr = *list_ptr;
      string_list_node_t *prepended_node = malloc(sizeof(*prepended_node));
      prepended_node -> id = id;
      prepended_node -> value = value;
      prepended_node -> next = node_ptr;
      *list_ptr = prepended_node;
      }

      //returns 0 if not found, 1 if found and list changed
      int delete_first_by_id(string_list_node_t **source,
      int id,
      string_list_node_t ** deleted_value_out){
      string_list_node_t *prev = NULL;
      string_list_node_t *current = *source;
      while(current && current -> id != id){
      prev = current;
      current = current -> next;
      }

      if(current){
      if(!prev){
      *source = current -> next;
      *deleted_value_out = current;
      } else {
      *deleted_value_out = current;
      prev -> next = current -> next;
      }
      return 1;
      } else {
      return 0;
      }
      }


      Is this code looks ok in terms of sort of common C style? Is the behavior of the code undefined? I created a live example of usage here:



      LIVE DEMO







      c linked-list






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Mar 29 at 9:33









      St.AntarioSt.Antario

      1555




      1555






















          1 Answer
          1






          active

          oldest

          votes


















          7












          $begingroup$

          Style




          • POSIX reserves type names ending with _t. This means that in the future, standard c may create standardized types with the same name as string_list_node_t, and your code would not compile. See https://stackoverflow.com/a/12727104 for more details. A common alternative is to use upper camel-case for typedefs, e.g. StringListNode.

          • There is a strong case against typedefing structures. The major benefit of this is to save keystrokes, but it removes important information available to programmers. To see some opinions on this, here is another stack overflow question.

          • I have never seen code that has spaces between -> when accessing struct members. Just use prepended_node->id instead. You wouldn't write prepended_node . id, would you?


          Code Review



          Use the bool type



          A boolean type, bool, was introduced in C99. Unless you have really good reasons not to, you should use this for true/false values instead of an integer. This type is found in the <stdbool.h> header.



          Multi-use functions



          Finding nodes in a linked list is a relatively common procedure. If your list supports lookup, it is better to have a separate function that can handle all of your lookup cases:



          static void _find_node_by_id(int id, str_node *head, 
          str_node **prev, str_node **node) {
          *prev = NULL;
          *node = head;
          while(node && (*node)->id != id) {
          *prev = *node;
          *node = (*node)->next;
          }
          }


          This way, you can use the search implementation in multiple places:



          bool delete_first_by_id(string_list_node_t **source,
          int id,
          string_list_node_t ** deleted_value_out){
          string_list_node_t *prev = NULL;
          string_list_node_t *current = *source;

          _find_node_by_id(id, *source, &prev, &current);

          if(current){
          if(!prev){
          *source = current -> next;
          *deleted_value_out = current;
          } else {
          *deleted_value_out = current;
          prev -> next = current -> next;
          }
          return true;
          } else {
          return false;
          }
          }

          bool find_node_by_id(str_node *head, int id, str_node **return_ptr) {
          string_list_node_t *prev = NULL;
          *ret_ptr = head;

          _find_node_by_id(id, *source, &prev, return_ptr);

          if(*return_ptr) {
          return true;
          } else {
          return false;
          }
          }


          Note that I didn't test any of this code.



          Try to avoid pointers to pointers



          More specifically, don't use them when it's not necessary. There isn't a great reason for string_list_alloc to return string_list_node_t **. It makes much more sense to just return a pointer to the node. This does two things for you:




          1. Non-destructive operations don't need a pointer to a pointer, so it avoids either
            you or the function from needing to deference them.

          2. It makes destructive operations more explicit, as you need to take the address of
            the pointer to use the function.


          Edit addressing question in comments:




          In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?




          There seems to be some confusion on how malloced memory and pointers work, especially in the context of function return values.



          First, remember that a variable holds a value. In this case, the variable contains a memory location, whose value could be any integer greater than or equal to 0. We really don't care about the variable itself, we care about its value.



          Second, lets talk about return values. When you return a value from a function, it is copied so that the code calling the function has access to it. Here's a little program that illustrates this:



          #include <stdio.h>

          struct test_struct {
          int a;
          int b;
          };

          struct test_struct return_struct() {
          struct test_struct test = { .a = 1, .b = 2 };
          printf("The pointer in the function is: %pn", &test);
          return test;
          }

          int main(void) {
          struct test_struct main_struct;
          main_struct = return_struct();
          printf("The pointer in main is: %pn", &main_struct);
          return 0;
          }


          On my system, this program prints out



          The pointer in the function is: 0x7fffef30f048
          The pointer in main is: 0x7fffef30f070


          Notice that the addresses are different. Since the value being returned is copied, we don't need to worry about manually manipulating memory, as the compiler takes care of that for us. It works the same way when returning a pointer from function: the value we care about is copied, so that we can access it when the variable that originally held it no longer exists.



          Lets look at this in a different context.



          // what your alloc function is doing:
          int *malloc_add(int a, int b) {
          int *result = malloc(sizeof(result));
          *result = a + b;
          return result;
          }
          // what it should be doing:
          int add(int a, int b) {
          int result;
          result = a + b;
          return result;
          }


          While we get the value we want in both cases, in the first we need to perform two extra steps: we need to dereference the pointer to get the value, and we need to free the memory allocated on the heap.






          share|improve this answer











          $endgroup$













          • $begingroup$
            Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
            $endgroup$
            – St.Antario
            Mar 30 at 6:12












          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216465%2fremoving-node-from-a-linked-list%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          7












          $begingroup$

          Style




          • POSIX reserves type names ending with _t. This means that in the future, standard c may create standardized types with the same name as string_list_node_t, and your code would not compile. See https://stackoverflow.com/a/12727104 for more details. A common alternative is to use upper camel-case for typedefs, e.g. StringListNode.

          • There is a strong case against typedefing structures. The major benefit of this is to save keystrokes, but it removes important information available to programmers. To see some opinions on this, here is another stack overflow question.

          • I have never seen code that has spaces between -> when accessing struct members. Just use prepended_node->id instead. You wouldn't write prepended_node . id, would you?


          Code Review



          Use the bool type



          A boolean type, bool, was introduced in C99. Unless you have really good reasons not to, you should use this for true/false values instead of an integer. This type is found in the <stdbool.h> header.



          Multi-use functions



          Finding nodes in a linked list is a relatively common procedure. If your list supports lookup, it is better to have a separate function that can handle all of your lookup cases:



          static void _find_node_by_id(int id, str_node *head, 
          str_node **prev, str_node **node) {
          *prev = NULL;
          *node = head;
          while(node && (*node)->id != id) {
          *prev = *node;
          *node = (*node)->next;
          }
          }


          This way, you can use the search implementation in multiple places:



          bool delete_first_by_id(string_list_node_t **source,
          int id,
          string_list_node_t ** deleted_value_out){
          string_list_node_t *prev = NULL;
          string_list_node_t *current = *source;

          _find_node_by_id(id, *source, &prev, &current);

          if(current){
          if(!prev){
          *source = current -> next;
          *deleted_value_out = current;
          } else {
          *deleted_value_out = current;
          prev -> next = current -> next;
          }
          return true;
          } else {
          return false;
          }
          }

          bool find_node_by_id(str_node *head, int id, str_node **return_ptr) {
          string_list_node_t *prev = NULL;
          *ret_ptr = head;

          _find_node_by_id(id, *source, &prev, return_ptr);

          if(*return_ptr) {
          return true;
          } else {
          return false;
          }
          }


          Note that I didn't test any of this code.



          Try to avoid pointers to pointers



          More specifically, don't use them when it's not necessary. There isn't a great reason for string_list_alloc to return string_list_node_t **. It makes much more sense to just return a pointer to the node. This does two things for you:




          1. Non-destructive operations don't need a pointer to a pointer, so it avoids either
            you or the function from needing to deference them.

          2. It makes destructive operations more explicit, as you need to take the address of
            the pointer to use the function.


          Edit addressing question in comments:




          In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?




          There seems to be some confusion on how malloced memory and pointers work, especially in the context of function return values.



          First, remember that a variable holds a value. In this case, the variable contains a memory location, whose value could be any integer greater than or equal to 0. We really don't care about the variable itself, we care about its value.



          Second, lets talk about return values. When you return a value from a function, it is copied so that the code calling the function has access to it. Here's a little program that illustrates this:



          #include <stdio.h>

          struct test_struct {
          int a;
          int b;
          };

          struct test_struct return_struct() {
          struct test_struct test = { .a = 1, .b = 2 };
          printf("The pointer in the function is: %pn", &test);
          return test;
          }

          int main(void) {
          struct test_struct main_struct;
          main_struct = return_struct();
          printf("The pointer in main is: %pn", &main_struct);
          return 0;
          }


          On my system, this program prints out



          The pointer in the function is: 0x7fffef30f048
          The pointer in main is: 0x7fffef30f070


          Notice that the addresses are different. Since the value being returned is copied, we don't need to worry about manually manipulating memory, as the compiler takes care of that for us. It works the same way when returning a pointer from function: the value we care about is copied, so that we can access it when the variable that originally held it no longer exists.



          Lets look at this in a different context.



          // what your alloc function is doing:
          int *malloc_add(int a, int b) {
          int *result = malloc(sizeof(result));
          *result = a + b;
          return result;
          }
          // what it should be doing:
          int add(int a, int b) {
          int result;
          result = a + b;
          return result;
          }


          While we get the value we want in both cases, in the first we need to perform two extra steps: we need to dereference the pointer to get the value, and we need to free the memory allocated on the heap.






          share|improve this answer











          $endgroup$













          • $begingroup$
            Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
            $endgroup$
            – St.Antario
            Mar 30 at 6:12
















          7












          $begingroup$

          Style




          • POSIX reserves type names ending with _t. This means that in the future, standard c may create standardized types with the same name as string_list_node_t, and your code would not compile. See https://stackoverflow.com/a/12727104 for more details. A common alternative is to use upper camel-case for typedefs, e.g. StringListNode.

          • There is a strong case against typedefing structures. The major benefit of this is to save keystrokes, but it removes important information available to programmers. To see some opinions on this, here is another stack overflow question.

          • I have never seen code that has spaces between -> when accessing struct members. Just use prepended_node->id instead. You wouldn't write prepended_node . id, would you?


          Code Review



          Use the bool type



          A boolean type, bool, was introduced in C99. Unless you have really good reasons not to, you should use this for true/false values instead of an integer. This type is found in the <stdbool.h> header.



          Multi-use functions



          Finding nodes in a linked list is a relatively common procedure. If your list supports lookup, it is better to have a separate function that can handle all of your lookup cases:



          static void _find_node_by_id(int id, str_node *head, 
          str_node **prev, str_node **node) {
          *prev = NULL;
          *node = head;
          while(node && (*node)->id != id) {
          *prev = *node;
          *node = (*node)->next;
          }
          }


          This way, you can use the search implementation in multiple places:



          bool delete_first_by_id(string_list_node_t **source,
          int id,
          string_list_node_t ** deleted_value_out){
          string_list_node_t *prev = NULL;
          string_list_node_t *current = *source;

          _find_node_by_id(id, *source, &prev, &current);

          if(current){
          if(!prev){
          *source = current -> next;
          *deleted_value_out = current;
          } else {
          *deleted_value_out = current;
          prev -> next = current -> next;
          }
          return true;
          } else {
          return false;
          }
          }

          bool find_node_by_id(str_node *head, int id, str_node **return_ptr) {
          string_list_node_t *prev = NULL;
          *ret_ptr = head;

          _find_node_by_id(id, *source, &prev, return_ptr);

          if(*return_ptr) {
          return true;
          } else {
          return false;
          }
          }


          Note that I didn't test any of this code.



          Try to avoid pointers to pointers



          More specifically, don't use them when it's not necessary. There isn't a great reason for string_list_alloc to return string_list_node_t **. It makes much more sense to just return a pointer to the node. This does two things for you:




          1. Non-destructive operations don't need a pointer to a pointer, so it avoids either
            you or the function from needing to deference them.

          2. It makes destructive operations more explicit, as you need to take the address of
            the pointer to use the function.


          Edit addressing question in comments:




          In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?




          There seems to be some confusion on how malloced memory and pointers work, especially in the context of function return values.



          First, remember that a variable holds a value. In this case, the variable contains a memory location, whose value could be any integer greater than or equal to 0. We really don't care about the variable itself, we care about its value.



          Second, lets talk about return values. When you return a value from a function, it is copied so that the code calling the function has access to it. Here's a little program that illustrates this:



          #include <stdio.h>

          struct test_struct {
          int a;
          int b;
          };

          struct test_struct return_struct() {
          struct test_struct test = { .a = 1, .b = 2 };
          printf("The pointer in the function is: %pn", &test);
          return test;
          }

          int main(void) {
          struct test_struct main_struct;
          main_struct = return_struct();
          printf("The pointer in main is: %pn", &main_struct);
          return 0;
          }


          On my system, this program prints out



          The pointer in the function is: 0x7fffef30f048
          The pointer in main is: 0x7fffef30f070


          Notice that the addresses are different. Since the value being returned is copied, we don't need to worry about manually manipulating memory, as the compiler takes care of that for us. It works the same way when returning a pointer from function: the value we care about is copied, so that we can access it when the variable that originally held it no longer exists.



          Lets look at this in a different context.



          // what your alloc function is doing:
          int *malloc_add(int a, int b) {
          int *result = malloc(sizeof(result));
          *result = a + b;
          return result;
          }
          // what it should be doing:
          int add(int a, int b) {
          int result;
          result = a + b;
          return result;
          }


          While we get the value we want in both cases, in the first we need to perform two extra steps: we need to dereference the pointer to get the value, and we need to free the memory allocated on the heap.






          share|improve this answer











          $endgroup$













          • $begingroup$
            Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
            $endgroup$
            – St.Antario
            Mar 30 at 6:12














          7












          7








          7





          $begingroup$

          Style




          • POSIX reserves type names ending with _t. This means that in the future, standard c may create standardized types with the same name as string_list_node_t, and your code would not compile. See https://stackoverflow.com/a/12727104 for more details. A common alternative is to use upper camel-case for typedefs, e.g. StringListNode.

          • There is a strong case against typedefing structures. The major benefit of this is to save keystrokes, but it removes important information available to programmers. To see some opinions on this, here is another stack overflow question.

          • I have never seen code that has spaces between -> when accessing struct members. Just use prepended_node->id instead. You wouldn't write prepended_node . id, would you?


          Code Review



          Use the bool type



          A boolean type, bool, was introduced in C99. Unless you have really good reasons not to, you should use this for true/false values instead of an integer. This type is found in the <stdbool.h> header.



          Multi-use functions



          Finding nodes in a linked list is a relatively common procedure. If your list supports lookup, it is better to have a separate function that can handle all of your lookup cases:



          static void _find_node_by_id(int id, str_node *head, 
          str_node **prev, str_node **node) {
          *prev = NULL;
          *node = head;
          while(node && (*node)->id != id) {
          *prev = *node;
          *node = (*node)->next;
          }
          }


          This way, you can use the search implementation in multiple places:



          bool delete_first_by_id(string_list_node_t **source,
          int id,
          string_list_node_t ** deleted_value_out){
          string_list_node_t *prev = NULL;
          string_list_node_t *current = *source;

          _find_node_by_id(id, *source, &prev, &current);

          if(current){
          if(!prev){
          *source = current -> next;
          *deleted_value_out = current;
          } else {
          *deleted_value_out = current;
          prev -> next = current -> next;
          }
          return true;
          } else {
          return false;
          }
          }

          bool find_node_by_id(str_node *head, int id, str_node **return_ptr) {
          string_list_node_t *prev = NULL;
          *ret_ptr = head;

          _find_node_by_id(id, *source, &prev, return_ptr);

          if(*return_ptr) {
          return true;
          } else {
          return false;
          }
          }


          Note that I didn't test any of this code.



          Try to avoid pointers to pointers



          More specifically, don't use them when it's not necessary. There isn't a great reason for string_list_alloc to return string_list_node_t **. It makes much more sense to just return a pointer to the node. This does two things for you:




          1. Non-destructive operations don't need a pointer to a pointer, so it avoids either
            you or the function from needing to deference them.

          2. It makes destructive operations more explicit, as you need to take the address of
            the pointer to use the function.


          Edit addressing question in comments:




          In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?




          There seems to be some confusion on how malloced memory and pointers work, especially in the context of function return values.



          First, remember that a variable holds a value. In this case, the variable contains a memory location, whose value could be any integer greater than or equal to 0. We really don't care about the variable itself, we care about its value.



          Second, lets talk about return values. When you return a value from a function, it is copied so that the code calling the function has access to it. Here's a little program that illustrates this:



          #include <stdio.h>

          struct test_struct {
          int a;
          int b;
          };

          struct test_struct return_struct() {
          struct test_struct test = { .a = 1, .b = 2 };
          printf("The pointer in the function is: %pn", &test);
          return test;
          }

          int main(void) {
          struct test_struct main_struct;
          main_struct = return_struct();
          printf("The pointer in main is: %pn", &main_struct);
          return 0;
          }


          On my system, this program prints out



          The pointer in the function is: 0x7fffef30f048
          The pointer in main is: 0x7fffef30f070


          Notice that the addresses are different. Since the value being returned is copied, we don't need to worry about manually manipulating memory, as the compiler takes care of that for us. It works the same way when returning a pointer from function: the value we care about is copied, so that we can access it when the variable that originally held it no longer exists.



          Lets look at this in a different context.



          // what your alloc function is doing:
          int *malloc_add(int a, int b) {
          int *result = malloc(sizeof(result));
          *result = a + b;
          return result;
          }
          // what it should be doing:
          int add(int a, int b) {
          int result;
          result = a + b;
          return result;
          }


          While we get the value we want in both cases, in the first we need to perform two extra steps: we need to dereference the pointer to get the value, and we need to free the memory allocated on the heap.






          share|improve this answer











          $endgroup$



          Style




          • POSIX reserves type names ending with _t. This means that in the future, standard c may create standardized types with the same name as string_list_node_t, and your code would not compile. See https://stackoverflow.com/a/12727104 for more details. A common alternative is to use upper camel-case for typedefs, e.g. StringListNode.

          • There is a strong case against typedefing structures. The major benefit of this is to save keystrokes, but it removes important information available to programmers. To see some opinions on this, here is another stack overflow question.

          • I have never seen code that has spaces between -> when accessing struct members. Just use prepended_node->id instead. You wouldn't write prepended_node . id, would you?


          Code Review



          Use the bool type



          A boolean type, bool, was introduced in C99. Unless you have really good reasons not to, you should use this for true/false values instead of an integer. This type is found in the <stdbool.h> header.



          Multi-use functions



          Finding nodes in a linked list is a relatively common procedure. If your list supports lookup, it is better to have a separate function that can handle all of your lookup cases:



          static void _find_node_by_id(int id, str_node *head, 
          str_node **prev, str_node **node) {
          *prev = NULL;
          *node = head;
          while(node && (*node)->id != id) {
          *prev = *node;
          *node = (*node)->next;
          }
          }


          This way, you can use the search implementation in multiple places:



          bool delete_first_by_id(string_list_node_t **source,
          int id,
          string_list_node_t ** deleted_value_out){
          string_list_node_t *prev = NULL;
          string_list_node_t *current = *source;

          _find_node_by_id(id, *source, &prev, &current);

          if(current){
          if(!prev){
          *source = current -> next;
          *deleted_value_out = current;
          } else {
          *deleted_value_out = current;
          prev -> next = current -> next;
          }
          return true;
          } else {
          return false;
          }
          }

          bool find_node_by_id(str_node *head, int id, str_node **return_ptr) {
          string_list_node_t *prev = NULL;
          *ret_ptr = head;

          _find_node_by_id(id, *source, &prev, return_ptr);

          if(*return_ptr) {
          return true;
          } else {
          return false;
          }
          }


          Note that I didn't test any of this code.



          Try to avoid pointers to pointers



          More specifically, don't use them when it's not necessary. There isn't a great reason for string_list_alloc to return string_list_node_t **. It makes much more sense to just return a pointer to the node. This does two things for you:




          1. Non-destructive operations don't need a pointer to a pointer, so it avoids either
            you or the function from needing to deference them.

          2. It makes destructive operations more explicit, as you need to take the address of
            the pointer to use the function.


          Edit addressing question in comments:




          In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?




          There seems to be some confusion on how malloced memory and pointers work, especially in the context of function return values.



          First, remember that a variable holds a value. In this case, the variable contains a memory location, whose value could be any integer greater than or equal to 0. We really don't care about the variable itself, we care about its value.



          Second, lets talk about return values. When you return a value from a function, it is copied so that the code calling the function has access to it. Here's a little program that illustrates this:



          #include <stdio.h>

          struct test_struct {
          int a;
          int b;
          };

          struct test_struct return_struct() {
          struct test_struct test = { .a = 1, .b = 2 };
          printf("The pointer in the function is: %pn", &test);
          return test;
          }

          int main(void) {
          struct test_struct main_struct;
          main_struct = return_struct();
          printf("The pointer in main is: %pn", &main_struct);
          return 0;
          }


          On my system, this program prints out



          The pointer in the function is: 0x7fffef30f048
          The pointer in main is: 0x7fffef30f070


          Notice that the addresses are different. Since the value being returned is copied, we don't need to worry about manually manipulating memory, as the compiler takes care of that for us. It works the same way when returning a pointer from function: the value we care about is copied, so that we can access it when the variable that originally held it no longer exists.



          Lets look at this in a different context.



          // what your alloc function is doing:
          int *malloc_add(int a, int b) {
          int *result = malloc(sizeof(result));
          *result = a + b;
          return result;
          }
          // what it should be doing:
          int add(int a, int b) {
          int result;
          result = a + b;
          return result;
          }


          While we get the value we want in both cases, in the first we need to perform two extra steps: we need to dereference the pointer to get the value, and we need to free the memory allocated on the heap.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Apr 3 at 16:45

























          answered Mar 29 at 20:52









          SoupySoupy

          1863




          1863












          • $begingroup$
            Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
            $endgroup$
            – St.Antario
            Mar 30 at 6:12


















          • $begingroup$
            Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
            $endgroup$
            – St.Antario
            Mar 30 at 6:12
















          $begingroup$
          Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
          $endgroup$
          – St.Antario
          Mar 30 at 6:12




          $begingroup$
          Thanks for the review. But I have one more question about string_list_node_t **. In case I want to preserve it across different function calls I should not allocate it on the stack shouldn't I?
          $endgroup$
          – St.Antario
          Mar 30 at 6:12


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216465%2fremoving-node-from-a-linked-list%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

          How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

          Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...