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;
}
$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
c linked-list
$endgroup$
add a comment |
$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
c linked-list
$endgroup$
add a comment |
$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
c linked-list
$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
c linked-list
asked Mar 29 at 9:33
St.AntarioSt.Antario
1555
1555
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$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 asstring_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 useprepended_node->id
instead. You wouldn't writeprepended_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, ¤t);
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:
- Non-destructive operations don't need a pointer to a pointer, so it avoids either
you or the function from needing to deference them. - 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.
$endgroup$
$begingroup$
Thanks for the review. But I have one more question aboutstring_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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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 asstring_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 useprepended_node->id
instead. You wouldn't writeprepended_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, ¤t);
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:
- Non-destructive operations don't need a pointer to a pointer, so it avoids either
you or the function from needing to deference them. - 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.
$endgroup$
$begingroup$
Thanks for the review. But I have one more question aboutstring_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
add a comment |
$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 asstring_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 useprepended_node->id
instead. You wouldn't writeprepended_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, ¤t);
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:
- Non-destructive operations don't need a pointer to a pointer, so it avoids either
you or the function from needing to deference them. - 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.
$endgroup$
$begingroup$
Thanks for the review. But I have one more question aboutstring_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
add a comment |
$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 asstring_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 useprepended_node->id
instead. You wouldn't writeprepended_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, ¤t);
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:
- Non-destructive operations don't need a pointer to a pointer, so it avoids either
you or the function from needing to deference them. - 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.
$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 asstring_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 useprepended_node->id
instead. You wouldn't writeprepended_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, ¤t);
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:
- Non-destructive operations don't need a pointer to a pointer, so it avoids either
you or the function from needing to deference them. - 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.
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 aboutstring_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
add a comment |
$begingroup$
Thanks for the review. But I have one more question aboutstring_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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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