In this piece of code I'm trying to create a list which contains all the chars form an input file, and my main problem is with the sentence "You can't return a local variable of the function" Iv'e been told which made me very confused. I dynamically allocated a List and return it, can I just define List list without a dynamic allocation and return it? I believe it's wring since all the information would be automatically deleted and I would be left only with the address of the original list I created.
Here's the code for more information:
typedef struct Item {
char tav;
struct Item* next;
} Item;
typedef struct List {
Item* head;
} List;
List* create(char* path) {
FILE* file;
List* list;
Item* trav;
Item* curr;
char c;
file=fopen(path, "r");
if (file==NULL) {
printf("The file's not found");
assert(0);
}
if (fscanf(file, "%c", &c)!=1) {
printf("The file is empty");
assert(0);
}
trav=(Item *)calloc(1, sizeof(Item));
trav->tav=c;
list=(List *)calloc(1, sizeof(List)); /* allocating dynamiclly the list so it won't be lost at the end of the function*/
list->head=trav;
while (fscanf(file, "%c", &c)==1) {
curr=(Item*)calloc(1, sizeof(Item));
curr->tav=c;
trav->next=curr;
trav=curr;
}
trav->next=NULL;
fclose(file);
return list;
}
Am I correct? Is this necessary? can I define List instead a pointer to one a return it?
You cannot return a pointer to variable local to the function. The local variable does not live beyond the scope({,}) of the function.
Returning address of a variable local to a function will give you what is called as an Undefined Behavior.
You can very well return:
a local variable by value or
a pointer pointing to dynamically allocated memory
Prefer the first unless you are really bothered about the memory overhaed due to returning a copy.
can I just define List list without a dynamic allocation and return it?
Yes,
Provided you have:
List create(char* path);
You can't return a local variable of the function
This sentence is completely wrong. For example in this function:
int f(void)
{
int x = 5;
return x;
}
is a perfectly valid function in which you are returning a local variable.
What you should know is that you can't (or better say shouldn't) return the address of a local variable of the function. This is because after the function returns the address points to garbage and is not usable anymore.
In your example, you can very safely define a local List and return it.
Note that you still need to dynamically allocate trav, i.e. you can't take a local variable of type Item and the point list->head to it for the same reason above.
Related
I am pretty new to C and I was wondering if the order of elements in a struct matter.
I have the following struct:
struct list
{
struct list_el *head;
int size;
}
I use this to make a linked list. The head points to the first element and the size shows the amount of elements in the list.
I have also have the following function to initialize the list.
typedef struct list list;
list* list_init()
{
list *list = malloc(sizeof(list));
if(list)
{
list->head = NULL;
list->size = 0;
return list;
}
return NULL;
}
The program compiles fine, without any errors, warnings or notes, but when I run the program using valgrind it says I have an invalid write of size 4 on the line in the list_init() function where I assign 0 to list->size. I have the same invalid read/write every time I access the size variable. I have no idea why. Also when I switch the two struct elements around (declare size first and then head) I get the invalid write on the line where I assign NULL to head and then the size variable is used just fine. Can anybody explain me why this happens and how I can fix it?
Last note: the struct as it is shown here is defined in an header file while the function is in the C file. Not sure if this is important.
You named the variable with the same name as the type, so sizeof(list) is implemented as the size of the variable (pointer), not the size of the struct. The struct has a pointer and a integer, so the size will be larger than a pointer, so access to not allocated place occurred.
Rename the variable.
example:
list *list_init(void)
{
list *list_data = malloc(sizeof(list));
if (list_data)
{
list_data->head = NULL;
list_data->size = 0;
return list_data;
}
return NULL;
}
In legacy C code I have one pointer basically an array of size equal to one of enumerator and it is static in local scope. But now I have to remove that enumeration and now this static local array is giving error. I can convert that array to normal pointer and then allocate it dynamically but I am not sure how to do that. Below is sample code I have simplified from existing code base.
enum
{
E1,
E2,
EOL
};
void func
{
//static int array[EOL]; //-> that how it was earlier
static int *array = (int*)malloc(sizeof(int)*EOL); //Will this allocated memory only once
//or on every invokation.
free(array);//will it free the memory more than once?
}
Now I can move array pointer to global scope and then allocate it in main and free it in atexit functions but I want to keep changes minimum as I am not sure of impact it will have in shared projects?
Thanks
The malloc will occure only once.
1) You can use a static boolean to let you know if the pointer in the array variable can be free.
2) You can free the pointer then set it to NULL. The next occuration of the free will do nothing.
If you want to keep changes minimal then simply move the enumeration definition inside the function body before this static variable. Or you can use even an unnamed enum with one enumerator for the size of the array.
I do not understand your attempts to substitute the array for a dynamically allocated array.
Moreover at present C allows to use variable length arrays. So your could define the function such a way that it had a parameter that would specify the size of the local (non-static) array.
You can't initialise a static variable with something non const if you are using C.
If you are using C++, then the static pointer will only get a memory pointer allocated to it once.
I just solved the problem by using one function which basically is collects all memory allocated to local static pointers like above and then other functions free them during the end as it is registered using atexit function.
struct node
{
node *next;
void *content;
};
node* head = NULL, tail =NULL;
void addToList(void* ptr)
{
struct node* p = (struct node*)malloc(sizeof(struct node));
p->next = NULL;
p->conent = ptr;
tail->next = p;
tail = p;
return;
}
void freeList()
{
struct node* p = NULL, p1 = NULL;
for(p = head; p != NULL; p = p->next)
{
free(p1);
free(p->content);
p1 = p;
}
head = tail = NULL;
return;
}
/*
*/
void func
{
//static int array[EOL]; //-> that how it was earlier
static int *array = (int*)malloc(sizeof(int)*EOL); //Will this allocated memory only once
addToList(array); //or on every invokation.
free(array);//will it free the memory more than once?
}
As you can see it above code a linked list is created in separate .c file and using .map method head,tail and node will not be exposed to outside world only addToList and freeList will be visible. In every places just after doing a malloc I am calling addToList and then freeList will free up the memory.
Thanks
I've been trying to learn C by myself after basic Java knowledge and I'm trying to implement a linked list. I'm running into trouble when making a createList function - a function that's supposed to make an empty list. I'm having a bit of a weird problem with passing pointers though. These are just snippets of the code, and my definitions of the node / list are located in a header file given by a book CD (so that's not the problem)
LList* createList(void){
LList list;
list.first = NULL;
printf("List from mk_list: \nfirst:%d\nlist address:%d\n",list.first,&list);
return &list;
} //This prints 0 and then the address of list
void print_list(LList *list){
printf("\nList from print_List\nFirst%d\nAddress:%d\n", list->first,list);
} //This prints some random non-null address for some reason, but also the
//SAME address as the list from createList. Why does list->first not print as 0?
int main(void)
{
LList*newList = createList();
printf("\nList from main:\nFirst:%d\nAddress:%d\n", newList->first,newList);
print_list(newList);
system("pause");
return 0;
} //Prints same exact thing as the printf in the createList.
return &list;
list resides on stack and its lifetime completes as function returns. So, using a pointer to it invokes undefined behavior.
Create the object on heap.
Here is your problem:
LList* createList(void)
{
LList list;
...
return &list;
}
Variable list is allocated on the stack, whenever you enter the code of function createList.
Whenever this function (or any other function for that matter) is called, the stack is in a different status.
Sometimes it's 75% full, sometimes only 24% (this is just a figure of speech though).
Your local variable is allocated at the next free slot in the stack, so it doesn't have a constant address throughout the lifetime of your program, but only during the execution of the function itself.
Shorty after the function returns, some other local variable might be allocated in the same address.
Hence, there is no point in returning this address, as you are doing in your code.
UPDATE:
One optional solution for you is to declare LList newList in main, and pass &newList to createList instead of returning a value from that function:
void createList(LList* list)
{
list->first = NULL;
printf("List from mk_list: \nfirst:%d\nlist address:0x%X\n",list->first,list);
}
P.S.: You should probably name this function initList instead.
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
#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.