So I'm trying to use the bsearch function in C to do a binary search in an array of strings. For some reason, it always returns NULL. For the compare function of bsearch I didn't use strcmp, I made my own which, surprisingly, works for my manual implementation of the binary search function. I'll explain in a bit, anyway, here's my main code:
int main(){
int n;
scanf("%d", &n);
if(n<=0) qerror();
char ** words = (char **)calloc(n, sizeof(char *));
if(words == NULL) qerror();
int i = 0;
for(;i<n;i++){
words[i] = (char *)calloc(MAX, sizeof(char));
if(words[i] == NULL) qerror();
scanf("%s", words[i]);
}
char seek_word[MAX];
scanf("%s", seek_word);
char ** c = (char **)bsearch(seek_word, words, n, sizeof(char *), &b_compare_words);
if(c == NULL) printf("-1\n");
else printf("%d\n", c - words);
//printf("%d\n", bin_search_string(words, n, seek_word)); //manuelna binarna pretraga
free(words);
return 0;
}
The first part of this code is just creating an array of strings (named words) that contains N strings of the length MAX (which is a macro defined at the beginning). I've tested this part and it works, so that's not the problem.
Afterwards, the seek_word is inputted, which is just the word that we're trying to find in 'words'. The I call the bsearch and as a compare function I used the function that I've created myself. Here's what it looks like:
int compare_words(char * a, char * b){
int i = 0;
while(a[i] && b[i]){
char ac = tolower(a[i]), bc = tolower(b[i]);
if(ac < bc) return -1;
else if (ac > bc) return 1;
i++;
}
return 0;
}
int b_compare_words(const void * a, const void * b){
return compare_words((char *)a, (char *)b);
}
The actual comparison work is done in the compare_words function which I've called from the b_compare_words function where I've just casted the void pointers into char pointers.
And whatever I test this on, I get -1 (meaning the pointer returned by bsearch is NULL).
Now here's the interesting part. Apart from this I've made my own binary search function that does the same thing and it works (you can see the part that I commented out below). The function is called bin_search_string and it looks like this:
int bin_search_string(char ** a, int n, char * x){
int l_index = 0;
int r_index = n-1;
int check_index;
while(l_index <= r_index){
check_index = l_index + (r_index - l_index)/2;
int test = compare_words(x, a[check_index]);
if(test == 0) return check_index;
else if(test < 0) l_index = check_index + 1;
else r_index = check_index - 1;
}
return -1;
}
As you can see, this function uses the same compare_words function that I've also passed to bsearch. But when I test my program using my own function, it really works (returns the index where the string was found).
So, any ideas why the bsearch won't work?
Thanks in advance.
Related
int function(char * WordList[], int nSize, char * param_str)
{
int i = 0;
int bFlag = 0;
while(i < nSize && !bFlag)
{
if(WordList[i] == param_str){
return bFlag = 1;
}
i++;
}
return bFlag;
i tried using strcmp and made it work [if(strcmp(WordList[i], param_str) == 0)] but im wondering whats wrong with my current code.
WordList[i] is a pointer
param_str is another pointer.
WordList[i] == param_str tests if the 2 pointers point to the same location.
What these pointers reference is not considered. WordList[i] == param_str is not a string compare, just an address compare.
strcmp(a,b) compares the data at locations a and b.
To do the same, without strcmp(), form your own ...
int my_strcmp(const char *a, const char *b) {
// Pseudo code
while what `a` points to is the same as what `b` points to and `*a` is not 0
advance both pointers
return the sign of the difference of `*a` and `*b`.
}
#include <stdio.h>
int main()
{
int N, i, j, k, z, v, x, c;
printf("Determine the number of your words: \n");
scanf("%d", &N);
char dict[N][50];
char tmp[50];
for(i=0; i<N; i++)
{
printf("Determine your %d. word: \n", i+1);
scanf("%49s", &dict[i]);
}
for(j=1; j<N; j++)
{
k=j;
z=0;
while(dict[j][z]!= '\0')
{
tmp[z]=dict[j][z];
z=z+1;
}
while((dict[j][1]<dict[k-1][1]) && (k>0))
{
v=0;
while(dict[k][v]!= '\0')
{
dict[k][v] = dict[k-1][v];
v= v+1;
}
k=k-1;
}
x=0;
while(dict[k][x]!= '\0')
{
dict[k][x]=tmp[x];
x=x+1;
}
}
for(c=0; c<N; c++)
{
printf("%s \n", dict[c]);
}
return 0;
}
So as a junior CE student our task is to write a C code that sort the strings in a 2D array according to their first characters position in the alphabet by only using the <stdio.h> library and this is the result I get is below(I know the variables are not really bearable but I'm in a little bit of a hurry):
Determine the number of your words:
5
Determine your 1. word:
LION
Determine your 2. word:
FISH
Determine your 3. word:
TIGER
Determine your 4. word:
SNAKE
Determine your 5. word:
SEALION
LION
FISH
TIGER
SEALI
SNAKE
I get no errors just the result is faulty and I do not wish any answers. But I would be glad to be enlightened about the reasons I get this results
Limited to stdio.h
If you are limited to using what is provided in stdio.h, then you simply have to write similar string handling functionality to what you would require from string.h. For sorting, unless you want to use a partitioning sort like quicksort or mergesort, you would be limited to a nested loop sort -- which are simpler to write -- but much much slower. (but for a 1000 or so strings, it doesn't matter, 100,000 - then there will be a perceivable difference)
To compare strings, you can write a simple strcmp() like function that requires no additional header, e.g.
/** string compare without string.h */
int strcmp_nohdr (const char *a, const char *b)
{
if (!a && !b) return 0;
if ( a && !b) return 1;
if (!a && b) return -1;
for (; *a && *a == *b; a++, b++) {}
return (*a > *b) - (*a < *b);
}
If you are not declaring an additional set of pointers to sort, then you will need to copy strings between the rows (1D arrays) in your 2D array. You can write a simple strcpy() like function that needs no header as:
/** string copy wihout string.h */
char *strcpy_nohdr (char *dest, const char *src)
{
char *p = dest;
do {
*p++ = *src;
} while (*src++);
return dest;
}
Finally, the only other thing you need is a sort function. (you will not be able to write a quick qsort() replacement) The meager Bubblesort will do. You can write a function to sort your 2D array of strings as:
#define NCHR 50
/** bubble sort of strings in 'a' (highly inefficient) */
void bubblesort2D (char (*a)[NCHR], size_t n)
{
size_t i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < (n-1); j++) {
if (strcmp_nohdr(a[j], a[j + 1]) > 0) {
char temp[NCHR];
strcpy_nohdr (temp, a[j + 1]);
strcpy_nohdr (a[j + 1], a[j]);
strcpy_nohdr (a[j], temp);
}
}
}
}
If you put those altogether in a short example (the same used before with qsort(), you would have:
#include <stdio.h>
#define NCHR 50
/** string compare without string.h */
int strcmp_nohdr (const char *a, const char *b)
{
if (!a && !b) return 0;
if ( a && !b) return 1;
if (!a && b) return -1;
for (; *a && *a == *b; a++, b++) {}
return (*a > *b) - (*a < *b);
}
/** string copy wihout string.h */
char *strcpy_nohdr (char *dest, const char *src)
{
char *p = dest;
do {
*p++ = *src;
} while (*src++);
return dest;
}
/** bubble sort of strings in 'a' (highly inefficient) */
void bubblesort2D (char (*a)[NCHR], size_t n)
{
size_t i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < (n-1); j++) {
if (strcmp_nohdr(a[j], a[j + 1]) > 0) {
char temp[NCHR];
strcpy_nohdr (temp, a[j + 1]);
strcpy_nohdr (a[j + 1], a[j]);
strcpy_nohdr (a[j], temp);
}
}
}
}
int main (void) {
char strings[][NCHR] = { "zebra", "alligators", "frogs", "racoons", "opossums" };
size_t n = sizeof strings / sizeof *strings;
bubblesort2D (strings, n);
for (size_t i = 0; i < n; i++)
puts (strings[i]);
}
Example Use/Output
The results are the same as before:
$ ./bin/sort_2d_strings_stdio
alligators
frogs
opossums
racoons
zebra
Since I originally wrote the qsort() answer, I'll leave it below as the way you would want to handle sorting (whatever array you are sorting) in practice.
In Practice Use qsort()
C provides qsort() that will sort arrays of any object based on the compare function you write to tell qsort() how elements of the array are to be compares. With strings, you just want to return the result of strcmp(). Each argument to the compare function ia a Pointers to an element of your array. Since it is a void* pointer, you must cast the pointer back to the correct type.
Recall in C that a 2D array is simply an array of 1D arrays. In your case it is a array of 50-character arrays. A pointer to an array of 50-chars has the type char (*)[50], so your qsort() compare function can be written as follows:
int cmpstr50 (const void *a, const void *b)
{
const char *pa = *(char (*)[50])a,
*pb = *(char (*)[50])b;
return strcmp (pa, pb);
}
Where each of the parameters is a pointer to an array of char [50]. Nothing could be easier. Moreover the algorithm used by qsort() will be orders of magnitude faster than what you write with nested loops.
A complete example for your case would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmpstr50 (const void *a, const void *b)
{
const char *pa = *(char (*)[50])a,
*pb = *(char (*)[50])b;
return strcmp (pa, pb);
}
int main (void) {
char strings[][50] = { "zebra", "alligators", "frogs", "racoons", "opossums" };
size_t n = sizeof strings / sizeof *strings;
qsort (strings, n, sizeof *strings, cmpstr50);
for (size_t i = 0; i < n; i++)
puts (strings[i]);
}
Where after writing your compare function, all it takes to sort the array of strings (or array of anything) is a single call to qsort():
qsort (strings, n, sizeof *strings, cmpstr50);
Much simpler than trying to reinvent the wheel.
Example Use/Output
$ ./bin/qsort_2d_strings
alligators
frogs
opossums
racoons
zebra
Let me know if you have further questions.
This comparison is incorrect:
while((dict[j][1]<dict[k-1][1]) && (k>0))
First, if you want to compare the first character of each word, use [0] instead of [1]. This is because array indexes are zero-based in C. Second, put the k>0 comparison first, before you use k-1 as an index to the array. This will take advantage of "short circuiting" to make sure you're not using an invalid index to the array. Result:
while(k > 0 && dict[j][0] < dict[k-1][0])
Another issue is that you are not making sure your strings are null terminated when you copy them. I believe this is the reason your "SEALION" is coming out "SEALI". For example, this loop:
x=0;
while(dict[k][x]!= '\0')
{
dict[k][x]=tmp[x];
x=x+1;
}
You're checking for null on the destination instead of the source. Also, you stop copying at the null character, but you actually need to copy that too, or at least tack a null at the end:
x=0;
while(tmp[x]!= '\0')
{
dict[k][x]=tmp[x];
x=x+1;
}
dict[k][x] = '\0';
Take a close look at each of your string copy loops for such errors.
So, I have to write down a recursive function for checking how much times a character (c) occurs in a string (array) : note that the function MUST BE recursive. To be honest this is the hardest thing I've had to face since I started, so this is taking very long :| Here is the code (language is C) :
#include <stdio.h>
#include <time.h>
#define MAX 5
int charOccur (char [], int);
int main() {
char array[MAX+1]={'a', 'x', 'c', 'f', 'r'};
charOccur (array, MAX);
return 0;
}
int charOccur (char arr[], int dim){
int i, flag=0;
if (arr[0] == 'c'){
flag = 1;
return flag;
} else {
for (i=1; i<dim; i++){
if (arr[i] == 'c'){
flag++;
charOccur (arr, dim);
}
} return flag;
}
}
int occur(char *s, char c)
{
if (!*s) // if (*s == '\0')
return (0); // s is empty, do the character we're looking for does not exist
else if (*s == c)
return (1 + occur(s + 1, c));
else
return (occur(s + 1, c));
}
In the second case, we found our character, so we count 1 plus the rest of the input fed in our recursive function.
In the third case, the character pointed by s is neither '\0' nor c, so we keep going until we reach the end of the string.
In boths case, we use pointer arithmetic (s + 1), to change the character which is being pointed.
You should read a bit on memoization (it's not a typo). This wikipedia link is a good starting point; a more practical example is this one, which says:
Memoization means recording the results of earlier calculations so that we don’t have to repeat the calculations later.
In other words, in your code you might consider passing a counter to your function to store the result of the computation, before passing it to itself for the next recursion.
The example is shown in Ruby - it will help you understand the concept, rather than copy-pasting it as a solution.
And don't forget that in order to understand recursion, you must first understand recursion.
Consider a recursion that divides by 2 in each call. It does not reduce the number of calls, but limits the call depth to O(log2(n)).
#include <stdio.h>
#include <string.h>
static size_t Occurrence_helper(const char *s, size_t length, int ch) {
if (length > 1) {
size_t left = length / 2;
size_t right = length - left;
return Occurrence_helper(s, left, ch)
+ Occurrence_helper(s + left, right, ch);
} else if (length > 0) {
return *s == ch;
} else {
return 0;
}
}
size_t Occurrence(const char *s, int ch) {
return Occurrence_helper(s, strlen(s), ch);
}
Sample
int main(void) {
int ch = 'y';
const char *s = "xyzzy";
printf("'%c' occurs %zu times in \"%s\".\n", ch, Occurrence(s, ch), s);
return 0;
}
// Ouptut
'y' occurs 2 times in "xyzzy".
// 01 ) where 'c' is a constant
int charOccur (char arr[], int dim){
if(!dim) return 0;
dim--;
return ('c'==arr[dim])+charOccur (arr,dim);
}
// 02 ) passing c as a second parameter
int charOccur (char arr[],char c , int dim){
if(!dim) return 0;
dim--;
return (c==arr[dim])+charOccur (arr,c,dim);
}
I'm a java student who's currently learning about pointers and C.
I tried to make a simple palindrome tester in C using a single array and pointer arithmetic.
I got it to work without a loop (example for an array of size 10 :*(test) == *(test+9) was true.
Having trouble with my loop. School me!
#include<stdio.h>
//function declaration
//int palindrome(int *test);
int main()
{
int output;
int numArray[10] = {0,2,3,4,1,1,4,3,2,0};
int *ptr;
ptr = &numArray[0];
output = palindrome(ptr);
printf("%d", output);
}
//function determine if string is a palindrome
int palindrome(int *test) {
int i;
for (i = 0; i <= (sizeof(test) / 2); i++) {
if (*(test + i) == *(test + (sizeof(test) - i)))
return 1;
else
return 0;
}
}
The Name of the array will itself acts as a pointer to an first element of the array, if you loose the pointer then there is no means for you to access the element of the array and hence you can send just the name of the array as a parameter to the function.
In the palindrome function:
you have used sizeof(test)/2. what happens is the address gets divided which is meaningless and hence you should not use that to calculate the mid element.
sizeof the pointer will be the same irrespective of the type of address that gets stored.
Why do you copy your pointer in another variable?
int *ptr;
ptr = &numArray[0];
Just send it to you function:
palindrome(numArray);
And sizeof(test) give you the memory size of a pointer, it's not what you want. You have to give the size in parameter of your function.
int palindrome(int *test, int size){
...
}
Finally your code must look like this:
#include<stdio.h>
int palindrome(int *test, int size);
int main()
{
int output;
int numArray[10] = {0,2,3,4,1,1,4,3,2,0};
output = palindrome(numArray, 10);
printf("%d", output);
}
//function determine if string is a palindrome
int palindrome(int *test, int size) {
int i;
for (i = 0; i < size / 2; i++) {
if (*(test + i) != *(test + (size - 1) - i))
return 0;
}
return 1;
}
Here is a simple function that converts a string to an integer.
int str2int(char *str)
{
int ret = 0;
char *c;
for (c = str; (*c != '\0') && isdigit(*c); ++c)
ret = ret*10 + *c - '0';
return ret;
}
As an exercise, I'd like to write a recursive function that does the same thing. This is what I came up with.
int str2int2(char *c, int *i)
{
if (*c == '\0' || !isdigit(*c))
return *i;
*i = *i * 10 + *c - '0';
return str2int2(c + 1, i);
}
.
.
int i = 0;
.
... str2int2(str, &i);
Is there a way to write the recursive function without using the extra int* argument?
Sure, it's easy enough, but you need to write two functions, one with an accumulator, like this:
int str2int_rec(char *c, int accum)
{
if (!c || !*c || !isdigit(*c))
return accum;
return str2int_rec(c + 1, accum * 10 + (*c - '0'));
}
int str2int(char *c)
{
return str2int_rec(c, 0);
}
Well, you could hide the functionality from the person using the function. So you will have a function named int str2int(char *str) which will call int str2int(char *c, int *i) thereafter.
It's how I've done it in the past.
I think you may use horner scheme in order not to keep any 'i'.
You must reverse the string (yeah quit ugly) and then you can simply use:
int str2int (char* str)
{
if (*str+1)
{
return 10*str2int(str+1)+(*str-'0');
}
return 0;
}
One way could involve passing the length of the digit as an argument so we can read backwards efficiently:
int strtoi(char *c, size_t l)
{
return l ? c[l-1] - '0' + 10 * strtoi(c, l - 1) : 0;
}
Then call it like this:
int i = strtoi("432", 3);
Or:
char *c = "432";
int i = strtoi(c, strlen(c));
But it's not always optimal to bother with the length of a string. Plus, if a string has characters after a number, we'd have to factor that in manually, because this function won't do it for us. We can't (shouldn't) use strlen() inside our function to avoid having to pass arguments, because that can cause a considerable slowdown to recalculate the string's length every time. Surely there must be a way to do this from the beginning, even if we have to bring out the heavy artillery:
int strtoi(char *c)
{
if(!isdigit(*c)) return 0;
int i = strtoi(c + 1);
return i + pow(10, (int)(log(i + 1)/log(10)) + (i != 0)) * (*c - '0');
}
And no, that (int) cast isn't optional. Basically, all of that math calculates the power of 10 we need to multiply our current digit by, based on the number returned by the last recursive call.
I understand this is probably a learning exercise, but recursion is not the be-all, end-all of programming. In some languages, and for some tasks, it is what some, myself included, would call beautiful, but from the looks of it this is not one of those cases.