Hide type's nature - c

To learn linked lists' (doubly-linked) conception I'm writing a bunch of simple common functions to handle them. Most of the functions take pointer to the DL_List
structure, which is the handle and contains not only links to the first and last elements but also current position in the list.
typedef struct DL_Node {
struct DL_Node *next = NULL;
struct DL_Node *prev = NULL;
int data;
} DL_Node;
typedef struct DL_List {
struct DL_Node *first = NULL;
struct DL_Node *last = NULL;
struct DL_Node *cur = NULL;
} DL_List;
Therefore I always have to pass a pointer to the handle.
int main() {...
DL_List list; // lives on the stack
init_list(&list, 9);
push(&list, 7);
append(&list, 10);
insert_after(&list, -31);
print_list(&list);
...}
So here is the question: is there any way to avoid rereading passing &list? Maybe typedefing?
github repo

You can do
int main() {...
DL_List list; // lives on the stack
DL_List * plist = &list;
init_list(plist, 9);
push(plist, 7);
append(plist, 10);
insert_after(plist, -31);
print_list(plist);
...}
if you want to avoid typing &, but it doesn't really save you something.
A good compiler should optimize this away.

Short of using global variables or horrendous macros, no. You explicitly pass as arguments the objects you want a function to operate on.

Related

Why is a pointer to a structure not overridable in a function? [duplicate]

This question already has answers here:
Changing address contained by pointer using function
(5 answers)
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 2 years ago.
lets look at this code:
typedef struct nodes{
int val;
struct nodes next;
} node;
insertHead(node *list, int val) {
node *temp = malloc(sizeof(node));
temp->val;
temp->next = list;
list = temp;
}
int main() {
node *list = NULL;
insert(list, 5);
}
Here if I try to call the list->val it does not work as if it passes in the copy of the list and did not actually set the new head of the list.
typedef struct nodes{
int val;
struct nodes next;
} node;
insertHead(node **list, int val) {
node *temp = malloc(sizeof(node));
temp->val;
temp->next = *list;
*list = temp;
}
int main() {
node *list = NULL;
insert(&list, 5);
}
I know that this will work, but my actual question is why doesn't the first example work. I mean I pass in the list into the function and list is a pointer to a structure. I'm new to C but isn't it correct to pass in the address of a structure to change its members and maybe override it. I totally understand that this would not work if I would pass in the struct itself with *list because it would just create a copy.
but the node *list isn't a copy, it is a pointer to the actual structure, so why do I need to do this with a pointer-to-pointer-to-structure?
P.S: I'm new to C so don't judge too hard. This will be a linked list btw.
Because you want to modify the pointer itself, not the referenced value. To modify it you need to pass pointer to this pointer
Similar to "normal" types. If you pass the integer, you cant modify it in the function. To archive that you need to pass pointer.

Implementing a simple linked list in C without malloc

All the implementations I have seen online use pointer to declare nodes and then will use malloc to create space for them like this:
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
head = (struct Node*)malloc(sizeof(struct Node));
second = (struct Node*)malloc(sizeof(struct Node));
third = (struct Node*)malloc(sizeof(struct Node));
...
But I can also create the same without pointers and malloc like this:
struct node {
int id;
struct node* next;
};
struct node head = {0, NULL};
struct node one = {1, NULL};
struct node two = {2, NULL};
struct node tail = {3, NULL};
int main(){
head.next = &one;
one.next = &two;
two.next = &tail;
...
My question is, why the 1st method is mostly the one used, why do we need to declare each node as pointer and why do we need malloc?
(Just to point out I know why struct Node *next; is declared as pointer int the struct declaration).
You should do this with local variables, not global ones, but the general idea would be the same. You should also steer towards having arrays and not heaps of otherwise unrelated variables:
struct node {
int id;
struct node* next;
};
int main(){
struct node nodes[4];
for (int i = 0; i < 4; ++i) {
nodes[i].id = (3 - i);
if (i != 0) {
nodes[i].next = &nodes[i-1];
}
}
return 0;
}
Where something like that assembles them in reverse order for convenience, but they're all grouped together in terms of memory initially.
malloc is used because you often don't know how many you're going to have, or they get added and removed unpredictably. A general-purpose solution would allocate them as necessary. A specialized implementation might allocate them as a single block, but that's highly situational.
Here the lifespan of nodes is within that function alone, so as soon as that function ends the data goes away. In other words:
struct node* allocateNodes() {
struct node nodes[10];
return &nodes; // Returns a pointer to an out-of-scope variable, undefined behaviour
}
That won't work. You need a longer lived allocation which is precisely what malloc provides:
struct node* allocateNodes() {
struct node *nodes = calloc(10, sizeof(struct node));
return nodes; // Returns a pointer to an allocated structure, which is fine
}
The problem is if you malloc you are responsible for calling free to release the memory, so it becomes more work.
You'll see both styles used in code depending on the required lifespan of the variables in question.
If you know ahead of time exactly how many items will be in your list, then you're probably better off using an array rather than a list. The whole point of a linked list is to be able to grow to an unknown size at runtime, and that requires dynamic memory allocation (i.e., malloc).

Modified structs allocated in memory do not retain values after end of function

I have a couple of structs: A HashTable, which contains a table of pointers to WordNodes, and each WordNode contains a pointer to a List, which is a linked list made up of ListNodes.
I wrote a function to create a list and add list nodes to a WordNode:
int addWord(char* word, HashTable* hash_table, int id)
{
WordNode* current = calloc(1, sizeof(WordNode));
current = hash_table->table[hash];
// ...
if(current->docs == NULL){
// Create a new list, and initialize it
List* list = calloc(1, sizeof(List));
list->head = NULL;
list->tail = NULL;
int occur = 1;
ListNode* list_node = AddNode(list); // Create the first node in the list
current->docs = list; // Link the WordNode to the list
// Fill in relevant details of ListNode
list_node->id= &id;
list_node->occurrences = &occur;
list_node->next = NULL;
That is my function, but since it's been giving me trouble, I added a couple of lines inside it to test it:
printf("Testing:\n");
WordNode* wnode = calloc(1, sizeof(WordNode));
wnode = hash_table->table[hash];
List* my_list = calloc(1, sizeof(List));
my_list = wnode->docs;
ListNode* dnode = calloc(1, sizeof(ListNode));
dnode = my_list->head;
printf("Results: ocurrences: %d, id: %d\n",*((int*)dnode->occurrences),
*((int*)dnode->id));
printf("The dnode is %d\n", doc_node);
}
When called in main, the testing code inside the function produces the expected output:
Results: ocurrences: 1, id: 15
The dnode is 13867424
However, the same testing in the line immediately following the function call in main produces a weird output, even though the pointer seems to be pointing to the same address.
Results: ocurrences: 0, id: 54
The dnode is 13867424
Possibly relevant code from the function that adds a new node to the list:
ListNode* AddNode(List * list)
{
ListNode* node = calloc(1, sizeof(ListNode));
node->next = NULL;
if(list->tail == NULL){
list->head = node;
list->tail = node;
}
else{
list->tail->next = node;
list->tail = node;
}
return node;
}
I can't seem to figure out what I am doing wrong. It would seem to me that I am somehow handling the structs as local variables, even though I am allocating memory for them, which makes me think they shouldn't change after the function is done. It is probably a C-programmer's beginner mistake, but I can't seem to figure out where I am getting this wrong. Any help would be greatly appreciated.
One set of problems is in the code:
int addWord(char* word, HashTable* hash_table, int id)
{
…omitted…
int occur = 1;
ListNode* list_node = AddNode(list); // Create the first node in the list
current->docs = list; // Link the WordNode to the list
// Fill in relevant details of ListNode
list_node->id= &id;
list_node->occurrences = &occur;
You're storing a pointer to a parameter and a pointer to a local variable in your structure. Dereferencing either of those after the function returns is undefined behaviour. The space occupied by those could be reused by the compiler for any purpose at any time; they could become completely invalid (but probably won't).
Why do you have pointers in your structure for those two items? Surely, the structure should just contain a couple of int members, not int * members!
If, perchance, your code is compiling with warnings, don't submit it to SO; fix the warnings first. Or seek help on how to resolve the compiler warnings. They all matter. At this stage in your career, remember that the compiler knows a lot more about C than you do. If it warns about something in your code, the compiler is probably correct to be worried and the code is probably incorrect in some way.
Your code doesn't show where word is used — it could be that you are not copying that data either.

type-problems in C

This may be a stupid question, and I see similar questions been asked, but I dont get the answers given. why does the following code produce:
error: incompatible types when assigning to type ‘node_t’ from type ‘struct node_t *’
node_t list_array[10];
typedef struct node
{
int value;
struct node *next;
struct node *prev;
} node_t;
node_t* create_node(void)
{
node_t *np;
np->next = NULL;
np->prev = NULL;
np->value = rand() % 10;
return np;
}
int main(void)
{
int i;
for(i = 0; i < 10; i++)
{
list_array[i] = create_node();
}
return 0;
}
Make the array into an array of pointers to fix the error, since create_node returns a pointer:
node_t *list_array[10];
Note you're not allocating any memory in create_node so using np is illegal. Try:
node_t *np = malloc(sizeof *np);
I want to make an array of node_t structs
In that case you could leave the node_t list_array[10] and:
Pass &list_array[i] as an argument to the function
Have the function return a node_t instead of a node_t *
Because one is a structure and the other is a pointer to the structure.
The create_node() function returns a pointer to a node (which you really should malloc() in that function, by the way) and you try to assign it to an actual structure in the array.
You can solve it by simply changing your declaration to:
node_t *list_array[10];
so that it's an array of pointers rather than an array of structures.
Because create_node() returns a pointer, but list_array[i] is an actual instance. You can't assign a pointer over an instance, they're completely different.
The solution is typically to represent each node as a pointer, which requires list_array to be an array of pointers:
node_t *list_array[10];
Then the assigment makes sense, and the code will compile.
Note, however, that the code will not "work", since it's dereferencing a NULL pointer inside create_node(). It seems you forgot to call malloc():
node_t* create_node(void)
{
node_t *np;
if((np = malloc(sizeof *np)) != NULL)
{
np->next = NULL;
np->prev = NULL;
np->value = rand() % 10;
}
return np;
}
This is the classic "pointer vs. instance" confusion. Even more serious than your warning is:
node_t *np;
np->next = NULL;
which will compile, and then segfault.
The confusion arises because of a misunderstanding of what a pointer is. When compiled, a pointer is just a single number, like 140734799803888. This number is used just to locate the physical chunk of data. It's a memory address.
Pointers vs. instances are confusing, and one of the first conceptual challenges you encounter in programming. So here's an analogy:
If you've ever used a GPS, it will tell you where you are (pointer) but not what you are (data). Pointers work the same way. If someone wants to shake your hand, they wouldn't shake GPS coordinates (pointer)! They'd use the GPS coordinates to locate you, then physically visit you (data) and shake your hand. That's how pointers work.
So in your code above, you declare a pointer np, but don't give it any location to keep track of. Then, you ask "use the number in np to locate my data" (but you haven't set a number for np!) In particular, np->next asks to use the location np + someOffset (which is undefined!) to find your physical data (which is nowhere), and change it.
That's why you get a seg fault.
node_t list_array[10] should be node_t *list_array[10]
You have also, not malloced your node_t *np
node_t *np = malloc(sizeof(node_t));
For this program, I see no point to using dynamic storage duration (malloc). I would dispose of create_node in favour of memcpy, if you wish to keep all of your objects in static storage duration. For example,
#include <string.h>
typedef struct node
{
int value;
struct node *next;
struct node *prev;
} node_t;
int main(void) {
node_t list_array[10];
for (int i = 0; i < sizeof (list_array) / sizeof (*list_array); i++) {
memcpy(list_array + i,
&(node_t){ .value = rand() % 10,
.next = NULL,
.prev = NULL },
sizeof (*list_array));
}
return 0;
}

I'm getting the error: petition of a member in something that it's not an structure or an enum in C

Basically what I'm trying to do is to make a generic list in which I could put any data type inside the node by using a void* to it. This is the generic list and the structure I want to put inside of it.
typedef struct LIST{
struct LIST *next;
void *data;
} LIST;
struct person {
char *name;
int age;
};
This is the function to assign the pointer
LIST *create_node(void *data, int size) {
LIST *tmp;
tmp = NULL;
tmp = (LIST*)malloc(sizeof(LIST));
tmp->data = malloc(size);
tmp->data = data;
memcpy(tmp->data, data, sizeof(data));
tmp->next = NULL;
return tmp;
}
This is the function where I call the create_node function
void test_list() {
LIST *my_list;
struct person *my_person;
my_list = NULL;
my_person = (struct person*)malloc(sizeof(struct person));
strcpy(my_person->name, "PABLO");
my_person->age = 23;
my_list = create_node(my_person, sizeof(struct person));
printf("%d \n", my_list->data->age);
}
The problem comes in the last line in which I get the error: "petition of the member age in something that is not an structure or an enum". How can I get rid of this problem?
You've declared data as a void *. If you know for sure that it's really pointing at a person, then you'll need to cast:
((struct person *)my_list->data)->age
There are a couple of things I would do slightly differently, but the specific error here is in this expression:
my_list->data->age
remember from your declaration of the LIST type that the type of my_list->data is void *. You know that this particular void * actually points to a struct person -- but the compiler does not, and cannot know this, since these two functions might be in different files compiled at different times.
So, you need to explicitly tell the compiler that this void * should be treated as a struct person * using a type cast:
((struct person *)my_list->data)->age

Resources