Member Avatar for Member #506555

Hello all,
I hope that someone can help me with this,
I am trying to add an element to a dictionary, and in order to do such, I am using a binary search mechanism to find the correct place in the dictionary. My code compiles fine, but when I run it, it will hang. When I use a debugger, I get that the code gets stuck at the line "if ( strcmp(key, pDict->wordList[mid]) > 0){" (line 56 in the code here) when my third word is going through.

Can anyone help me out on this? my code is below:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_DICT_SIZE 10

typedef struct {
    char **wordList;
    unsigned long wordListSize;
    unsigned long count;
  } dictionary;

int initDict(dictionary ** pDict) {
     *pDict = (dictionary*) malloc(sizeof(dictionary)); 
    if (NULL != pDict) { /* if malloc doesn't fail*/
        (*pDict)->count = 0;
        (*pDict)->wordListSize = INITIAL_DICT_SIZE; 
        (*pDict)->wordList = malloc(sizeof(char*)*INITIAL_DICT_SIZE);
        if (NULL == (*pDict)->wordList) { /* free it if it does*/
            free(pDict);
            return -1;
        }
    }
    return 0;
}

void destroyDict(dictionary* pDict) { 
    free(pDict->wordList);
    free(pDict);
}


int printDict(dictionary* pDict) {
        char** index;
        for (index = pDict->wordList; index != &pDict->wordList[pDict->count]; ++index) {
                printf("%s \n", *index); /*maybe not the most effective way*/
        }

        return 0;
}

static int growDict(dictionary* pDict, unsigned long newS, void ** newWs){
    * newWs = realloc(pDict->wordList, sizeof(char*)*newS); /*change old to old + new*/
    if (NULL == newWs) { /*if that fails */
            return 1;
        } else { /*update variables */
            pDict->wordListSize = newS; 
            pDict->wordList = (char**)newWs;
        }
    return 0;

}

unsigned long binarysearch(dictionary* pDict, char* key, unsigned long first, unsigned long last){
    while (first <= last){
	unsigned long mid = (last-first) / 2; /*find midpt*/
	if ( strcmp(key, pDict->wordList[mid]) > 0){ /*above midpoint*/
	    first = mid + 1;  
	}else if (strcmp (key, pDict->wordList[mid]) < 0){ /*below midpoint*/
	    last = mid - 1;  
	} else if (strcmp (key, pDict->wordList[mid]) == 0){/*we've got it*/
	    return mid;
    	}
    }
    return -(first + 1); /*fail*/
}

int addWordDict(dictionary* pDict, char* word) {
    unsigned long count = pDict->count;
    if (count >= pDict->wordListSize) { 
        unsigned long newSize = pDict->wordListSize + sizeof(pDict->wordList); 
	void * newWords;
        growDict(pDict, newSize, &newWords); /* grow the dictionary for me*/
    }

	/*sort method:*/
	/*handle the case where word should be the first element*/
    if (pDict->wordList[0] == NULL || strcmp(word, pDict->wordList[0]) > 0){
	pDict->wordList[0] = word;
    }else{     /*else search for correct location*/
	unsigned long front = 0;
	unsigned long back = pDict->count;
	/*input it there*/
	pDict->wordList[binarysearch (pDict, word, front, back)] = word;
	    
    }
	/*back to regularly scheduled code*/
    ++pDict->count; /*keep count up to date*/


    return 0; 
}

int verifyDict(dictionary * pDict, char* match){
    unsigned long index;
    for (index = 0; index < pDict->count; index++) {
	if (!strcmp(pDict->wordList[index], match)){ /* strcmp returns 0 on success*/
	  return 0;	  
	}
    }
    return 1;
}

int main (/*int argc, char* argv[]*/) {
   dictionary dict;
   dictionary* pdict = &dict;
   initDict(&pdict);

   addWordDict(pdict, "Hello");
   addWordDict(pdict, "World");
   addWordDict(pdict, "Sorry");
   addWordDict(pdict, "Excuses");

   printDict(pdict);
 
   verifyDict(pdict, "Hello");

   destroyDict(pdict);

return 0;
}

sorry for the long post, but I could really use another set of eyes on this.

Dani AI

Generated

The hang/segfault is a mix of unsigned underflow, an incorrect midpoint calculation, and several allocation/checking bugs. was on the right track with using count-1 for the upper bound, and correctly pointed out the wrap-around when you subtract 1 from 0 (unsigned underflow -> 4294967295). Fix these and the binary search will stop indexing wildly out of range.

Practical fixes to apply (short, focused):

  • Use signed indices (e.g. long) for first/last and make the function return a signed type. The code currently returns a negative sentinel but the function is unsigned long, which produces huge positive indices. Also compute mid as first + (last - first)/2 (not (last - first)/2) so mid is an absolute index, not an offset. Example binary search pattern:
long binarysearch(char **list, const char *key, long first, long last) {
    while (first <= last) {
        long mid = first + (last - first) / 2;
        int cmp = strcmp(key, list[mid]);
        if (cmp > 0) first = mid + 1;
        else if (cmp < 0) last = mid - 1;
        else return mid;
    }
    return -(first + 1); // insertion point encoded as negative
}
  • init/grow: check the result of malloc/realloc correctly (test the returned pointer, not the pointer-to-pointer), free the same pointer you allocated (free(*pDict), not free(pDict)), and initialize the wordList slots to NULL (use calloc or memset). Use the standard safe realloc idiom:
char **tmp = realloc(pDict->wordList, newSize * sizeof *tmp);
if (!tmp) return -1;
pDict->wordList = tmp;
pDict->wordListSize = newSize;
  • add/insert: the current head test uses the wrong comparison sign and overwrites entries instead of shifting to make room. After finding the insertion index (from binarysearch), if negative decode insertion point, memmove the tail to the right, then insert. Decide and document ownership (store strdup(word) or require caller ownership).

Run with bounds-checking tools (Valgrind or AddressSanitizer) after these fixes; they will quickly show the unterminated reads/writes that cause the segfault. For reference on strcmp/realloc: strcmp man page and realloc man page.

Recommended Answers

All 3 Replies

else{     /*else search for correct location*/
    unsigned long front = 0;
    unsigned long back = pDict->count;
    /*input it there*/
    pDict->wordList[binarysearch (pDict, word, front, back)] = word;
        
    }

Should be

else{     /*else search for correct location*/
    unsigned long front = 0;
    unsigned long back = pDict->count - 1; //HERE is the change
    /*input it there*/
    pDict->wordList[binarysearch (pDict, word, front, back)] = word;
        
    }
Member Avatar for Member #506555

Thank you for the prompt reply,
I implemented the change you suggested, however, when I run it I still Seg Fault. Wnhen I backtrack it through gdb I get

(gdb) bt
#0  0xb7ee942a in strcmp () from /lib/libc.so.6
#1  0x08048643 in binarysearch (pDict=0x804b008, key=0x8048951 "Sorry", first=0, last=4294967295) at dictionary.c:51
#2  0x08048762 in addWordDict (pDict=0x804b008, word=0x8048951 "Sorry") at dictionary.c:78
#3  0x0804882e in main () at lab5.c:15

last=MAX_UNSIGNED_INT?
Sounds like you subtracted 1 from 0, as there are not that many words in a dictionary.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.