This is an address:
struct Adress {
char name[31], lastname[31], email[48];
};
The goal is to have an address book in the main function, and the user should be able to type in a string, and the program lists out all of the people from the address book whose name or the last name contains the given string.
For example, if the address book contains "john" "doe", "jane" "doey" and "george" "johnson", and the user types in "doe", the output is:
1. john doe johndoe#email.com
2. jane doey janedoey#email.com
This part of the main function should use a function
int search(struct Adress array[], int size_of_the_addressbook, char* string_to_search)
which returns the index of the first found address, and -1 in case no address has been found.
Here's my try:
In the snippet from my main function (there 's no need to post input stuff here):
struct Adress adressbook[1000], *member;
int i = 0;
member = adressbook;
if (search(member, number_of_elements, string_to_seach)) == -1)
printf("No person found.\n");
else while((search(member, number_of_elements, string_to_seach)) != -1)
{
member = adressbook + search(member, number_of_elements, string_to_seach);
++i;
printf("%d. %s %s - %s\n", i, (*member).name, (*member).lastname, (*member).email);
++member;
}
And here's the search function:
int search(struct Adress array[], int size_of_the_addressbook, char* string_to_search)
{
int j, index;
struct Adress *i;
i = array;
while (strstr((*i).name, string_to_search) == 0 && strstr((*i).lastname, string_to_search) == 0)
{
index = ((i - array)/(sizeof (struct Adress)));
if (index == size_of_the_addressbook) return -1;
++i;
}
index = ((i - array)/(sizeof (struct Adresa)));
return index;
}
However, this program gets stuck in an infinite loop in pretty much any case when there is more than one member in the address book. I'm suspecting that in the while loop the search doesn't go on from the previously found member, but rather it starts from the begin each time, therefore it keeps finding the same, firstly found member each time.
Your search never actually returns -1, and your invoke of that search doesn't thusly have an exit condition. Further, you should be adjust each starting point of the next search to be one slot beyond the last discovery point.
I'm nearly certain this is what you're trying to do. I've not tested this (have no data to do so nor any info on the invocation of this functionality), but I hope the point is obvious:
int search(const struct Adress array[],
int size_of_the_addressbook,
const char* string_to_search)
{
const struct Adress *end = array + size_of_the_addressbook;
const struct Adress *i = array;
for (; i != end; ++i)
{
if (strstr(i->name, string_to_search) != NULL ||
strstr(i->lastname, string_to_search) != NULL)
break;
}
return i == end ? -1 : (int)(i - array);
}
void do_search(const struct Adress *array,
int number_of_elements,
const char *string_to_search)
{
int i = search(array, number_of_elements, string_to_search), base=0;
if (i == -1)
{
printf("No person found.\n");
return;
}
while (i != -1)
{
base += i;
printf("%d. %s %s - %s\n", base,
array[base].name,
array[base].lastname,
array[base].email);
base += 1;
// note adjustment of starting point using pointer arithmetic.
i = search(array + base,
number_of_elements - base,
string_to_search);
}
}
Hope it helps. Best of luck.
You have a few problems to mention
You call search() twice in your main loop which is absolutely unnecessary, you should call it once and store it's return value.
Your member pointer, never points after the first match, so the first match will always be found,
leading to an infinite loop.
You increase the member pointer and still pass number_of_elements to the search function. When you increase the member pointer the number of elements left to the right of it's resulting position is decreased by the same number that you increase member.
This expression is not giving the value you think
((i - array)/(sizeof (struct Adress)));
because you are computing the distaince between the two pointers i and array and then dividing it by sizeof(struct Address) which is 110, and as another answer mentioned, the value is automatically scaled, so
((i - array)/(sizeof (struct Adress))); -> i - array;
to see what I mean you may try to print this values
printf("\t%d\n", ((void*)member - (void*)adressbook));
printf("\t%d\n", ((void*)member - (void*)adressbook) / sizeof(*member));
printf("\t%d\n", member - adressbook);
Note: if your OS is 64bit, change the format specifier to "%ld".
This is the code that will do what you need
int search(struct Adress **array, int size_of_the_addressbook, char* string_to_search)
{
int index;
struct Adress *pointer;
if ((size_of_the_addressbook == 0) || (array == NULL) || (*array == NULL))
return -1;
pointer = *array;
index = 0;
while (strstr(pointer->name, string_to_search) == 0 &&
strstr(pointer->lastname, string_to_search) == 0)
{
/* check that we are not at the end of the array. */
if (++index == size_of_the_addressbook)
return -1;
/* not found yet, increment both arrays */
(*array)++;
pointer = *array;
}
return index;
}
and in main()
int index;
int foundIndex;
index = 1;
while ((foundIndex = search(&member, number_of_elements, string_to_seach)) != -1)
{
printf("%d. %s %s - %s\n", index, member->name, member->lastname, member->email);
index += 1 + foundIndex;
number_of_elements -= 1 + foundIndex;
++member;
}
in this approach, the member pointer is increased inside the search() function to point to the found element, a counter is added to reflect how much was advanced.
After the search() function returns, member should be increased by 1 again to point to the next element, and number_of_elements should be decreased by the number of elements advanced in the search function + 1 for the found element.
Also, keep a variable that you update on each iteration that gives you the actual index of the element in the array.
Related
So I am having two problems with this piece of code. The first problem when I'm following break points through it and I'm trying to copy the array to the struct fields, it is saying Error reading character string. And the second problem I am having is with the output "printMovieInfo function it is not printing out my list and it is causing an "unexpected breakpoint"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#pragma warning(disable : 4996)
#pragma warning(disable : 6387)
typedef struct {
char* g;
char* t;
}MovieInfo;
#define kTenItemsEntered 10
#define kCharacterString 30
//prototypes
void eliminateEndOfLine(char* buffer);
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]);
void printMovieInfo(MovieInfo list[]);
int main() {
MovieInfo newMovieInfo[kTenItemsEntered];
char title[kCharacterString];
char genre[kCharacterString];
printf("Enter 10 Title of Movie and 10 Genres\n");
for (int i = 1; i <= kTenItemsEntered; i++) {
printf("%d:\n", i);
printf("Title: ");
fgets(title, kCharacterString,stdin);
eliminateEndOfLine(title);
printf("Genre: ");
fgets(genre, kCharacterString, stdin);
eliminateEndOfLine(genre);
getMovieInfo(newMovieInfo, title, genre);
}
printMovieInfo(newMovieInfo);
return 0;
}
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
printf("%-35s %-35s\n", list[i].t, list[i].g);
}
}
//This is the end of new line function from examples
void eliminateEndOfLine(char* buffer)
{
char* target = strchr(buffer, '\n');
if (target != NULL)
{
*target = '\0';
}
}
Output
The output does not print
I'm guessing you're actually compiling in C++, otherwise this wouldn't compile as you need to use struct MovieInfo, not MovieInfo in C. You can fix that by doing:
typedef struct MovieInfo {
char* g;
char* t;
} MovieInfo;
In main your newMovieInfo is an array of MovieInfo structs. When you call getMovieInfo(newMovieInfo, title, genre);, you're actually passing a pointer to the first element of the array to the function (the array 'decays' into a pointer to the first element). What you should do instead is pass a pointer to the element you actually want to fill in. Array indices start at 0, not 1, so you need to fix your loop as well:
// previously: for (int i = 1; i <= kTenItemsEntered; i++) {
for (int i = 0; i < kTenItemsEntered; i++) {
...
// previously: getMovieInfo(newMovieInfo, title, genre);
getMovieInfo(&newMovieInfo[i], title, genre);
}
Then in getMovieInfo, since you already have a valid pointer to the struct, you don't need to allocate space for a new one:
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
// removed:
// ptr = (MovieInfo*)malloc(sizeof(MovieInfo));
// if (ptr == NULL) {
// printf("Error allocating the struct\n");
// }
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
Finally in printMovieInfo, your current code is printing the first element's data multiple times. You can fix that by indexing into the array to get the element you want:
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
// previously: printf("%-35s %-35s\n",list->t,list->g);
printf("%-35s %-35s\n",list[i].t,list[i].g);
}
}
Each time you call getMovieInfo, you're storing the data in a local pointer to allocated memory. That pointer gets lost when the function returns, so newMovieInfo is never populated with anything.
The pointer being passed to the function points to an already existing instance of MovieInfo, so there's no need to allocate memory for it (though you still need to allocate memory for the strings).
Even after this fix, you're only ever updating the first element of the newMovieInfo. You need to pass a pointer to the array element in question when you call getMovieInfo:
getMovieInfo(newMovieInfo + i - 1, title, genre);
Note the -1 here because you're looping from 1 to kTenItemsEntered and array indices in C start at 0. If you changed the loop to go from 0 to kTenItemsEntered-1, you can remove the -1 from the line above.
Your printing function is also only printing the first element of the array, so change the line in the loop body to:
printf("%-35s %-35s\n",list[i].t,list[i].g);
I am facing some issues regarding a realloc with a double pointer dynamic array.
What I would like to perform is to add 2 pointers of type Flight* inside the array schedule of type Flight **.
For that, I am relying on the function add_flight in the Functions.c file.
This function asks the user for the airline and flight number values and stores these data in a new Flight* f. If the schedule is null (no flight yet added) it allocates memory for the newly created flight otherwise it realloc the size of schedule in order the add the new flight.
Main.c file:
int main() {
int choice = 1;
Flight** schedule = NULL;
printf("---AIRPORT MANAGER---");
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule)->airline, (*schedule)->flightNumber);
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule + 1)->airline, (*schedule)->flightNumber);
return 0;
}
Functions.c file :
#include "Functions.h"
void mygets(char* s, int maxLength) {
fflush(stdout);
if (fgets(s, maxLength, stdin) != NULL) {
size_t lastIndex = strlen(s) - 1;
if (s[lastIndex] == '\n')
s[lastIndex] = '\0';
}
}
void flush() {
char buffer;
while ((buffer = getchar()) != EOF && buffer != '\n');
}
Flight** add_flight(Flight** schedule) {
Flight* f;
char buffer[100];
if ((f = (Flight*)malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
printf("\n\n---FLIGHT CREATION---");
printf("\nAirline: ");
mygets(buffer, sizeof(buffer));
if ((f->airline = _strdup(buffer)) == NULL) {
exit(1);
}
memset(buffer, 0, 100);
printf("\nFlight number: ");
scanf("%d", &f->flightNumber);
flush();
if (schedule == NULL) {
if ((schedule = malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
*schedule = f;
}
else {
int numberFlights = ((sizeof(*schedule)) / 4) + 1;
if ((schedule = realloc(schedule, numberFlights * sizeof(Flight*))) == NULL) {
exit(1);
}
*(schedule + numberFlights -1) = f;
}
return schedule;
}
The issue comes when the second call of add_flight is performed in the main.c
In the add_flight function, the data are indeed stored in the new Flight* f and then the else statement is considered: the variable numberFlights gets the value 2. However, the realloc doesn't work, the schedule is not enlarged and thus there is still only the first flight stored inside this schedule array. I can't figure out why the second flight is not added inside the schedule.
Can someone explain me why this realloc fails ?
Thanks for your help :)
The sizeof operator is evaluated at compile time. It cannot be used to determine the size of a dynamically allocated array.
C imposes the burden of keeping track of the actual size of an array onto the programmer. You could kee a separate count variable, but because the actual array and its size belong together, it is useful to store them alongside each other in a struct:
typedef struct Flight Flight;
typedef struct Flights Flights;
struct Flight {
char airline[4];
int number;
char dest[4];
};
struct Flights {
Flight *flight;
int count;
};
Instead of operating on the array, operate on the struct:
void add_flight(Flights *fl,
const char *airline, int number, const char *dest)
{
int n = fl->count++; // n is old count; fl->count is new count
fl->flight = realloc(fl->flight,
(fl->count + 1) * sizeof(*fl->flight));
snprintf(fl->flight[n].airline, 4, "%s", airline);
snprintf(fl->flight[n].dest, 4, "%s", dest);
fl->flight[n].number = number;
}
Intialize the flights struct with NULL and a count of zero and don't forget to release the used memory when you're done:
int main(void)
{
Flights fl = {NULL, 0};
add_flight(&fl, "AF", 5512, "CDG");
add_flight(&fl, "AA", 1100, "ATL");
add_flight(&fl, "LH", 6537, "FRA");
add_flight(&fl, "BA", 8821, "LHR");
add_flight(&fl, "IB", 1081, "EZE");
print_flights(&fl);
free(fl.flight);
return 0;
}
You can see it in action here. Some observations:
There is no need to distinguish between adding the first and subsequent flights, because realloc(NULL, size) behaves exactly like malloc(size).
It is not very efficient to reallocate the memory for each added item. Instead, you pick a suitable initial array size like 4 or 8, then double the size when you hit the limit. That means that the allocated size and the count may differ and you need an aditional memsize field in your flights struct.
The code above relies on manual initialization and destruction. Usually, you will write "constructor" and "destructor" functions to do that for you.
I have implemented code that imports data from a file containing 5 different values, one of them being Time. I have converted the time given in the format Hour.Minute.Second.Millisecond into just Milliseconds.
With this data I created a function Find that finds the data for a given time. This is where the problem arises, since there are multiple days of data here, and the time will repeat multiple times. Is there a function in the C library that returns all instances of a value? Ex.arr =[2,3,4,1,2,] I want it to tell me when the second 2 appears, returning 4.
Edit: For better clarity
These are the functions
void Find(SortedLinkedList *list,int target,int date, char *search) {
if(strcmp(search, "Time") == 0){
Sate *found = findTime(list, target,date);
printf("The Node with time:%d\n Is from the date:%d\n Contains the following:",found->Time,found->Date);
printf("RMag:%6.3f ", found->rmag);
printf("NSmag:%6.3f ", found->NSmag);
printf("azmag:%6.3f ", found->azmag);
printf("avgmag:%6.3f \n", found->avgmag);
}
}
Sate *findTime(SortedLinkedList *list, int target,int date){
Node *current = list->head;
for (int i = 0; i < (list->size)+1 && current != NULL; i++) {
if(current->data->Time == target && current->data->Date == date)
return current->data;
else{
current = current->next;
}
}
}
Right now for it to work I implemented a date insert to differentiate between the times but I'm wondering if it can be done without it.
There's not any kind of a iterate over a collection type of function in the Standard C library other than something like strtok() which will iterate over a text string using the provided token identification pattern.
There is the bsearch() function however that does a search through a sorted list of items and is not really what you want either.
It sounds like you want something like the following. This demonstrates an instantiation of an algorithm however I am not sure what the time points data looks like so that is something you will need to provide.
typedef unsigned long long TimePoint; // made up time data item
typedef struct {
int bFound;
unsigned long ulCurOffset; // array position where item found if bFound is true.
unsigned long ulOffset; // next array position to test
unsigned long ulCount; // count of times found
} IteratorThing;
IteratorThing IterateFunc (IteratorThing x, TimePoint *array, size_t len, TimePoint search)
{
x.bFound = 0; // assume we didn't find one.
// resuming from the current place in the array, search until we
// find a match or we reach the end of the array.
for ( ; x.ulOffset < len; x.ulOffset++) {
// this is a simple comparison for equality which may need to be
// more complex for your specific application.
if (array[x.ulOffset] == search) {
// we have found a match so lets update counts, etc.
x.ulCount++; // count of this search item found.
x.bFound = 1; // indicate we found one.
x.ulCurOffset = x.ulOffset; // remember where we found it.
x.ulOffset++; // point to the next array item to look at
break;
}
}
return x;
}
This would be used as in:
void main_xfun(void)
{
TimePoint array[] = { 1, 2, 3, 2, 3, 4, 0 };
TimePoint search = 2;
size_t len = sizeof(array) / sizeof(array[0]);
{
IteratorThing x = { 0 }; // define and initialize our iterator
while ((x = IterateFunc(x, array, len, search)).bFound) {
// do what is needed when we find a time value
// array offset to the item is x.ulCurOffset
// current count of times found is in x.ulCount;
printf(" found item %d at offset %d count is %d\n", (long)array[x.ulCurOffset], x.ulCurOffset, x.ulCount);
}
printf(" item %d found %d time\n", (long)search, x.ulCount);
}
{
IteratorThing x = { 0 }; // define and initialize our iterator
search = 25;
while ((x = IterateFunc(x, array, len, search)).bFound) {
// do what is needed when we find a time value
// array offset to the item is x.ulCurOffset
// current count of times found is in x.ulCount;
printf(" found item %d at offset %d count is %d\n", (long)array[x.ulCurOffset], x.ulCurOffset, x.ulCount);
}
printf(" item %d found %d time\n", (long)search, x.ulCount);
}
}
produces output of
found item 2 at offset 1 count is 1
found item 2 at offset 3 count is 2
item 2 found 2 time
item 25 found 0 time
To restart the search from the beginning just initialize the iterator struct to all zeros again.
What would be really interesting is to provide a pointer to a comparison function in the interface of the function IterateFunc() which would be called to do the comparisons. This would be along the lines of the bsearch() function which requires a pointer to a comparison function but then that is probably overkill for your specific needs.
If you want this hypothetical function to work for either an array of integers or for your time indexed structures, you will probably need to write a generic function.
If POSIX functions are available to you, you can use lfind() as a starting point for such a generic function.
The lsearch() function shall linearly search the table and return a pointer into the table for the matching entry. If the entry does not occur, it shall be added at the end of the table. ...
The lfind() function shall be equivalent to lsearch(), except that if the entry is not found, it is not added to the table. Instead, a null pointer is returned.
Since lfind() will return the first instance, you need to re-invoke lfind() again past the given instance to find the second instance.
void * lfind_Nth (const void *key, const void *base, size_t *nelp,
size_t width, int (*compar)(const void *, const void *),
int N)
{
const char (*array)[width] = base;
char (*p)[width] = NULL;
size_t n = *nelp;
while (N-- > 0) {
p = n ? lfind(key, array, &n, width, compar) : NULL;
if (p == NULL) break;
n -= (p + 1) - array;
array = p + 1;
}
return p;
}
For your integer array example:
int compar_int (const void *a, const void *b) {
return *(const int *)a != *(const int *)b;
}
int where_Nth_int(int key, int *arr, size_t nelm, int N) {
int *w = lfind_Nth(&key, arr, &nelm, sizeof(*arr),
compar_int, N);
return w ? w - arr : -1;
}
int main (void) {
int arr[] = {2,3,4,1,2,};
int nelm = sizeof(arr)/sizeof(*arr);
printf("Second 2 # %d\n", where_Nth_int(2, arr, nelm, 2));
}
I want to pass the contents of an array to another method and have that method print out the entire array - how would i do this?
Currently:
I'm returning an array from a function.
char* search_value(struct PDB *llist)
{
int realID = -7;
int x = 0;
int task = 0;
char *received;
char theMessage[100];
theMessage[0] = '\0';
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strcpy(theMessage, llist->data1);
break;
}
}
return theMessage;
}
i'm getting the return value:
void getMessage(const int GET_MESSAGE)
{
char * received = NULL;
int x = 0;
received = search_value(llist);
printf("%s", received);
}
I want to somehow print the entire value (rather than just the first value to which the pointer is pointing at - how would i do this?
A few corrections and it should work:
// - struct contents shouldn't be changed by the function, make its pointer const.
// - pass a pointer to an allocated array as parameter
char* search_value(const struct PDB *llist, char* theMessage)
{
int realID = -7;
int x = 0;
int task = 0;
char *received;
theMessage[0] = '\0';
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strcpy(theMessage, llist->data1);
break;
}
}
return theMessage;
}
void getMessage(const int GET_MESSAGE)
{
char received[100]; // allocate the array outside the function
int x = 0;
search_value(llist, received); // pass a pointer to the first element
printf("%s", received);
}
You have an issue with variable scope here: theMessage is local to the function search_value, so you're returning a pointer to an array which no longer exists once the function completes.
Instead you should use malloc() to allocate the space for theMessage and then subsequently free() it later on outside of the function when you're finished with it — however this can often lead to memory leaks if you're not diligent about cleaning up after yourself.
You can allocate the memory like so:
char * message = malloc(100);
One alternative would be to allocate the buffer in getMessage() and pass a pointer to the buffer into search_value which could then write into it:
void getMessage(const int GET_MESSAGE)
{
char received[100];
int x = 0;
search_value(llist, received);
printf("%s", received);
}
void search_value(struct PDB *llist, char * buffer)
{
// write to buffer
}
Another option is to declare a char * pointer inside getMessage(), pass a pointer to a pointer into search_value() and again use malloc() to allocate space for the buffer.
Finally, this is a minor style criticism, but you'd do well to learn to stick to one convention for naming your functions, search_value and getMessage are not consistent names, and this will irk many a coder that you work with.
You have several problems with your code. I'm guessing that you want to search a list for some value, then return that value.
The first problem is that you do not actually iterate over the list, but only check the same item over and over again. The other problem is that you return a pointer to a local variable. This is undefined behavior, because as soon as the function returns the memory the pointer points to can be used for something else.
I suggest you change your code as follows:
char *search_value(struct PDB *llist, char *theMessage, size_t theMessageMaxLength)
{
int realID = -7;
int task = 0;
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist != NULL && llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strncpy(theMessage, llist->data1, theMessageMaxLength);
theMessage[theMessageMaxLength] = '\0';
break;
}
llist = llist->next; /* Assuming the field is named "next" */
}
return theMessage;
}
void getMessage(const int GET_MESSAGE)
{
char *received = NULL;
char theMessage[100];
/* Subtract 1 from the size, for the terminating '\0' */
received = search_value(llist, theMessage, sizeof(theMessage) - 1);
printf("%s", received);
}
the array you are returning is local to that function. Either the calle function shall provide the array in which it expects the values or use static array.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 12 years ago.
I have a file with format: [name][number][amount]
number is taken as a string. and im using it in a strcmp.
Problem is that i get a segmentation fault. I know that on most cases when strcmp signs segmentation fault it means that one of the parameters is null or cant find its "end" ('\0').
I checked with gdb and i cant say if this is the problem.Take a look:
> (gdb) bt full
> #0 0x08048729 in lookup (hashtable=0x804b008, hashval=27,
> number=0x804b740 "6900101001")
> list = 0xffffffff
> #1 0x080487ac in add (hashtable=0x804b008,
> number=0x804b740 "9900101001", name=0x804b730 "Smithpolow",
> time=6943)
> new_elem = 0xffffffff
> hashval = 27
> #2 0x08048b25 in main (argc=1, argv=0xbffff4b4)
> number = 0x804b740 "9900101001"
> name = 0x804b730 "Smithpolow"
> time = 6943
> i = 2
Code:
typedef struct HashTable
{
int length;
struct List *head;
} HashTable;
//(resolving collisions using chaining)
typedef struct List
{
char *number;
char *name;
int time;
struct List *next;
} List;
int primes[]={17,29,51,79,163,331,673,1361,2729,5471,10949,21911,43853,87719,175447,350899};
*int PrimesIndex=1;* **int PrimesIndex=0;** **//changed.**
HashTable *createHashTable(size)
{
HashTable *new_table = malloc(sizeof(*new_table)*size);
if (new_table == NULL)
{ return NULL;
}
int i=0;
for(i; i<size; i++)
{ new_table[i].length=0;
new_table[i].head=NULL;
}
return new_table;
}
int hash ( HashTable *hashtable,char* number)
{
int hashval = 0;
int i = 0;
for ( i = 0; i < 10; i++)
{ hashval = (hashval << 5)|(hashval >> 27);
hashval += ( int)number[i];
}
return hashval % primes[PrimesIndex];
}
List *lookup ( HashTable *hashtable,int hashval,char number[10])
{
printf("NUMBER:%s\n",number);
List *list=hashtable[hashval].head;
for(list; list!=NULL; list=list->next){
if (strcmp(number,list->number)==0)
return list;
}
return NULL;
}
int add ( HashTable* hashtable,char number[10],char* name,int time)
{
List *new_elem;
int hashval=hash (hashtable,number);
new_elem=hashtable[hashval].head;
if(hashtable[hashval].length>0)
{
if ((lookup (hashtable,hashval,number))!=NULL) {return 0;}
}
if (!(new_elem=malloc(sizeof(struct List)))){ return -1;}
//insert values for the new elem
new_elem->number=strdup(number);
new_elem->name=strdup(name);
new_elem->time=time;
hashtable[hashval].head=new_elem;
new_elem->next=NULL;
hashtable[hashval].length++;
/* rehash existing entries if necessary */
if(hashTableSize(hashtable)>= 2*primes[PrimesIndex])
{
hashtable = expand(hashtable);
if (hashtable ==NULL){
return 0;
}
}
return 1;
}
HashTable* expand( HashTable* h )
{ printf("EXPAND \n");
HashTable* new;
List *temp;
int n;
List *node,*next;
PrimesIndex++;
int new_size= primes[PrimesIndex]; /* double the size,odd length */
if (!(new=malloc((sizeof( List*))*new_size))) return NULL;
for(n=0; n< h->length; ++n) {
for(node=h[n].head; node; node=next) {
add (new, node->number, node->name,node->time);
next=node->next;
//free(node);
}
}
free(h);
return new;
}
and the main:
int main(int argc, char *argv[])
{
char **token;
FILE *delimitedFile;
/*Here's an example of tokenizing lines from an actual file*/
/*Open file for reading ("r"), and take a FILE pointer,
which you can use to fetch lines using fgets()*/
my_hash_table = createHashTable(17);
if(my_hash_table==NULL)
{ return 1;
}
FILE * File2;
if ( ( File2=fopen(" File.txt","r")) !=NULL )
{ // File.txt format: [name number time]
int li = 0;
char *lin = (char *) malloc(MAX_LINE * sizeof(char));
while(fgets(lin, MAX_LINE, File2) != NULL)
{
token = my_linetok(lin, " ");
if(token != NULL)
{
char* number ;
char* name;
int time;
int i;
for(i = 0; token[i] != NULL; i++)
{
name=strdup(token[0]);
number=strdup(token[1]);
time=atoi(token[2]);
if (i==2)
{ int insertDone=0;
insertDone =add(my_hash_table,number,name,time);
}
}
free(name);
free(number);
free(token);
}
else
{
printf("Error reading line %s\n", lin);
exit(1);
}
}
}
else
{
printf("Error opening file \nEXIT!");
exit(0);
}
return 1;
}
The underlying problem here is that you create a hashtable with 17 buckets:
my_hash_table = createHashTable(17);
But C arrays are 0-based, and PrimesIndex starts out at 1, not 0, so inside add(), the call to hash():
int hashval=hash (hashtable,number);
will return a number between 0 and 28, not a number between 0 and 16. So at some point, an out-of-range value will be assigned to hashval, and one of the subsequent accesses indexed by hashval, e.g.
new_elem=hashtable[hashval].head;
will be reading uninitialised memory, leading ultimately to crazy pointer values like 0xffffffff surfacing later on.
Solution: Change int PrimesIndex = 1; to int PrimesIndex = 0;.
But honestly, I think there could well be other issues that I'm missing. There are:
Issues with the for loop inside the while loop in main() that I've pointed out in comments;
The dubious declaration for the number parameter to lookup_on_Clients();
The fact that sometimes the function is called lookup() and sometimes lookup_on_Clients() (as noticed by Oli);
And I don't trust that my_linetok() (which you don't show source for) works properly -- at the very least, unless it uses a static buffer, it must be allocating an array of char * in order to hold the pointers to the individual tokens, which is never freed -- a memory leak.
You don't have a room for null terminator in number. You set size of number to be equal to 10 chars, but you have 10 digits in your number and no space for \0.
EDIT:
I looked your updated code. You created hashtable of initial size = 17, but your hasval = 27. But you don't have code to extend the size of hashtable properly.
new_elem=hashtable[hashval].head;
if(hashtable[hashval].length>0) // <-- when hashval is out of array
// hashtable[hashval] can have any value of length and head (not NULL)
You don't actually show the source for add() which presumably calls lookup_on_Clients(), and the backtrace mentions lookup() instead of lookup_on_Clients(), so I can't be sure, but here's my diagnosis:
The backtrace says list = 0xffffffff -- that's definitely not a valid address, so it's probably the list->name access that is causing the SIGSEGV.
I'm also bothered by the fact that the number parameter to lookup_on_Clients() is declared as char number[10] and yet gdb shows it contains a 10-digit number -- that suggests that the variable holding the argument for this is declared the same way, meaning that there's no room for a terminating 0 byte. And the fact that you're calling strcmp() on it means that you are treating number as nul-terminated string, so the variable holding the argument that gets passed to lookup_on_Clients() as number (possibly a local variable declared in add()?) should be declared as an array with size at least 11 to avoid crashes. You're safe if add() just passes its own number argument straight through, since that is dynamically allocated to be large enough via strdup() in main(), but I would nevertheless change the declaration on lookup_on_Clients().