I am currently using a list in structs that looks like this:
This is a function in which removes elements from a list. I start with a for loop to go through the entire list. If i is less than the number of entries it enters an if statement. Then it the old position into a hold ptr. Makes the old on = to NULL and then moves the list so that the elements below take its spot.
Heres a sample list when I call this function:
100
125
150
When I do this and I want to remove 150 from the list it goes through but looses access to memory in the list -> wlist_ptr[i] -> eth_address. I then get a set fault. Is there any way around loosing track?
There's a big mistake in the loop.
Only one element is "moved up", and it's moved up after it's set to null.
So, this
list -> wlist_ptr[i] = NULL;
list->wlist_ptr[i-1] = list->wlist_ptr[i];
Needs to change to this to prevent moving the NULL up:
list->wlist_ptr[i-1] = list->wlist_ptr[i];
list -> wlist_ptr[i] = NULL;
But then, a loop is needed to iterate through the remainder of the list to move them up as well. memmove is your friend for this. Keep in mind too, that when that's done, you don't want to increase i for the next iteration, because the next element in the list will now be at the original i location.
Perhaps this will do the job:
struct wifi_info_t *wifilist_remove(struct wifilist_t * list, int user_address)
{
int i;
int count;
struct wifi_info_t *ptr;
ptr = NULL;
count = wifilist_number_entries(list);
// TODO: take out the ( ptr == NULL ) logic if more than one match needs to be
// removed.
for(i=0; ( i < count ) && ( ptr == NULL ); i++)
{
if(list -> wlist_ptr[i] -> eth_address == user_address)
{
ptr = list -> wlist_ptr[i];
if ( i < ( count - 1 ) )
memmove(&(wlist_ptr[i]), &(wlist_ptr[i + 1]), (count - (i + 1)) * sizeof(wlist_ptr[0]));
// TODO: decrement the length of the list returned by
// wifilist_number_entries(list)
}
}
if(ptr != NULL)
{
list->wlist_entries--;
}
return ptr;
}
Note that I just typed this here, so it may have syntax errors or the like.
Some example:
struct wifi_info_t *wifilist_remove(struct wifilist_t * list, int user_address)
{
int i;
struct wifi_info_t *ptr;
ptr = NULL;
for(i=0; i < wifilist_number_entries(list); i++)
{
if(list -> wlist_ptr[i] -> eth_address == user_address)
{
ptr = list -> wlist_ptr[i];
if(i != (wifilist_number_entries(list) -1))
{
//replace it with last element
list -> wlist_ptr[i] = list -> wlist_ptr[wifilist_number_entries(list)-1];
list -> wlist_ptr[wifilist_number_entries(list)-1] = ptr;
}
//you can use free and realloc there if you want
list->wlist_entries--;
}
}
//why? don't do that
return ptr;
}
Related
I have the task of going through a binary search tree and collecting the keys in every node and storing them in an array. After this, I have to set the last element of this array as NULL. This is all done inside a function, which then returns this array of *char.
I believe the part about collecting the keys and storing them in the array is working well, as I've tested that enough. However, I'm not able to define the last element of the array as NULL correctly. I thought it should be done like this:
list_keys[tree_size(tree)] = NULL;
However, after some printf's, I realized that this was cleaning my tree. When I print tree_size(tree) before this line, it gives me the size of the tree correctly. However, when I do it after that line, it gives me 0. I don't believe that the problem is in the function tree_size() because when I attempt to access an element of the tree before that line, it works well, but after the line, I get a Segmentation fault (core dumped) error, probably due to trying access something that doesn't exist anymore.
I have no idea what's wrong, so any help is appreciated. Thanks in advance.
EDIT:
The tree is of the type tree_t and it's defined as:
struct tree_t {
struct entry_t *entry;
struct tree_t *right_child;
struct tree_t *left_child;
};
The tree_size() is defined as:
int tree_size(struct tree_t *tree) {
if (tree->entry != NULL) {
//if two children exist
if (tree->left_child != NULL && tree->right_child != NULL) {
return 1 + tree_size(tree->left_child) + tree_size(tree->right_child);
}
//there's a right child and there isnt a left child
if (tree->right_child != NULL && tree->left_child == NULL) {
return 1 + tree_size(tree->right_child);
}
//there's a left child and there isnt a right child
if (tree->left_child != NULL && tree->right_child == NULL) {
return 1 + tree_size(tree->left_child);
}
//there are no children
if (tree->left_child == NULL && tree->right_child == NULL) {
return 1;
}
}
//if the entry is empty
return 0;
}
Basically, what I'm doing right now is:
First, I define list_keys and allocate memory:
char **list_keys;
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
Then, I call an auxiliary function tree_get_keys_aux(tree, list_keys, 0) that will do the initial part I mentioned. It is defined as:
void tree_get_keys_aux(struct tree_t *tree, char **list_keys, int index) {
//N
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
index = index + 1;
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
return;
}
Then, I do the line that's bringing me problems,
list_keys[tree_size(tree)] = NULL;
And lastly,
return list_keys;
Firstly,
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
is wrong. The elements are char*, so you have to allocate for that or you will cause out-of-range access.
It should be:
list_keys = malloc((tree_size(tree)+1)*sizeof(char*));
or
list_keys = malloc((tree_size(tree)+1)*sizeof(*list_keys));
See also: c - Do I cast the result of malloc? - Stack Overflow
Secondly,
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
is wrong. You have to allocate one more element for terminating null-character.
It should be:
list_keys[index] = malloc((strlen(tree->entry->key) + 1)*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
Thirdly, tree_get_keys_aux is not recoginizing number of elements in left child and data from left child will be overwritten by data from right child.
To avoid this overwriting, you can use tree_size to determint the tree size and advance index according to that.
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
index += tree_size(tree->left_child); // add this
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
I repeat this function 2500 times in a loop for different parameters and it takes 85 seconds. What is wrong with this function? How can I improve running time? Thanks for your help.
Function:
int findBaconNumber(v * actors[], int actorCount, int index, v * visited[]){
// Bacon number is 0:
if(strcmp(actors[index]->name, "Bacon,Kevin") == 0)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
memset(visited, NULL, sizeof(visited));
q * queue = createQueue();
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int visitCount = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[visitCount] = tmp;
visitCount++;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(strcmp(tmp->name, "Bacon, Kevin") != 0 && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
if(contains(visited,visitCount+1,actors[tmp2->actorNo]) != 1){
enqueue(queue, actors[tmp2->actorNo]);
visited[visitCount] = actors[tmp2->actorNo];
visitCount++;
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(strcmp(tmp->name, "Bacon, Kevin") != 0)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}
For Loop:
for(i=0; i<actorCount; i++){
baconNumbers[i] = findBaconNumber(actors, actorCount, i, visited);
}
EDIT:
Thanks all of you for answers. This is my school project and it must be solved by breadth first search algorithm so I can not use other solutions. I profiled the code it looks like the problem is with the contains function. I use it to check if node is visited or not. Now, I am working on finding another solution for this.
img1
img2
Some things that gets to messy to write in comments:
1) Use a profiler to find out what parts of the code that is actually taking time. It can be wise to break out all loops into functions to get more useful information from the profiler.
2) Use the information from the profiler to optimize the code
You're not showing the implementation of queue. That could be the bottleneck. If it is, there are basically two ways to attack it. Either try to optimize the queue code, or change the overall algorithm to use the queue less or in a smarter way.
Solving the Bacon number is basically a shortest path problem, so you might want to tinker with different algorithms. Dijkstras and A* are common shortest path algorithms.
Another thing you might look into is preprocessing the data. Right now, you have a struct array, and the structs have a field called name. Maybe you could change this to an int where 1 indicates that the name is "Bacon,Kevin" and 0 that it's not. That would save you potentially costly calls to strcmp and might also make it more cache friendly.
You might want to change the representation completely. Right now, I get the impression that you have something like this:
struct actor {
struct actor *parent, *next;
char *name;
}
I already mentioned that you could change name to a simple integer, but perhaps you could also change everything to:
int *names;
int *parents;
int *next;
That could make the code a lot more cache friendly. Or not. But it is worth trying. Linked lists are usually not very cache friendly. And this can be good to think about when it comes to the queue too.
Also, you might have a look at this: Calculating "Kevin Bacon" Numbers
I solved this problem with your helpful advices.
The way I used before (Too slow => takes 85 seconds for my input):
-When a node is visited, append this node to the end of visited[] array.
-Checking if node is visited or not by looping over all elements of visited[] array (via contains() function)
Faster way which I am using now (1.75 seconds for the same input):
-When a node is visited, assign this node to related index => (visited[actorNumber] = node;)
-Checking if node is visited or not without looping over all elements, only checking the related index.
I also removed strcmp and I am checking by index now.
Updated code is here:
int findBaconNumber(v * actors[], int actorCount, int index, int baconsIndex){
// Bacon number is 0:
if(actors[index]->index == baconsIndex)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
q * queue = createQueue();
v * visited[ACTORS_SIZE] = {};
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[tmp->index] = tmp;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(tmp->index != baconsIndex && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
// If not visited:
if(visited[tmp2->actorNo] == NULL){
enqueue(queue, actors[tmp2->actorNo]);
visited[tmp2->actorNo] = actors[tmp2->actorNo];
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(tmp->index != baconsIndex)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}
im trying to insert one list in the end of another
with this code:
typedef struct Element
{
int vKey;
char vInfo[30];
struct Element *pNext;
} tsElement;
typedef struct
{
tsElemento *pFirst;
int vLength;
} tsLDSE;
void Unir(tsLDSE *pListIn, tsLDSE *pListOut)
{
tsElement *pElementOut;
pElementOut = pListOut->pFirst;
while (pElementOut != NULL)
{
pElementOut = pElemenoOut->pNext;
}
pElementOut = pListIn->pFirst;
pListOut->vLength = pListOut->vLength + pListIn->vLength ;
}
i checked printing the adresses, pElementoOut is really the end of the first list and is poiting to NULL, then it receives the fisrt adress of the second list, but when i print it out it only prints the first list and i can't figure out why.
Your function Unir only adds the length of the input list to the length of the output list.
This loop:
while (pElementOut != NULL)
{
pElementOut = pElemenoOut->pNext;
}
Only gets pElementOut to be NULL.
In addition, when you write pElementOut = pListIn->pFirst;, all you change is the local variable pElementOut.
What you want to do instead is this:
while (pElementOut->pNext != NULL)
{
pElementOut = pElementOut->pNext;
}
pElementOut->pNext = pListIn->pFirst;
This puts the first element of pListIn at the end of the last element of pListOut.
Also, add a NULL check at the beginning of your function! You can easily get a NULL pointer dereference there if you aren't careful.
I don't know why I can read the Linked list without problems in LABEL : 1 ; but the program just crashes and print grabage in the LABEL : 0 ;
In other terms, why the linked list works fine inside the lecture function , but not outside it ?
Here is my code :
/* including libraries */
#define V 20
typedef struct DATA{
char* NomP;char* NomA;
struct DATA *Next;
}DATA;
// Prototypes .
int main(void)
{
char FileName[V];
puts("Data file ? : ");gets(FileName);
FILE* fs = fopen(FileName,"r"); // Check if fs is NULL
DATA *HEAD = MALLOC(sizeof (DATA)); int len = lecture_data(fs,HEAD);
print_data(HEAD,len); //LABEL : 0
return 0;
}
int lecture_data(FILE *fs,DATA *ROOT)
{
char cNom[V],cArticle[V];
int eofs=0;int i=0;
while(!eofs)
{
DATA *Data = MALLOC(sizeof (DATA));
fscanf(fs,"%s %s",cNom,cArticle);
Data->NomA = MALLOC(strlen(cArticle)+1);
Data->NomP = MALLOC(strlen(cNom)+1);
strcpy(Data->NomA,cArticle);
strcpy(Data->NomP,cNom);
if( i==0 )
{
Data -> Next = NULL ;
ROOT = Data ;
}
else
{
DATA* Ptr = ROOT ;
while( (Ptr->Next) != NULL )
{
Ptr = (Ptr -> Next);
}
Data -> Next = NULL ;
Ptr -> Next = Data ;
}
i++;
eofs = feof(fs) ;
// check ferror(fs) here
}
puts("Start of reading :");
print_data(ROOT,len); // LABEL : 1
puts("End Of Reading ");
fclose(fs);
return i;
}
Here is the printing function :
void print_data(DATA *L_ROOT,int len)
{
int i = 0 ;
DATA* LINK;
LINK = L_ROOT;
while( LINK != NULL )
{
printf("%d : DATA->NomA : %s\n",i,LINK->NomA);
printf("%d : DATA->NomP : %s\n",i,LINK->NomP);
LINK = LINK -> Next ;
i++;
}
}
You're allocating data for the root of the list in the main function, and pass that to the function so that it may populate the list, but the first time you allocate an element you overwrite the ROOT pointer value.
this makes you lose the only connection between the function and the outside world (since the return value is just a number), so the HEAD value in main() is left pointing at nothing meaningful (because your function never uses it), while the list remains allocated in some memory location that no one outside is pointing to, which means it's lost. Running valgrind would have been able to identify this.
You can fix that by changing the (i==0) case from -
ROOT = Data ;
into
ROOT->next = Data ;
but make sure you're ignoring the data of the root node later on.
p.s. - using capitalized variables and types is not considered a good idea (it's mostly reserved for macros). It also makes your code look like you're shouting :)
The (main) problem is that lecture_data doesn't use it's input parameter (ROOT) for storage of the linked list, nor does it return the internal generated list. The correct way to handle this is to have ROOT reference the calling scope's parameter so that it can update it's reference as necessary.
int main(void)
{
char FileName[V];
puts("Data file ? : ");gets(FileName);
FILE* fs = fopen(FileName,"r"); // Check if fs is NULL
DATA *HEAD = NULL;
int len = lecture_data(fs, &HEAD);
print_data(HEAD); //LABEL : 0
return 0;
}
int lecture_data(FILE *fs,DATA **ROOT)
{
char cNom[V],cArticle[V];
int i=0;
DATA *current = *ROOT; // grab the passed in reference
while(!feof(fs))
{
if(fscanf(fs,"%s %s",cNom,cArticle) <= 0) // This call is only successful if the return value is > 0
{
// check ferror(fs) here
continue; // Can also "break;" here, essentially, it's eof already
}
DATA *Data = MALLOC(sizeof (DATA));
Data->NomA = MALLOC(strlen(cArticle)+1);
Data->NomP = MALLOC(strlen(cNom)+1);
strcpy(Data->NomA,cArticle);
strcpy(Data->NomP,cNom);
if(NULL == current) // ROOT was uninitialized before the call
{
Data -> Next = NULL;
*ROOT = Data;
}
else
{ // We don't need to iterate the list in every step.
Data->Next = current->Next; // This part allows the function to insert nodes in the middle / end of an existing list
current->Next = Data;
current = Data;
}
i++;
}
puts("Start of reading :");
print_data(ROOT); // LABEL : 1
puts("End Of Reading ");
fclose(fs);
return i;
}
Note: print_data didn't do anything with the len parameter, so no need passing it in at all.
This solution is not wasteful in terms of "empty" nodes in the list (as opposed to having an empty head to ignore), and is suitable both for initializing the list from scratch AND for cases where you need to append / insert into an existing list.
I have a linked list whose elements are of type
typedef struct List * News;
struct List
{
char * Text;
News NextN;
};
In the main function I declared an array of type News as follows
News PTH[50];
for (i = 0; i < 50; i++)
{
PTH[i] = (News) malloc(sizeof(struct List));
PTH[i] -> Text = NULL;
PTH[i] -> NextN = NULL;
}
I added new nodes in the beginning of the list with
if ( PTH[i] -> Text == NULL)
PTH[i] -> Text = msg;
else
{
t -> Text = msg;
t -> NextN = PTH[i];
PTH[i] = t;
}
Where msg is a array of char of length 2000; and and then tried to print the texts apointed by PTH[i] -> Text with
p = PTH[i];
if (p -> Text != NULL)
{
printf("%s", p -> Text);
p = p -> NextN;
}
while (p != NULL)
{
printf("%s", p -> Text);
p = p -> NextN;
}
}
This algorithm only add one node. The error is how I define the PTH or is there an error in how I put nodes in the list.
maybe it is something like this you are after:
if ( PTH[i]->Text == NULL )
{
PTH[i]->Text = msg;
}
else // first create node, then append by first finding the last node
{
News it = NULL;
News t = malloc( sizeof(struct List));
t->Text = msg;
t->NextN = NULL;
for ( it = PTH[i]->NextN; it != NULL; it = it->NextN)
{
;
}
it->NextN = t;
}
Assuming that msg is a buffer you use to receive new data, you have to be careful with this statement:
PTH[i] -> Text = msg;
Since msg is a pointer to char, the assignment will not copy the characters sequence; instead, it will just make PTH[i]->Text point to the same location as msg. This is problematic if you change the contents in msg - the changes are, of course, reflected back in every PTH[i]->Text for which the assignment was made, namely, every node that you ever added. Probably, not quite what you want. This is why it seems like you can only handle one node at a time. They all get the same text, because they all point to the same memory location.
You should use strcpy instead:
strcpy(PTH[i]->Text, msg);
Don't forget to include string.h.
This assumes that PTH[i]->Text is already allocated. You might want to use strncpy if there is a chance that msg exceeds 2000 characters, to avoid buffer overflows.
If you didn't alloc space for PTH[i]->Text, you could allocate exactly strlen(msg)+1 positions for PTH[i]->Text and then use strcpy safely. Or you could use strdup, also declared in string.h, which has exactly this behavior:
PTH[i]->Text = strdup(msg);