function searching in an array of structures - stop condition unknown - c

The following program stores every word and then prints them with a number of occurrences.
Global typedef declaration:
typedef struct {
char * word;
int occ;
}
words;
words *data=NULL;
I have a problem with the search function. I've created a function returning int that looks like this: (max is the constantly updated size of an array of structures, that's why I call search function after EOF is reached.)
int search(char *word,int max)
{
int i;
for(i=0; i<max; i++)
{
if(!strcmp(data[i].word,word)) return i;
}
return -1;
}
But I noticed I'm supposed to write a search function having that prototype:
struct abc *find(char *word)
So I've created the following code:
struct words *findword(char *word)
{
struct words *ptr;
for (ptr = data; ptr != NULL; ptr++) { /* IS THE STOP CONDITION OK? */
if (strcmp(word, ptr->word) == 0)
return ptr;
}
return NULL;
}
And I receive many errors during compilation:
reverse.c: In function ‘findword’:
reverse.c:73: warning: assignment from incompatible pointer type
reverse.c:73: error: increment of pointer to unknown structure
reverse.c:73: error: arithmetic on pointer to an incomplete type
reverse.c:74: error: dereferencing pointer to incomplete type
reverse.c: In function ‘main’:
reverse.c:171: error: ‘which’ undeclared (first use in this function)
reverse.c:171: error: (Each undeclared identifier is reported only once
reverse.c:171: error: for each function it appears in.)
make: * [reverse.o] Error 1
which is an int variable assigned to the return of my firstly written search function.
The error with which is easily fixed, but I don't know how to replace that (solution working with my base search function):
data[which].occ++;
How to fix it so that it'll work with my new approach to search?
EDIT
main() added:
int main(int argc, char **argv)
{
char *word;
words *temp;
int c,i,num;
/*int which;*/
FILE *infile;
if(argc!=2) {}
if((infile=fopen(argv[1],"r"))==NULL) {}
num=0;
while(1)
{
c=fgetc(infile);
if(c==EOF) break;
if(!isalpha(c)) continue;
else ungetc(c,infile);
word=getword(infile);
word=convert(word);
/*which=search(word,num);*/
if(findword(word))
{
if(!(temp=realloc(data,sizeof(words)*(num+1))))
{}
else
data=temp;
data[num].word=strdup(word);
data[num].occ=1;
num++;
}
else
data[which].occ++;
free(word);
}
sort(num-1);
for(i=0;i<num;i++)
{}
free(data);
if(fclose(infile))
{}
return 0;
}
I've left {} for the irrelevant pieces of code eg. error handling.
EDIT2
The things I'm asking for above, are fixed. However, I get a seg fault now.
I'll give a link to the whole code, I don't want to put it in an edited post since it'd create a big mess. Seg fault is caused by lines 73 and 152 (strcmp is not working somehow). Hope that full code will be easier to understand.
FULL CODE

The problems are with your findword function, lets go through all the lines
struct words *ptr;
This is not what you ment to do. The typedef you used in defining the structure allows you to not need to write struct anymore. This is why you're getting the error: reverse.c:73: error: increment of pointer to unknown structure. What you want is just:
words *ptr;
Next, the loop:
for(ptr=data; //This is fine, you're assigning your local ptr to the global data. I assume that's something valid
ptr != NULL; //That could OK too... we can loop while ptr is not NULL
ptr++) //This line makes no sense...
You may want to look up how for loops work again, the point is you're incrementing something until it hits a condition. ptr++ will move where you're pointing too, so you'll no longer be pointing to your structure.
I need to see your main() function to understand what you're trying to accomplish, but based on the prototype you have to follow, I think the easiest solution would be something like:
void main()
{
// init your vars
bool more_words_to_add = true;
words *ptr = NULL;
int i;
// populate your array of words
while(more_words_to_add) {
for(i = 0; i<max; i++) {
if(ptr = findword("word")) //if we find the word
ptr->occ++; //increment the number of times we found it
else {
//I don't know what you want to do here, it's not clear what your goal is.
//Add the new word to your array of words and set occ to 1,
//then increment max because there's one more word in your array?
}
}
//get the next word to fine, or else set more_words_to_add = false to break
}
}
If this type of solution is what you're looking to do, then you can adjust your findwords function to be very simple:
struct words *findword(char *word)
{
words *ptr = data;
if (strcmp(word, ptr->word) == 0)
return ptr;
return NULL;
}
EDIT: For your new error I suspect the problem is with your memory allocation, see this short example of using your structure:
words *findword(char *word)
{
words *ptr = data;
if(strcmp(word, ptr->word) == 0)
return ptr;
return NULL;
}
int main(){
words *ptr;
data = realloc(data, sizeof(words));
data->word = "hello"; //DO NOT SKIP THESE LINES
data->occ = 0; //DO NOT SKIP THESE LINES
if(ptr = findword("hello")) {
ptr->occ++;
printf("I found %d %s's\n",ptr->occ, ptr->word);
}
}
mike#linux-4puc:~> ./a.out
I found 1 hello's
You can see here that you need to alloc some memory for the global structure then you can store data in it and pass pointers to it.
EDIT 2:
Your main() code does this:
if((ptr = findword(word)))
{
//do stuff
}
else
ptr->occ++;
That's not going to work because if findword() fails it returns NULL, so in the if check ptr is set to NULL, then in the else you're trying to deference NULL. If (and keep in mind I'm not really reading your logic so this is up to you) you really want to increment ptr->occ if a word is not found then you want this instead:
if(findword(word))
{
ptr = findword(word);
//do stuff
}
else
ptr->occ++; //increments the current ptr's occ, no new ptr was assigned.

for (ptr = data; ptr != NULL; ptr++) {
/* IS THE STOP CONDITION OK? */
No. Your pointer just keeps getting incremented. The only thing that would make it NULL in that code is integer overflow. You could look at what it points to, and see if that is NULL, IF you preset the data area to 0's:
#define NUM_WORDS 100
data = calloc(NUM_WORDS,sizeof(words));
Or
#define NUM_WORDS 100
int bytes = NUM_WORDS * sizeof(words);
data = malloc(bytes);
memset(data,0,bytes);
....
for (ptr = data; ptr->word != NULL; ptr++) {
If you don't want to preset the data area to 0 then you will have to pass the current amount of structs currently held in the data area to your function in order to know how much to loop.

There's no such thing as struct words in your program; there's an unnamed struct type, and a typedef words to that type. Either use struct words or words consistently.
You'll then need to replace
data[which].occ++;
with
result->occ++;
where result is the return value from your new search function.

Related

c Struct Array, Storing string and its occurrence and writing it to a file

so I'm having a little problem with my struct array not doing what its supposed to. I get no compiler warnings or errors when building the program.
int Array_Size=0;;
int Array_Index=0;
FILE *Writer;
struct WordElement
{
int Count;
char Word[50];
};
struct WordElement *StructPointer; //just a pointer to a structure
int Create_Array(int Size){
StructPointer = (struct WordElement *) malloc(Size * sizeof(StructPointer));
Array_Size = Size;
return 0;
}
int Add_To_Array(char Word[50]){
int Word_Found=0;
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
{
if(strcmp(StructPointer[i].Word, Word)) // This should only run if the word exists in struct array
{
StructPointer[i].Count++;
Word_Found=1;
}
}
if(Word_Found==0) // if the above if statement doesnt evualate, this should run
{
strcpy(StructPointer[Array_Index].Word, Word); //copying the word passed by the main function to the struct array at a specific index
printf("WORD: %s\n", StructPointer[Array_Index].Word); // printing it just to make sure it got copied correctly
Array_Index++;
}
return 0;
}
int Print_All(char File_Name[50])
{
Writer = fopen(File_Name, "w");
printf("Printing starts now: \n");
for(int i=0; i < Array_Size; i++)
{
fprintf(Writer, "%s\t\t%d\n",StructPointer[i].Word, StructPointer[i].Count);
}
free(StructPointer);
return 0;
}
These functions get called from a different file, The Add_To_Array is called when the program reads a new word form the text file. That function is supposed to check if the word already exists in the struct array and if it does, it should just increment the counter. If it doesn't, then it adds it.
The Print_All function is called after all the words have been stored in the struct array. Its supposed to loop through them and print each word and their occurrence. In the text file, there are 2 of every words but my program outputs:
this 13762753
document -1772785369
contains 1129268256
two 6619253
of 5701679
every 5570645
word 3342389
doccontains 5374021
I don't know what to make of this as im really new to C programming... It's probably worth mentioning the if(Word_Foun==0) doesn't execute
StructPointer = malloc(Size * sizeof(*StructPointer));
This will be the correct allocation. Otherwise you will have erroneous behavior in your code. Also check the return value of malloc.
StructPointer = malloc(Size * sizeof(*StructPointer));
if(NULL == StructPointer){
perror("malloc failure");
exit(EXIT_FAILURE);
}
You are allocating for struct WordElement not a for a pointer to it. You already have a pointer to struct WordElement all that you needed was memory for a struct WordElement.
Also in the loop you are accessing array index out of bound
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
^^^
It will be i < Array_Size.
In case match occurs you want to set the variable Word_found to 1.
if(strcmp(StructPointer[i].Word, Word) == 0){
/* it macthed */
}
Also Writer = fopen(File_Name, "w"); you should check the return value of fopen.
if(Writer == NULL){
fprintf(stderr,"Error in file opening");
exit(EXIT_FAILURE);
}
Also when you are increasing the Array_index place a check whether it might access the array index out of bound.
The more global variable you use for achieving a small task would make it more difficult to track down a bug. It is always problematic because the places from which data might change is scattered - making it difficult to manage.

Segmentation fault copying tree nodes into array

I use this structure for my tree:
typedef struct product{
char name[50];
char id[5];
double price;
int amount;
struct product *left_p, *right_p;
}product_t;
So, I must convert the tree into an array.
I wrote this for the tree dimension:
int tree_dim(product_t *node_p){
int i = 1 ;
if (node_p == NULL)
i = 0;
else{
i += tree_dim(node_p->left_p);
i += tree_dim(node_p->right_p);
}
return i;
}
My tree is populated by reading the records from a txt file. The records are 21 and the value returned by tree_dim is correct. The value is stored inarr_dim.
Then I create aproduct_t *products_a; wich will be the "array" and allocate it in memory by using products_a = malloc (arr_dim*sizeof (product_t));
Now, this is the function to fill the array with the tree nodes:
void fill_array(int *index, product_t *node_p, product_t *products_a){
if (node_p != NULL){
fill_array(index, node_p->left_p, products_a);
products_a[*index++] = *node_p;
fill_array(index, node_p->right_p, products_a);
}
}
But it gives me segmentation fault error so I also tried this 2nd solution:
int fill_array(product_t *node_p, product_t *products_a){
int i = 1 ;
if (node_p == NULL){
i=0;
}
else
{
i += fill_array(node_p->left_p, products_a);
products_a[i-1] = *node_p;
i += fill_array(node_p->right_p, products_a);
}
return i;
}
Which doesn't give segmentation fault but when I print the array there are empty positions.
I need some tips on where I'm wrong. Maybe a problem with the index and the recursive calls but I can't figure it out.
Look at the precedence of these two operators
*index++
++ Incrementation has higher precedence than * dereference right?
So if you first move in memory by sizeof(int) then you arent no more in your allocated memory and dereferencing would cause UB.
Its always better to use brackets () if you arent sure about precedence.
(*index)++ // This is right
Filip has already pointed out the problem with your first function.
The problem with your second function is that it works only when filling from the left branch. After you have done that and copied the current product, there are some elements in the array, but copying from the right branch will start at index 0 again, so it will overwrite existing data and leave data at the end uninitialised.
You could fix this by passing the current index i to your function, but I find the i = func(..., i); syntax a bit redundant.
In C, you can pass in a subarray of array starting at element i with &array[i] or just array + i. (Remember that an array in a function call "decays" into a pointer to the first element, &array[0].)
So this will work:
int fill_array(product_t *node_p, product_t *products_a)
{
int i = 0;
if (node_p == NULL) return 0;
i += fill_array(node_p->left_p, products_a);
products_a[i++] = *node_p;
i += fill_array(node_p->right_p, &products_a[i]);
return i;
}

Scanf leads to segfault

I have a text file:
In 0 John 66
In 1 May 77
In 0 Eliz 88
Out 0
Out 0
I'm trying to parse this text file using scanf, and at the moment send the values after "In" to the add function, however I'm getting a seg fault when trying to do this.
I have some code here:
A struct in a seperate header file:
typedef Officer test;
typedef struct {
test tests[6];
int s;
} copList;
And this one:
typedef struct {
char name[25];
int id;
} Officer;
Then I have my main method
int main(void) {
FILE * ptr;
char buffer [500];
char * temp;
int pos;
int grade;
char * name;
copList * L;
ptr = fopen("test.txt","r");
if(ptr == NULL)
exit(1);
temp = malloc(sizeof(char)*10);
name = malloc(sizeof(char)*10);
L = malloc(sizeof(copList));
while(fgets(buffer,500,ptr) != NULL) {
sscanf(buffer,"%s %d %s %d\n",temp,&pos,name,&grade);
add(L->tests[pos],pos,L); //this gives me a seg fault
}
free(name);
free(temp);
free(L);
fclose(ptr);
return 0;
}
In a separate c file I have the add function.(Can't be changed)
void add(Test b, int pos, copList * L) {
//code to be added here later...
}
I've tried allocating different amounts of memory, but that didn't help. Also I noticed if I set a value to pos, in the while loop, before the add function call, I don't get a seg fault, but obviously that's not what I want, because the value wouldn't change. Any help would be much appreciated.
The main problem I see with your code is that it does not check the return value of sscanf -- if sscanf returns something other than 2 or 4, that means your input is something other than what you say it is. In addition, the arrays temp and name might overflow (on inputs other than what you show), which would cause undefined behavior. Finally, the spaces and \n in the sscanf format are wrong and should be removed (though they shouldn't actually cause any problems in this case.)
So you code should be something like:
while(fgets(buffer,500,ptr) != NULL) {
int count = sscanf(buffer,"%9s%d%9s%d",temp,&pos,name,&grade);
if (count != 2 && count != 4) {
fprintf(stderr, "Invalid input line: %s", buffer);
continue; }
... do stuff with temp and pos (only use name and grade if count == 4)
in this line:
add(L->tests[pos],pos,L);
the first parameter is a copy of the 'test' struct.
It is almost always a bad idea to pass a whole struct. Much better to just pass a pointer to the struct:
add( &(L->tests[pos]), pos, L );
Then, this line has a couple of problems:
void add(Test b, int pos, copList * L) {
1) 'Test' is a non-existent type, perhaps you meant: 'test'
2) 'b' is expecting a passed struct. as mentioned above,
it is (almost) always better to pass a pointer to a struct.

Segmentation Fault(core dumped) - Structure;

So I have the following question:
I have this struct ListAut
struct ListAut{
char* biggestn;
int sizeof_biggestn;
int total_len;
struct node* avl;
};
Its typedef is as it follows:
typedef struct ListAut *IndexOfAuts;
IndexOfAuts *newIndexOfAuts()
{
int i;
IndexOfAuts *ioa = malloc(27 * sizeof(struct ListAut));
for (i = 0; i < 27; i++)
{
ioa[i]->biggestn = "";
ioa[i]->sizeof_biggestn = 0;
ioa[i]->total_len = 0;
ioa[i]->avl = NULL;
}
return ioa;
}
void insertName(IndexOfAuts * ioa, char *nome)
{
char *aux = malloc(sizeof(nome));
aux = trim(nome);
int index = getIndexOfLetter(aux);
if (nameLen(aux) > getSizeOfLongName(ioa[index]))
{
strcpy(ioa[index]->biggestn, aux);
ioa[index]->sizeof_biggestn = nameLen(aux);
}
ioa[index]->total_len += nameLen(aux);
insert(ioa[index]->avl, aux);
}
This is an important part of a module I need for a project, and on its main it's Seg Faulting. I suspect it's on the creation of an "object" newIndexOfAuts(),
The idea of this module is to have an array of 27 pointers to those structures, one to each letter and another to the special characters;
Now I'm just confused because it might be from the problem above or from a module loader I made:
void loadModules(char *filename, IndexOfAuts * ioa, StatTable st)
{
char *nameofile = malloc(20);
strcpy(nameofile, filename);
FILE *file = fopen(nameofile, "r");
if (file != NULL)
{
int counter, ano;
char *buff, *field, *auxil;
buff = malloc(1024);
field = malloc(200);
auxil = malloc(200);
while (fgets(buff, 1024, file))
{
counter = 0;
field = strtok(buff, ",");
printf("inserting 1st name\n");
insertName(ioa, field);
counter++;
while (!atoi(field))
{
if ((auxil = strtok(NULL, ",")) != NULL)
{
counter++;
field = auxil;
insertName(ioa, field);
}
}
ano = atoi(field);
incPub(st, ano, counter - 1);
}
fclose(file);
}
}
When i run this in main that has the following lines:
printf("Creating Stat Table");
StatTable st=newStatTable();\\This Line is correct, i checked it,i hope
printf("Creating index");
IndexOfAuts* ioa=newIndexOfAuts();
printf("Loading Modules");
loadModules(filename,ioa,st);
Those prints were for me to see where was the cause of the seg fault, but the last line printed was the "Creating Index".
There are several cases of undefined behavior and one memory leak (and a possible case of undefined behavior too):
You have this initialization ioa[i]->biggestn=""; It make the biggestn member point to a constant array of one character (the '\0' character). You then do strcpy(ioa[index]->biggestn,aux); which will write over a constant character, and then go outside of the array writing into unknown memory.
You have this: char* aux=malloc(sizeof(nome)); That allocates only 4 or 8 bytes, which the size of the pointer and not what the pointer points to. Use strlen to get the length of a string.
For the above allocation you also need to allocate a byte extra, as strlen only returns the length of the string without the terminator.
You have aux=trim(nome); This overwrites the pointer you just allocated, leading to a memory leak.
The above call might also lead to undefined behavior if you return a pointer to a local variable or array.
There are probably other problematic lines, these were just the ones I found on a quick glance.
And a general tip: Learn to use a debugger! The debugger is a programmers best tool next to the compiler. If you run your program in a debugger, the debugger will stop at the location of the crash, and let you examine (and also walk up) the function call stack, as well as let you examine values of variables.

C - Passing a Pointer to a Function and then Passing that Same Pointer Inside the Function to Another Function

Whew! Long title...here's some pseudo-code to explain that verbiage:
int main(){
int* ptr = function1(); //the data that ptr points to is correct here
function2(ptr);
}
int function2(int* ptr){
//the data that ptr points to is still correct
int i;
for(i=0;i<length;printf("%d\n", (*ptr)[i]), i++); //since ptr points to a contiguous block of memory
function3(ptr);
}
int function3(int* ptr){
//the data that ptr points to is INCORRECT!!!
}
Why would the data in function3 be incorrect?
Note: function1 performs a malloc() and returns the pointer to that memory.
ACTUAL CODE
#include <stdlib.h>
#include <stdio.h>
//Structures
struct hash_table_data_
{
int key, data;
struct hash_table_data_ *next, *prev;
};
struct hash_table_
{
int num_entries;
struct hash_table_data_ **entries;
};
typedef struct hash_table_data_ hash_table_data;
typedef struct hash_table_ hash_table;
//Prototypes
hash_table *new_hash_table(int num_entries);
int hash_table_add(hash_table *ht, int key, int data);
int hash_table_loader(hash_table* ht);
//Main
int main()
{
int num_entries = 8;//THIS MUST BE AUTOMATED
hash_table* ht = new_hash_table(num_entries);
hash_table_loader(ht);
return 0;
}
//Function Definitions
hash_table *new_hash_table(int num_entries)
{
hash_table* ht = (hash_table*) malloc(sizeof(hash_table));
hash_table_data* array = malloc(num_entries * sizeof(hash_table_data));
int i;
for (i=0;i<num_entries;i++)
{
array[i].key = -1;
array[i].data = -1;
array[i].next = NULL;
array[i].prev = NULL;
}
ht->entries = &array;
ht->num_entries = num_entries;
return ht;
}
int hash_table_add(hash_table *ht, int key, int data)
{
//VERIFY THAT THE VALUE ISN'T ALREADY IN THE TABLE!!!!!!!!!!!
int num_entries = ht->num_entries;
hash_table_data* array = *(ht->entries); //array elements are the LL base
int hash_val = key%num_entries;
printf("adding an element now...\n");
printf("current key: %d\n", array[hash_val].key);
int i;
for(i=0;i<num_entries;printf("%d\n", (*(ht->entries))[i].key),i++);//DATA IS INCORRECT!!!!
if (array[hash_val].key == -1)//is this the base link?
{
printf("added a new base link!\n");
array[hash_val].key = key;
array[hash_val].data = data;
array[hash_val].next = NULL;
array[hash_val].prev = &(array[hash_val]);
}
else//since it's not the base link...do stuff
{
hash_table_data* new_link = malloc(sizeof(hash_table_data));
new_link->key = key;//set the key value
new_link->data = data;//set the data value
if (array[hash_val].next == NULL)//we must have the second link
{
printf("added a new second link!\n");
new_link->prev = &(array[hash_val]); //set the new link's previous to be the base link
array[hash_val].next = new_link; //set the first link's next
}
else//we have the 3rd or greater link
{
printf("added a new 3rd or greater link!\n");
hash_table_data next_link_val = *(array[hash_val].next);
while (next_link_val.next != NULL)//follow the links until we reach the last link
{
next_link_val = *(next_link_val.next);//follow the current link to the next
}
//now that we've reached the last link, link it to the new_link
next_link_val.next = new_link; //link the last link to the new link
new_link->prev = &(next_link_val); //link the new link to the last link
}
}
return 0;
}
int hash_table_loader(hash_table* ht)
{
int i;
for(i=0;i<(ht->num_entries);printf("%d\n", (*(ht->entries))[i].key),i++); //DATA IS STILL CORRECT HERE
FILE *infile;
infile = fopen("input.txt", "r");
while(!feof(infile))
{
int key,data;
fscanf(infile, "%d %d", &key, &data);
hash_table_add(ht, key, data);
}
fclose(infile);
}
Note: Issue occurring the first time hash_table_add() is called.
Your first problem is here:
ht->entries = &array;
You cause the structure to hold a hash_table_data** which points to the variable hash_table_data* array which is local to the function; then you exit the function and return a pointer to the structure. The structure still exists (it was allocated via malloc(), and the stuff that array points to still exists, but array itself does not. Accordingly, this pointer within the structure is now invalid.
As far as I can tell, there is no reason for you to be holding a pointer-to-pointer here. Just use hash_table_data* as the entries type, and copy array into that struct member. Pointers are values too.
I guess you iterate incorrectly
for(i=0;i<length;printf("%d\n", (*ptr)[i]), i++);
this is nonsense.
You should rewrite it as this:
for(i=0;i<length;i++)
printf("%d\n", ptr[i]);
(*ptr)[i] is just wrong, it doesn't make sense if you think about it.
*ptr is the first element of the pointed-to array of ints.
ptr[i] is thi ith one, this is what you need.
Please, read Section 6 carefully.
A couple of advises based on this question:
Don't write overcomplicated code like this for statement with comma operator used, it just rarely needed and leads not only to confusion, but to mistakes (although no mistakes with it in this particular example)
Look carefully for mistakes, don't blame everything on functions. If your code doesn't work, try finding the exact place which is wrong and prove it. In this example people who tested your code were right: functions are definitely not the cause of the error.
hash_table *new_hash_table(int num_entries)
{
hash_table* ht = (hash_table*) malloc(sizeof(hash_table));
hash_table_data* array = malloc(num_entries * sizeof(hash_table_data));
// ....
ht->entries = &array; // Problem
// ...
return ht;
} // Life time of array ends at this point.
You are taking the reference of the local variable array and assigning it to ht->entries which is no more valid once the function returns.

Resources