The first message compiling your code is
link_list_test.c(7,5): warning C4013: 'create_new_node' undefined; \
assuming extern returning int
And where are the lines of create_new_node()? Just below the floating lines
struct node_lib *first_node;
first_node = malloc(sizeof(struct node_lib));
if (first_node == NULL) {
printf("Unable to allocate memory for new node_lib\n");
exit(EXIT_FAILURE);
}
first_node->value = 0;
in link_list_test.c
as told in other answers you can not have this code outside functions. Wrapping those misplaced lines inside a function, let us say first_code()
void first_code()
{
first_node = malloc(sizeof(struct node_lib));
if (first_node == NULL)
{
printf(
"Unable to allocate memory for new node_lib\n");
exit(EXIT_FAILURE);
}
first_node->value = 0;
return;
}
makes the compiler ok with the code and 2 errors remain:
test_lib.c(41,12): warning C4477: 'printf' : format string '%n' \
requires an argument of type 'int *', but \
variadic argument 1 has type 'int'
test_lib.c(41,12): warning C4313: 'printf': '%n' in format string \
conflicts with argument 1 of type 'int'
here
printf("%n\n", value);
and by changing %n to the normal%d your code compiles ok.
I will go on a bit to write something that you may or may not find useful, so you can stop reading here.
A linked list is a data structure. A container of some stuff. In your case you are trying to build a list of int.
But a list is a collection of nodes and are the nodes that point to (or contain) a unit of the thing that is supposed to go into the list.
You should write the code in this way and your life will be easier: your next list can be the unavoiadable list of books, then the playlists, them some Person struct ;)
Also you shoud never use globals. Globals are a maintenance disaster and forbidden in may places, business and academy.
- As a list is a dynamic thing consider using only pointers.
- For help in testing write at first a function to navigate the list.
- avoid
extern as long as you could. This can lead you to many errors on linking programs. You do not need this here
- always write your code the way you did: a header and the functions, and the
main() program in a separate file. This way you can write many tests using 10s of main without changing the code in main. Never write all in a single file: testing is a mess and you will not be able to use the same code in other programs...
I will change your code as an example:
Here we can have the node and the list:
typedef struct st_node
{
int value;
struct st_node* next;
} Node;
typedef struct
{
size_t size;
Node* start;
} List;
And you see the list has a convenient size that is size_t, unsigned. And the list contains only a pointer to Node
making life easier: creating and destroying List
List* create();
List* destroy(List*);
Use pointers. Pointers are great in C. Also we will need
int show(List*, const char*);
int insert(int, List*);
So test_lib.h is now
#ifndef TEST_LIB_H
#define TEST_LIB_H
#include <stdio.h>
typedef struct st_node
{
int value;
struct st_node* next;
} Node;
typedef struct
{
size_t size;
Node* start;
} List;
List* create();
List* destroy(List*);
int show(List*, const char*);
int insert_end(int, List*);
int insert_start(int, List*);
#endif
implementing the 4 functions
create()
List* create()
{
List* one = (List*) malloc(sizeof(List));
if ( one == NULL ) return NULL;
one->size = 0;
one->start = NULL;
return one;
};
It is easier to return a new List this way and each new List will have size 0 and pointers and whatever metadata you can need. A name maybe? Date of creation?
destroy()
Since we know the size it is just a loop.
List* destroy(List* L)
{ // here you write the code to
// delete the `size` nodes and
// free them
if (L == NULL) return NULL;
Node* p = L->start; // the 2nd or NULL
Node* temp = NULL;
for (size_t i = 0; i < L->size; i += 1)
{ // delete a node
temp = p->next;
free(p);
p = temp;
} // for
free(L);
return NULL;
}
Why return NULL? For safety. This code:
#include <stdio.h>
#include <stdlib.h>
#include "test_lib.h"
int main(void)
{
List* one = create();
show(one, "still empty");
one = destroy(one);
return 0;
}
Runs and shows
still empty
List: 0 elements
So you see that the pointer to the list can be set to NULL at the same line the list is destroyed: zero chances of using an invalid List pointer.
show()
Also simple, since each List has a size inside (encapsulation). And the msgis very convenient for test, as in the example above
int show(List* L, const char* msg)
{
if (L == NULL) return -1; // no list
if (msg != NULL) printf("%s\n", msg);
Node* p = L->start; // can be NULL
printf("List: %d elements\n", (int)L->size);
for (size_t i = 0; i < L->size; i += 1, p = p->next)
printf(" %d", p->value);
printf("\n"); // all in one line: just a test
return 0;
}
Inserting at the end of the list insert_end()
This is very similar to show since we have only forward pointers
int insert_end(int value, List* l)
{ // the list has `size` elements
// to insert at the end the last one
// will point to this new one
// the code is similar to show()
if (l == NULL) return -1; // no list
Node* new = (Node*)malloc(sizeof(Node));
new->value = value;
new->next = NULL; // will be the last
Node* p = l->start;
Node* last = NULL;
for (size_t i = 0; i<l->size; i += 1,p=p->next)
last = p;
// so `last` points to the last
// even if the list was empty
l->size += 1;
if (l->size == 1)
{ // ok: was empty
l->start = new; // new start
return 1;
}
last->next = new;
return (int) l->size;
}
Insert at the start is easier insert_start()
int insert_start(int value, List* l)
{
if (l == NULL) return -1; // no list
Node* new = (Node*)malloc(sizeof(Node));
new->value = value;
new->next = NULL;
// list may be empty
if (l->size == 0)
{
l->start = new;
l->size = 1;
return 1;
}
new->next = l->start;
l->start = new;
l->size += 1;
return (int) l->size;
}
a test: insert 1 to 5 at start 10 to 6 at the end
#include <stdio.h>
#include <stdlib.h>
#include "test_lib.h"
int main(void)
{
List* one = create();
show(one, "still empty");
for (int i = 5; i >= 1; i -= 1) insert_start(i, one);
show(one, "1 to 5");
for (int i = 6; i <= 10; i += 1) insert_end(i, one);
show(one, "\n1 to 10 if ok...");
one = destroy(one);
return 0;
}
That shows
still empty
List: 0 elements
1 to 5
List: 5 elements
1 2 3 4 5
1 to 10 if ok...
List: 10 elements
1 2 3 4 5 6 7 8 9 10
If you are still reading you may find writing this way easier to read and maintain.
code for test_lib.c
This is the same as above but easier to copy if you want to test
#include <stdio.h>
#include <stdlib.h>
#include "test_lib.h"
List* create()
{
List* one = (List*)malloc(sizeof(List));
if (one == NULL) return NULL;
one->size = 0;
one->start = NULL;
return one;
}
List* destroy(List* L)
{
if (L == NULL) return NULL;
Node* p = L->start; // the 2nd or NULL
Node* temp = NULL;
for (size_t i = 0; i < L->size; i += 1)
{ // delete a node
temp = p->next;
free(p);
p = temp;
} // for
free(L);
return NULL;
}
int show(List* L, const char* msg)
{
if (L == NULL) return -1; // no list
if (msg != NULL) printf("%s\n", msg);
Node* p = L->start; // can be NULL
printf("List: %d elements\n", (int)L->size);
for (size_t i = 0; i < L->size; i += 1)
{
printf(" %d", p->value);
p = p->next;
}
printf("\n"); // all in one line: just a test
return 0;
}
int insert_end(int value, List* l)
{ // the list has `size` elements
// to insert at the end the last one
// will point to this new one
// the code is similar to show()
if (l == NULL) return -1; // no list
Node* new = (Node*)malloc(sizeof(Node));
new->value = value;
new->next = NULL; // will be the last
Node* p = l->start;
Node* last = NULL;
for (size_t i = 0; i<l->size; i += 1,p=p->next)
last = p;
// so `last` points to the last
// even if the list was empty
l->size += 1;
if (l->size == 1)
{ // ok: was empty
l->start = new; // new start
return 1;
}
last->next = new;
return (int) l->size;
}
int insert_start(int value, List* l)
{
if (l == NULL) return -1; // no list
Node* new = (Node*)malloc(sizeof(Node));
new->value = value;
new->next = NULL;
// list may be empty
if (l->size == 0)
{
l->start = new;
l->size = 1;
return 1;
}
new->next = l->start;
l->start = new;
l->size += 1;
return (int) l->size;
}
first_node = malloc(sizeof(struct node_lib));is a statement (an expression statement). Statements can only be inside functions. The compiler is attempting to parse it as a declaration, and that leads to the error message (in combination with the archaic behavior of defaulting a type toint). This is a duplicate of previous questions.typedefand save yourself a mountain of typing (and readers a mountain of reading.) Further, the parameter tomalloc()is "better" if it is sizeof the object you hope to point to. Eg:new_list = malloc( sizeof *new_list );This saves, even, typing another pair of parentheses and reduces the chance of creating a bug by careless future editing not changing two instances of naming the struct.