I have three files: link_list_test.c
, which has main() function. test_lib.h
, and test-lib.c
I defined struct pointer in test_lib.h, and uses the same struct pointers in test_lib.c. However, it is not letting me compile.
The complain I get is
# test_lib.c, on this line `first_node = malloc(sizeof(struct node_lib));`
conflicting types for 'first_node'; have 'int'
# test_lib.c, struct node_lib *first_node;
previous declaration of 'first_node' with type 'struct node_lib *'
# test_lib.c, if (first_node == NULL) {
expected identifier or '(' before 'if'
# test_lib.c, line first_node->value = 0;
expected '=', ',', ';', 'asm' or '__attribute__' before '->' token
# test_lib.c, first_node = malloc(sizeof(struct node_lib));
data definition has no type or storage class
# test_lib.c, first_node = malloc(sizeof(struct node_lib));
type defaults to 'int' in declaration of 'first_node' [-Wimplicit-int]
What does this mean?
I use VScode and GDB.
Content of test_lib.h
:
#ifndef TEST_LIB_H
#define TEST_LIB_H
#include <stdio.h>
struct node_lib {
int value;
struct node_lib *next;
};
extern struct node_lib *first_node;
extern struct node_lib *current_node;
#endif
Content of test_lib.c
#include "test_lib.h"
#include <stdlib.h>
#include <stdio.h>
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;
// every time calling create_new_node(new_value),
// expect first_node to append a new node with new_value.
void create_new_node(int value) {
struct node_lib *new_list, *p;
new_list = malloc(sizeof(struct node_lib));
if (new_list == NULL) {
printf("Unable to allocate memory for new node_lib\n");
exit(EXIT_FAILURE);
}
new_list->value = value;
new_list->next = NULL;
p = first_node;
while (p->next != NULL) {
p = p->next;
}
p->next = new_list;
printf("%n\n", value);
}
Content of link_list_test.c
#include "test_lib.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
create_new_node(1);
create_new_node(2);
create_new_node(3);
return 0;
}
CodePudding user response:
You cannot have code outside a function. Combined your 3 files into 1 as it's just easier to talk about. create_new_node()
becomes a little more compact if you use a pointer to pointer (n
) to where you want to insert a new node:
#include <stdio.h>
#include <stdlib.h>
struct node_lib {
int value;
struct node_lib *next;
};
struct node_lib *first_node = NULL;
void create_new_node(int value) {
struct node_lib **n;
for(n = &first_node; (*n); n = &(*n)->next);
*n = malloc(sizeof(**n));
if(!*n) {
printf("Unable to allocate memory for new node_lib\n");
exit(EXIT_FAILURE);
}
(*n)->value = value;
(*n)->next = NULL;
}
void print_nodes() {
for(struct node_lib *n = first_node; n; n = n->next) {
printf("value=%d\n", n->value);
}
}
int main() {
create_new_node(1);
create_new_node(2);
create_new_node(3);
print_nodes();
return 0;
}
and it prints:
1
2
3
CodePudding user response:
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 ofmain
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(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 msg
is 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;
}