I want to search for a date (which is a struct) in an array of dates to see if it is in it. This is the first time I am using bsearch
and it always returns the same result, 0
, whereas it should either return null
or a pointer to the date found. I am using the same comparing function I used to sort the array and the sorting works fine. I'm guessing if the function returns `0 it means it has found the date in the array. What have I done wrong? If the mistake is not obvious I can post the full code.
#define MIN_SIZE 0
#define MAX_SIZE 10000
#define MAX_MONTH_STR 9
#define SWAP 1
#define NO_SWAP -1
#define EQUAL 0
//Define a struct data type
typedef struct
{
//Declaration of struct members
char* month;
int day;
int year;
}date;
//Method to allocate memory for a list of date structures
date* allocateStruct(int size)
{
//Declaration of variables
date *array;
int i;
//Allocate memory for array to store 'size' many 'date' struct data types
array = malloc(size*sizeof(date));
//For-loop to allocate memory for each struct's members and initialize them to zero
for (i=0; i<size; i++)
{
array[i].month = calloc(MAX_MONTH_STR,sizeof(char));
array[i].day = (int) calloc(1,sizeof(int));
array[i].year = (int) calloc(1,sizeof(int));
}
return array;
}
//Method to free memory allocated
void freeStruct(date* array, int size)
{
//Declaration of variable
int i;
//For-loop to free up struct members
for (i=0; i<size; i++)
{
free(array[i].month);
free(&array[i].day);
free(&array[i].year);
}
//Free up structs
free(array);
}
//Method to compare two dates
int cmpDates (const void *a, const void *b)
{
//Declaration and dereference of variables
date first = *(date*)a;
date second = *(date*)b;
int y_result, m_result, d_result;
//Calculate results
y_result = second.year-first.year;
m_result = second.month-first.month;
d_result = second.day-first.day;
//If-statements to determine whether to swap dates based on year
//If-statement to determine whether both years are in 90s group
if (first.year>=90 && first.year<=99 && second.year>=90 && second.year<=99)
{
//If-statement to determine whether years are equal
if (y_result!=0)
{
return (y_result);
}
}
//Else-if-statement to determine whether both years are in 00-12 group
else if (first.year>=0 && first.year<=12 && second.year>=0 && second.year<=12)
{
//If-statement to determine whether years are equal
if (y_result!=0)
{
return (y_result);
}
}
//Otherwise the two years belong to different year groups
else
{
//If-statement to determine whether first year belongs to 00-12 group
if (first.year>=0 && first.year<=12)
{
return NO_SWAP;
}
else
{
return SWAP;
}
}
//If-statement to determine whether to swap dates based on months
if (m_result!=0)
{
return m_result;
}
//If-statement to determine whether to swap dates based on days
if (d_result!=0)
{
return d_result;
}
//If dates are exactly the same
return EQUAL;
}
enum months {
January=1,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
int main()
{
//Declaration of variables
int n; //number of dates in array
date* date_list; //array of dates
date *key_date; //date to search for
date *q_result; //result of search
//Read input
do
{
//printf("Enter number of dates you want to enter (between 1 and 10000):\n");
scanf("%d", &n);
}while(n<MIN_SIZE || n>MAX_SIZE);
//Allocate memory for an array of n dates
date_list = allocateStruct(n);
//For-loop to store values in 'date_list'
for (i=0; i<n; i++)
{
//printf("Enter the date (month day year) in the following format <text number number>:");
scanf("%s", date_list[i].month);
scanf("%d", &date_list[i].day);
scanf("%d", &date_list[i].year);
}
//Allocate memory for one date
key_date = allocateStruct(1);
//Read date for query
//printf("Enter date you want to query:");
scanf("%s", key_date->month);
scanf("%d", &key_date->day);
scanf("%d", &key_date->year);
//Sort the array with built-in function qsort
qsort(date_list, n, sizeof(date), cmpDates);
//Print list of sorted dates
for (i=0; i<n; i++)
{
//printf("Enter the date (month day year) in the following format: text number number");
printf("%s ", date_list[i].month);
printf("%d ", date_list[i].day);
printf("%02d\n", date_list[i].year); //need & ?
}
//Query with bsearch --> I TRIED BOTH OF THESE LINES BUT THE RESULT WAS THE SAME
q_result = (date*) bsearch(&key_date, date_list, n, sizeof(date), cmpDates);
// q_result = bsearch(&key_date, date_list, n, sizeof(date), cmpDates);
//Printing answer to query
if(q_result!=NULL)
{
printf("Yes in list");
}
else
{
printf("No not in list");
}
}
Here is code I assembled based on yours that seems to work. I've not stress tested it.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define MIN_SIZE 1
#define MAX_SIZE 10000
#define MAX_MONTH_STR 10
#define EQUAL 0
#define LESS_THAN -1
#define MORE_THAN +1
typedef struct
{
char *month;
int day;
int year;
} date;
static
date *allocateStruct(int size)
{
date *array = malloc(size * sizeof(date));
if (array == 0)
exit(1);
for (int i = 0; i < size; i++)
{
array[i].month = calloc(MAX_MONTH_STR, sizeof(char));
if (array[i].month == 0)
exit(1);
}
return array;
}
static
void freeStruct(date *array, int size)
{
for (int i = 0; i < size; i++)
{
free(array[i].month);
}
free(array);
}
static inline int normalize_year(int year)
{
return (year < 50) ? year + 2000 : year + 1900;
}
static inline int month_number(const char *name)
{
static const char *months[] =
{
"",
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December",
};
for (int i = 1; i <= 12; i++)
if (strcmp(name, months[i]) == 0)
return i;
fprintf(stderr, "Invalid (unrecognized) month name <<%s>>\n", name);
exit(1);
/*NOTREACHED*/
}
static
int cmpDates(const void *a, const void *b)
{
date first = *(date *)a;
date second = *(date *)b;
int y1 = normalize_year(first.year);
int y2 = normalize_year(second.year);
if (y1 < y2)
return LESS_THAN;
else if (y1 > y2)
return MORE_THAN;
int m1 = month_number(first.month);
int m2 = month_number(second.month);
if (m1 < m2)
return LESS_THAN;
else if (m1 > m2)
return MORE_THAN;
if (first.day < second.day)
return LESS_THAN;
else if (first.day > second.day)
return MORE_THAN;
else
return EQUAL;
}
int main(void)
{
int n;
date *date_list;
date *key_date;
date *q_result;
do
{
if (scanf("%d", &n) != 1)
exit(1);
} while (n < MIN_SIZE || n > MAX_SIZE);
date_list = allocateStruct(n);
printf("Input data:\n");
for (int i = 0; i < n; i++)
{
scanf("%s", date_list[i].month);
scanf("%d", &date_list[i].day);
scanf("%d", &date_list[i].year);
printf("%.2d: %.2d %s %.2d\n", i, date_list[i].day, date_list[i].month, date_list[i].year);
}
qsort(date_list, n, sizeof(date), cmpDates);
printf("Sorted:\n");
for (int i = 0; i < n; i++)
printf("%.2d: %.2d %s %.2d\n", i, date_list[i].day, date_list[i].month, date_list[i].year);
key_date = allocateStruct(1);
scanf("%s", key_date->month);
scanf("%d", &key_date->day);
scanf("%d", &key_date->year);
printf("Search: %.2d %s %.2d\n", key_date->day, key_date->month, key_date->year);
q_result = (date *) bsearch(key_date, date_list, n, sizeof(date), cmpDates);
if (q_result != NULL)
{
printf("Yes (%.2d %s %.2d) in list (%.2d %s %.2d)\n",
key_date->day, key_date->month, key_date->year,
q_result->day, q_result->month, q_result->year);
}
else
{
printf("No (%.2d %s %.2d) in list\n",
key_date->day, key_date->month, key_date->year);
}
freeStruct(date_list, n);
freeStruct(key_date, 1);
return 0;
}
Sample data:
16
November 26 00
February 18 12
January 19 08
March 22 11
October 08 05
December 22 10
November 08 01
May 21 10
July 10 92
October 06 91
November 30 93
April 25 90
March 21 90
September 18 97
June 23 97
July 19 98
November 29 93
The last line of the file is the date to search for. The November 29 93
is not in the list, but change 29
to 30
and the date is in the list. In both cases, the code reports this correctly.
Sample output:
Input data:
00: 26 November 00
01: 18 February 12
02: 19 January 08
03: 22 March 11
04: 08 October 05
05: 22 December 10
06: 08 November 01
07: 21 May 10
08: 10 July 92
09: 06 October 91
10: 30 November 93
11: 25 April 90
12: 21 March 90
13: 18 September 97
14: 23 June 97
15: 19 July 98
Sorted:
00: 21 March 90
01: 25 April 90
02: 06 October 91
03: 10 July 92
04: 30 November 93
05: 23 June 97
06: 18 September 97
07: 19 July 98
08: 26 November 00
09: 08 November 01
10: 08 October 05
11: 19 January 08
12: 21 May 10
13: 22 December 10
14: 22 March 11
15: 18 February 12
Search: 29 November 93
No (29 November 93) in list
I think it is a lousy idea to store the month name in the date structure. If you are going to store a pointer, you could store a pointer to one of a fixed array of strings representing the month names. You'd read the input name into an array, then find the canonical pointer from a list of the month names. You'd store the names so that the address of January
is before the address for February
, etc, so that you could compare pointers and come up with the correct answer. However, I've not implemented that. In fact, I think storing month names in the structure is a bad idea; I'd store a month number. I also think storing just two digits of the year is a lousy decision. Maybe you weren't aware of the Y2K bug fixing work that was necessary because people tried to cut corners and only stored two digits for years instead of four digits, but it was messy. It is far, far simpler just to store the year number. You aren't even saving any space since you're using 4 bytes (an int
is usually 4 bytes these days) to store the 2-digit year. If you have year as a 4-digit number, and month and day as 2-digit numbers, then yyyy * 10000 + mm * 100 + dd
gives you an 8-digit integer that can be sorted trivially. If you need to compute the number of days between two dates, then the broken-down format isn't as convenient as a count of the days since a reference date (such as 1900-01-01 was day 1). You can take the difference between two such date values to get the number of days between them.