#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned length;
} List;
void init(List *l) {
l = (List *) malloc(sizeof(List));
l->length = 3;
}
int main(void) {
List *list = NULL;
init(list);
if(list != NULL) {
printf("length final %d \n", list->length);
return 0;
}
return 1;
}
This is a simplified version of the code that is giving me problems. I am trying to construct the pointer *list from a method where *list is passed as an parameter.
I know I can make void init(List *l) work by changing it to void init(List **l) but this is for a class tutorial. I can't change the method arguments. I have spent four hours working on this.
I want to ensure that there is no way to make void init(List *l) work before I confront my professor.
Thanks in advance
You're passing a copy of the pointer to init, which is allocating memory, storing it in its local copy, and promptly leaking it when init returns. You cannot pass data back to the calling function this way. You need to either return the allocated data, or pass in a pointer to the pointer you want to modify, both of which involve modifying the function signature.
void init(List **l) {
*l = (List *) malloc(sizeof(List));
(*l)->length = 3;
}
init(&list);
Did the assignment specify that you have to allocate the List from within init? If not, you could always pass a pointer to an already allocated List object, and perform whatever initialization length = 3 is a place-holder for:
void init(List *l) {
l->length = 3;
}
List list;
init(&list);
printf("length final %d \n", list.length);
The problem is that the pointer is passed by value, so you're changes are discarded. You really need a pointer to a pointer to do this correctly. As in you would do:
void init(List** l) {
*l = (List*) malloc(sizeof(List));
// ...
}
And when you call it, you would use init(&list) instead of init(list). Of course, in this case, it makes sense to just go ahead and return the result instead of using a pointer to a pointer:
List* init() {
List* result = (List *) malloc(sizeof(List));
result->length = 3;
return result;
}
And then, with the above, you could simply use list = init();.
Note that in C++, you can use references instead of pointers, but mixing references and pointers is incredibly messy. Here, using a return-type is really the neatest thing to do.
If you absolutely have to use the existing signature, you can be sneaky and initialize the list, then in the init() function you can make the passed-in list's next pointer point to the list you actually want to create. Then, after init() has been called, you can take the next pointer and dispose of the original list object you created. Or you could always just have the first element by some dummy element.
this assignment is probably a good way to teach in a class the pass by value and pass by reference. If you want to maintain the constructor's signature you need to modify the main function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned length;
} List;
void init(List *l) {
l->length = 3;
}
int main(void) {
List list;// x = NULL;
memset(&list,0,sizeof(List));
init(&list);
printf("length final %d \n", list.length);
return 1;
}
Now here list is of type List and not address to List. the init() method passed the address of list and inside init you can change the value of the structure contents.
./a.out
length final 3
init needs to be passed a pointer to an existing List. I suspect that the real problem here is with your data structure. You have something called a List, that contains a length, but there's no list to be seen anywhere. List should probably contain a pointer to an array of the given length, and init should malloc that array and set the pointer. You will probably find this out when you ask the professor to correct his requirements which aren't broken -- if they were, he probably would have heard about it from past students and corrected them by now.
Related
I have this code from a program given to me from a highly qualified instructor. Note the function pointer cmpfn below. It is used as a comparison operator. I did some reading up on function pointers and they make sense. However, nowhere in this codebase do I see it assigned to an actual defined function as I see in the tutorials I read online. It is never defined and assigned anywhere yet it appears to work. Is there some default behavior or how would you get this to work without assigning it as in cmpfn = &function_defined_elsewhere; ? Also why might he chose to use this function pointer as opposed to just a straight comparison operator?
/**
* Find an element in the list
*
* cmpfn should return 0 if the comparison to this node's data is equal.
*/
void *llist_find(struct llist *llist, void *data, int (*cmpfn)(void *, void *))
{
struct llist_node *n = llist->head;
if (n == NULL) {
return NULL;
}
while (n != NULL) {
if (cmpfn(data, n->data) == 0) {
break;
}
n = n->next;
}
if (n == NULL) {
return NULL;
}
return n->data;
}
Edit (Responding to feedback): Solved! What's below is for general public edification/curiosity but it makes sense to me now. Thanks for your indulgences.
I see now I needed to find where this function was being called. The above was in a file called llist.h and the pointer to the function (p2f) is called in a file called hashtable.c - twice actually. He actually uses p2f's a lot.
Here is one instance of a "p2f hosting" function being called:
/**
* Get from the hash table with a binary data key
*/
void *hashtable_get_bin(struct hashtable *ht, void *key, int key_size)
{
int index = ht->hashf(key, key_size, ht->size);
struct llist *llist = ht->bucket[index];
struct htent cmpent;
cmpent.key = key;
cmpent.key_size = key_size;
struct htent *n = llist_find(llist, &cmpent, htcmp); // HERE
if (n == NULL) { return NULL; }
return n->data;
}
So it's being passed htcmp. Previously I thought you had to assign a p2f to a function with the "=", but you can also do it through passing into calling function. I only learned about these today!
Here is htcmp's def:
/**
* Comparison function for hashtable entries
*/
int htcmp(void *a, void *b)
{
struct htent *entA = a, *entB = b;
int size_diff = entB->key_size - entA->key_size;
if (size_diff) {
return size_diff;
}
return memcmp(entA->key, entB->key, entA->key_size);
}
So it clearly returns an int, depending on difference between two htent (hashtable entry), noting either size difference or else calling powerful memcmp function.
This was a challenge to get to. The basics make sense but his code is very busy. I'll have to consolidate it in my sleep. There are so many structs and pointers to functions and much else interacting, but it was and is a great learning experience.
When you call the function llist_find you should pass the reference to the function, which must have the same prototype that is expected for llist_find.
And to answer your question, it's very helpful to use a pointer to function, especially when you are working with a structure where the comparison (this case) can be performed over one or multiple members of the structure. Also, as the function use void pointers, you can use the same llist_find to handle lists of different kind of structures.
You set the Pointer in This Parameter. So, when you call this Function I will automatically set these pointers automatically. So, I think you should look around the calling of this fuction. Then you will get the solution of your question
void *llist_find(struct llist *llist, void *data, int (*cmpfn)(void *, void *))
I'm trying to write a simple dictionary with an array of linked list but I keep losing data after calling the display function.
Here's my structure definition
typedef struct node{
int elem;
struct node *next;
}*L;
typedef L Dictionary[10];
And here's my display
void display(Dictionary A)
{
int i;
for(i=0;i<10;i++){
printf("A[%d]: ",i);
while(A[i]!=NULL){
printf("%d\t",A[i]->elem);
A[i] = A[i]->next;
}
printf("\n");
}
}
The solution for this is to make a temporary variable.
I tried
Dictionary tempStruct
for(i=0;i<10;i++){
tempStruct[i] = A[i];
}
and it works. But is there any other ways of assigning linked list that is more efficient than this one?
tempStruct = A;
Doesn't really work, I get incompatible types node** to Dictionary{*node[10]}
You can change the loop in your display function to this:
for(i=0;i<10;i++){
printf("A[%d]: ",i);
L tmp = A[i];
while(tmp!=NULL){
printf("%d\t",tmp->elem);
tmp = tmp->next;
}
printf("\n");
}
There's no need to copy the whole array, a simple temporary pointer navigating through the linked list is enough.
Side note: For the copy of the array, you tried to assign it with tempStruct = A;. There are two reasons this doesn't work:
Inside your function, A doesn't have an array type. C doesn't support passing an array to a function. When a function has a parameter with an array type, this is automatically adjusted to a pointer type, and instead of passing an array, a pointer to the array's first element is passed. This effect is often expressed as the array decays as a pointer, and it's the reason for your message incompatible types node** to Dictionary{*node[10]}.
Even if A had an array type, it still wouldn't work because C doesn't allow assigning to an array. This is a bit surprising because the same thing would work with a struct. I can't think of a good reason why assigning arrays is not allowed in C, you should just remember that you can't. Of course, you can do it manually, and if you don't want to assign every single element, you can use the function memcpy(), declared in string.h:
int foo[5];
int bar[5] = {1, 2, 3, 4, 5};
// instead of foo = bar;
memcpy(foo, bar, sizeof foo);
Unrelated to your question, but I had a hard time understanding this code. Your typedefs are catastrophic for readability. Never ever hide a pointer behind a typedef -- for understanding the code dealing with a pointer, it's important the pointer is obvious. A typedef for an array type is at least questionable as well. I would suggest the following code:
typedef struct node {
int elem;
struct node *next;
} node;
// not strictly necessary, but IMHO, if you want to typedef a struct type,
// it's the least confusing option to name it the same as the struct tag.
#define DICTSIZE 10
void display(node **a) // variable names are often lowercase by convention
{
// to cope with ANY possible size, you need size_t, int might be too small
// include stddef.h or stdlib.h to use it. Of course, with 10 elements,
// int is enough.
for (size_t i = 0; i < DICTSIZE; ++i) {
printf("a[%zu]: ", i);
node *tmp = a[i];
// now it's obvious tmp is a pointer, so no need to explicitly
// write the != NULL ... (any pointer that's not NULL evaluates true)
while (tmp) {
printf("%d\t", tmp->elem);
tmp = tmp->next;
}
printf("\n");
}
}
Also note how some added spaces greatly improve the readability of the code (so, use them).
I would consider your original display function broken, because it modified what it displayed. This is not expected behavior for a function that displays data. If you want to further improve on your code, you should use const to make it explicit that the function shouldn't modify what it receives, so the compiler can catch errors. In the example above, the signature for display should better look like this:
void display(const node *const *a)
The first const would make any struct node immutable, the second const (after the asterisk) makes the pointers in your array immutable. With this, you also have to write
const node *tmp = a[i];
because you can't assign a const pointer to a non-const pointer.
In your display function, you modify the entries of the dictionary array with A[i] = A[i]->next;, so you corrupt the data structure and lose the data.
You should instead use a local variable to enumerate each list:
void display(Dictionary A) {
struct node *n;
int i;
for (i = 0; i < 10; i++) {
printf("A[%d]:", i);
for (n = A[i]; n; n = n->next) {
printf(" %d", n->elem);
}
printf("\n");
}
}
Hiding pointers and arrays behind typedefs is a bad idea, it leads to confusing code for both the reader and the programmer. You should just typedef struct node node; and use explicit pointers and arrays.
I'm a C beginner experimenting with linked lists and I found a Github repo for one. the create function is written like this:
Node* ListCreate(void* data) {
Node *head = malloc(sizeof(Node));
if ( head != NULL) {
head->next = NULL;
head->data = data;
}
return head;
}
I was able to use it by changing the void* to int and then in main doing:
Node *root = ListCreate(5);
But then I read a little about void pointers and it seems like they can be used as generic types sort of like C++ templates, which would be useful if I could figure out how they work. I tried several things, and the closest I've got to getting it to work is no errors, but one warning:
incompatible integer to pointer conversion passing 'int' to parameter of type 'void *'
Am I missing a step here? I first felt like I should add something to the function definition, but I'm assuming the person who wrote it knows what they're doing and I just didn't use it properly in main. So is there a different way I'm supposed to pass an argument to this function?
As is mentioned by others in comment, it is more suitable to create different list for different types.
But if you want to use the same function definition, then use can pass the pointer to your data (int or char) and cast them as void *.
/* main: start */
int main(void)
{
Node *list_head; /* Points to first element in list */
Node *node_tmp; /* Just a temporary pointer */
int *pint;
char *pchar;
/* create an empty list */
list_head = NULL;
/* Note that there is no error checking done for
* malloc, which is not good
*/
/* Create a node which points to int */
pint = malloc(sizeof(int));
*pint = 10;
node_tmp = ListCreate((void *) pint);
/* Add this node to list */
list_head = add(node_tmp, list_head);
/* Create a node which points to char */
pchar = malloc(sizeof(char));
*pchar = 'c';
node_tmp = ListCreate((void *) pchar);
/* Add this node to list */
list_head = add(node_tmp, list_head);
/* print total number of nodes in list */
print_tot_nodes(list_head);
return 0;
}
Code for add and print_tot_nodes ommited are for brevity.
Note, that functions like print_tot_nodes or add will have not much issues if data points to different data types. But if you need to implement function like Node *smallest(Node *head), which returns pointer to the node having smallest element, then it may get complicated.
So, it is easier to use different list for different types. However you can cast the actual pointer to some datatype to void * if you need to use the same function definition as seen in your original post.
I think that there is a need to wrap for numeric literals(or cast?).
Like this:
void *BOX_VAR;//Not require if use GCC extension
#define BOX(type, value) ((*(type *)(BOX_VAR=malloc(sizeof(type))) = value), BOX_VAR)
#define UNBOX(type, value) (*(type *)(value))
Node *list = ListCreate(BOX(int, 5));
int v = UNBOX(int, list->data);
printf("%d\n", v);
I have been a sysadmin most of my life, but recently decided to practice some my dev knowledge and attempt a devops position. I have as such been practicing some C and Python skills and wrote some code for inserting a number into a linked list.
void list_insert(struct list *h, int d)
{
struct list *elem = malloc(sizeof(struct list));
elem->data = d;
elem->next = NULL;
if (!h) {
h = elem;
} else {
elem->next = h;
h = elem;
}
}
I noticed that this function doesn't seem to alter the outside view of the variable h (i.e whatever is passed to list_insert) and I noticed that printing at the end of the insert function seems to work. So having tried to look for answers online, I couldn't find anything, obvious but I found most list implementations would have double-pointers. I changed my function to use double-pointers and then it suddenly started working. Could someone help me understand what's happening here as I know pointer management is an important concept and I think I understand what a pointer is and how it relates to memory, but I don't think I get why a single pointer does not get changed, whereas a double-pointer does.
Thanks!
In C, arguments to function are passed by values. Even pointers are passed by values.
For example:
#include<malloc.h>
#include<stdio.h>
int allocatingMemory(int* ptr)
{
ptr = malloc(sizeof(int));
if(ptr==NULL)
return -1;
else
return 0;
}// We are not returning the pointer to allocated memory
int main(void)
{
int* ptr;
int allocated = allocatingMemory(ptr);
if(allocated == 0)
{
*ptr = 999;// Boom!!!
free(ptr);
}
return 0;
}
To overcome this issue, we use
int allocatingMemory(int** ptr)
{
*ptr = malloc(sizeof(int));
if(*ptr == NULL)
return -1;
else
return 0;
}
int main(void)
{
int* ptr;
int isAllocated = allocatingMemory(&ptr);
if(isAllocated == 0)
{
*ptr = 999;
free(ptr);
}
return 0;
}
If you are working with linked lists and say for example, you want to modify the head. You will pass a pointer to pointer (Note that, it is not called as double pointer) to head node.
To change memory in the caller's context, a function needs to have a pointer to that memory.
If the caller of your function has an empty list in a variable, and does an insert on that list like so:
struct list *numbers = NULL;
list_insert(numbers, 4711);
then of course inside list_insert() all we have is the NULL pointer, so we can't change the value of the variable numbers in the caller's context.
If, on the other hand, we're given a pointer to the variable, we can change the variable.
That said, it's much cleaner (in my opinion) to return the new head of the list, i.e. make the function's signature be struct list * list_insert(struct list *head, int x);.
h is actually a copy of the original pointer, so your original pointer doesnot get modified. That is why you should use a double pointer.
There are numerous questions related to that on SO. for example Using single versus double pointers in Linked lists implemented in C
I have a simple assignment that the professor wants us to do.
Basically to pull in some numbers from a text file and load into a linked list.
I don't want to get to much into the details but I have a basic question.
He provided us with a function like so:
INTLIST* init_intlist( int n )
{
INTLIST *lst;
lst = (INTLIST *)malloc(sizeof(INTLIST));
lst->datum = n;
lst->next = NULL;
return lst;
}
This function is used to initialize the linked list with the first element. Then he asked us to define a function with this signature:
int insert_intlist( INTLIST *lst, int n )
So I assume he just wants us to add to the linked list so I tried this:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp);
}
So what my thought process was is that it creates a temporary node, assigns the data value (Datum) and assigns the next pointer to point to where the current pointer is pointing at. Then I reassign the main pointer to this newly created temp node.
That way we have for instance 2 nodes:
[New Temp Node] -> [Prev Initialized Node]
When I step through the code it looks great...
Then back in main I have just a function to print the list:
while (lst!=NULL)
{
printf("The value is:%d", lst->datum);
lst=lst->next;
}
The problem is this only seems to print one digit (namely the first digit that I am reading in from the file, which I think is the last one in the list or at least I thought it was the last one in the list).
But it should keep going through as I have 10 digits in the file. I know the code is very dirty and I will clean it up...here is my entire main function if anyone needs more info:
#include <stdio.h>
#include <stdlib.h>
#include "intlist.h"
int main(int argc, char *argv[])
{
char c; /* Character read from the file. */
FILE* ptr; /* Pointer to the file. FILE is a
structure defined in <stdio.h> */
int index=0;
//INTLIST* aList[10]; //will use later
/* Open the file - no error checking done */
ptr = fopen("1.txt","r");
/* Read one character at a time, checking
for the End of File. EOF is defined
in <stdio.h> as -1 */
if(ptr==NULL) {
printf("Error: can't open file.\n");
/* fclose(file); DON'T PASS A NULL POINTER TO fclose !! */
return 1;
}
//aList[index] = malloc(sizeof(INTLIST)); WE NEED THIS LATER ON....
INTLIST *lst=NULL;
while ((c = fgetc(ptr)) != EOF)
{
if (c != ' ')
{
//make sure it isnt a space
int i = c - '0'; //get the value from the text file
if(c=='\n')
{
// aList[index]=lst;
// index++;
// aList[index] = malloc(sizeof(INTLIST));
while (lst!=NULL)
{
printf("The value is:%d", lst->datum);
lst=lst->next;
}
free(lst);
free(aList[index]);
return 0;
//new line in the file
//create another linked list
}
if (lst==NULL)
lst = init_intlist(i);
else
insert_intlist( lst, i);
}
}
fclose(ptr);
system("PAUSE");
return 0;
}
Here is intlist.h for anyone who may need it:
#ifndef __intlist_h__
#define __intlist_h__
/* each entry in the list contains an int */
typedef struct intlist {
int datum;
struct intlist *next;
} INTLIST;
INTLIST *init_intlist( int n ); /* initializes the intlist with initial datum n */
int insert_intlist( INTLIST *lst, int n ); /* Inserts an int (n) into an intlist from the beginning*/
void list_append(INTLIST *list, void *datum); /* Inserts entry to the end of the list */
INTLIST* list_front(INTLIST *list); /*return the element at the front of the list, and remove it
from the list*/
void list_map( INTLIST *list, void (*f)(void *) ); /*Applies a function to each element of the list */
void list_delete( INTLIST *list ); /* Deletes (and frees) all entries in the list */
#endif
A couple of issues here.
I'll start with a BAD bug:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp); // <<<<< NO!
}
You are still using that memory, so you can't free it.
Secondly, the proto-type supplied to you for insertion has no way to return a new front of the list, so you can not change the front of the list. This implies that you must add new nodes to the back, rather than to the front as you have done.
Also, the supplied return type of int probably means that he expects out the number of nodes in the list, which is no problem as you're going to have to walk the list to find the back anyway.
Have another go at it, you're not doing badly at all.
Working with code like:
int insert_intlist( INTLIST *lst, int n )
{
INTLIST* lstTemp;
lstTemp = (INTLIST *)malloc(sizeof(INTLIST));
lstTemp->datum = n;
lstTemp->next = lst;
lst = lstTemp;
free(lstTemp);
}
This has a couple of problems. First of all, the free(lstTemp) seems to be freeing the node that you just inserted in the list, which you probably don't want to do.
Second, you're passing the pointer to the list into the function -- which means the function can't modify that pointer, so when you assign to the pointer, you're only changing your local copy of it.
You have two choices: you can either pass in a pointer to that pointer (so you can modify the original pointer) or you can get clever and figure out a way to avoid needing to (but I won't give away the secret right away...)
This line:
lst = lstTemp;
Only changes the value of lst inside the function. It won't propagate back to the copy of the pointer that the caller has.
You can either use a pointer-to-a-pointer, or if you can't change the function signature, insert somewhere other than the head of the list.
Though the typical way of handling this is to not point to the first element in the list - rather, you have some sort of list structure that holds a pointer to the first element, and some other information about the list (say, how many elements it has). Then you pass a pointer to that structure around.
In C, parameters are passed to functions "by value", meaning they are copied when you enter the function and any changes you make to them are not reflected back to the caller. That means that when you modify lst to point to your newly allocated memory it doesn't actually modify the caller's pointer to the list.
EDIT: As dmckee pointed out, you shouldn't free the memory in your insert function as you are still using it. That's definitely a bug, but it's not the one causing your problem.
In C, everything is passed by value. If you want a function to change something, you need to pass its address to the function. Since in int insert_intlist( INTLIST *lst, int n ), you want to change the list head, you need to pass a pointer to it, i.e., the first parameter should be INTLIST **lst (see below too, though). But the function prototype is given and cannot be changed.
What that means is that you can't add a number to the beginning of the list—the caller can't know that you did so. So, you have to traverse the list pointed to by lst, and then add the new node anywhere down the chain. The professor probably wants you to add the node at the end, but he might have asked for some other condition.
With that information, let's look at the comments for the prototypes:
/* Inserts an int (n) into an intlist from the beginning*/
int insert_intlist( INTLIST *lst, int n );
The comment or the prototype is wrong. If your professor has given you this file, insert_intlist() cannot be written to satisfy the comment, since it can't return to the caller the new head. The prototype should be either:
/* Inserts an int (n) into an intlist from the beginning
and returns the new head */
INTLIST *insert_intlist( INTLIST *lst, int n );
Or:
/* Inserts an int (n) into an intlist from the beginning */
int insert_intlist( INTLIST **lst, int n );
(Note the **.)
The header also has:
/*return the element at the front of the list, and remove it from the list*/
INTLIST* list_front(INTLIST *list);
This is correct. Note that you need to modify the list's head in list_front(), so you're returning the new head.
Finally, you don't want to free() anything in insert_intlist(). You want to keep the new node in the list, don't you? Once the caller is done with the list, he will have to call list_delete(), which will traverse the linked list, and free each node.
I Agree with Alok. I Have the same problem/ Professor. I am new to C programming and I have been looking all over the web for forms and C webpages for help. I have came across a source that supports Alok.
I used
INTLIST *list_add(INTLIST **p, int i){
INTLIST *n;
n = (INTLIST *) malloc(sizeof(INTLIST));
if (n == NULL)
return NULL;
n->next = *p; /* the previous element (*p) now becomes the "next" element */
*p = n; /* add new empty element to the front (head) of the list */
n->datum = i;
return p; }
From my main I can pass in
INTLIST *list
list_add(&list, 1);
list_add(&list, 2);
so when i print the list it prints 2 1
The professor suggested this:
INTLIST *mylist[N];
Where N is the number of rows of your
input file. Then mylist[i] is a
pointer to the ith linked list.
Okay Fine: create for testing purposes INTLIST *mylist[2];
I call the same functions:
list_add(&list[0], 1);
list_add(&list[0], 2);
This prints out 2 1 ... Great,
But when I do this:
list_add(&list[1], 3);
list_add(&list[1], 4);
I get a Segmentation fault..