Garbage value with the string comparison - c

#include<stdio.h>
#include<string.h>
void* lsearch(void* key,void* base,int size,int elemSize){
int i = 0;
for(i = 0; i < size; i++){
void* elemAddr = ((char*)base + (i * elemSize));
if(memcmp(key,elemAddr,elemSize)==0){
return elemAddr;
}
}
return NULL;
}
int main(){
char* a[] = {"Hello","Good","Morning","Ladies"};
int size = sizeof(a)/sizeof(a[0]);
char* key = "Good";
char* search = (char*)lsearch(&key,&a,size,sizeof(char*));
if(search == NULL){
printf("\n key value not found!! \n");
return -1;
}
printf("\n search : %s \n",search);
return 0;
}
OUTPUT:
search : �#
I'm trying to do a lsearch on the strings in an array... The string matches, but I get a garbage value printed.. Why is it so..

Fixed:
A few things were wrong, here are the most notable issues:
arithmetic on a void*: illegal,
the length of a some_type array[] = {...}; is sizeof(array) (no fancy divisions whatsoever),
inappropriate choice of interfaces: when you're just reading, consider using const parameters. Also, make sure the parameters' types are consistent with your arguments (e.g. base is a const char* [] and NOT a char*). Finally, to look for a key in an array of strings you need a key, a handle to the array and the length of the array, that's all.
Run It Online
#include<stdio.h>
#include<string.h>
const char* lsearch(const char* key, const char* base[], const size_t size){
size_t i = 0;
const size_t max_len = strlen(key); // strlen(), that's how you compute the length of a string (you might add `+1` to include `\0` if you want)
for(i = 0; i < size; i++){
const char* elemAddr = base[i]; // Arithmetic on a void* is illegal in both C and C++: https://stackoverflow.com/a/3524270/865719
if(memcmp(key, elemAddr, max_len)==0) { // #TODO use strncmp(), more appropriate, and safer. In particular, if strlen(elemAddr) < strlen(key)
return elemAddr;
}
}
return NULL;
}
int main() {
// init
const char* a[] = {"Hello","Good","Morning","Ladies"};
const size_t size = sizeof(a); // size of the array -- i.e. element count
// search
const char* key = "Morning";
const char* search = lsearch(key, a, size);
// results
if(search == NULL) {
printf("\n key value not found!! \n");
return -1;
}
printf("\n search : %s \n",search);
return 0;
}
As pointed out by Jongware, a better way would be to use a string comparison function. In my opinion, the safest bet is
int strncmp(const char *s1, const char *s2, size_t n);
And you would use it as such:
// safer. in particular, if (and when) strlen(elemAddr) < strlen(key)
if(strncmp(key, elemAddr, max_len) == 0) {
return elemAddr;
}

That would do it:
printf("\n search : %s \n",*(char**)search);
You were trying to read from the right location but the compiler didn't know how to access it

Related

How do I allocate memory for a new string in a C Multiarray?

I am trying to find a way to create a dynamically allocated array of C strings. So far I have come with the following code that allows me to initialize an array of strings and change the value of an already existing index.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void replace_index(char *array[], int index, char *value) {
array[index] = malloc(strlen(value) + 1);
memmove(array[index], value, strlen(value) + 1);
}
int main(int argc, const char * argv[]) {
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
replace_index(strs, 2, "new_value");
// The above code works fine, but I can not use it to add a value
// beyond index 4.
// The following line will not add the string to index 5.
replace_index(strs, 5, "second_value");
}
The function replace_index will work to change the value of a string already include in the initializer, but will not work to add strings beyond the maximum index in the initializer. Is there a way to allocate more memory and add a new index?
First off, if you want to do serious string manipulation it would be so much easier to use almost any other language or to get a library to do it for you.
Anyway, onto the answer.
The reason replace_index(strs, 5, "second_value"); doesn't work in your code is because 5 is out of bounds-- the function would write to memory unassociated with strs. That wasn't your question, but that's something important to know if you didn't. Instead, it looks like you want to append a string. The following code should do the trick.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **content;
int len;
} string_array;
void free_string_array(string_array *s) {
for (int i = 0; i < s->len; i++) {
free(s->content[i]);
}
free(s->content);
free(s);
}
int append_string(string_array *s, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->len++;
char **resized = realloc(s->content, sizeof(char *)*s->len);
if (!resized) {
s->len--;
free(value);
return -1;
}
resized[s->len-1] = value;
s->content = resized;
return 0;
}
string_array* new_string_array(char *init[]) {
string_array *s = calloc(1, sizeof(string_array));
if (!s || !init) {
return s;
}
while (*init) {
if (append_string(s, *init)) {
free_string_array(s);
return NULL;
}
init++;
}
return s;
}
// Note: It's up to the caller to free what was in s->content[index]
int replace_index(string_array *s, int index, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->content[index] = value;
return 0;
}
int main() {
string_array *s = new_string_array((char *[]) {"help", "me", "learn", "dynamic", "strings", NULL});
if (!s) {
printf("out of memory\n");
exit(1);
}
free(s->content[2]);
// Note: No error checking for the following two calls
replace_index(s, 2, "new_value");
append_string(s, "second value");
for (int i = 0; i < s->len; i++) {
printf("%s\n", s->content[i]);
}
free_string_array(s);
return 0;
}
Also, you don't have to keep the char ** and int in a struct together but it's much nicer if you do.
If you don't want to use this code, the key takeaway is that the array of strings (char ** if you prefer) must be dynamically allocated. Meaning, you would need to use malloc() or similar to get the memory you need, and you would use realloc() to get more (or less). Don't forget to free() what you get when you're done using it.
My example uses strdup() to make copies of char *s so that you can always change them if you wish. If you have no intention of doing so it might be easier to remove the strdup()ing parts and also the free()ing of them.
Static array
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of pointer to char and initializes it with 5 elements, thus the implied [] is [5]. A more restrictive const char *strs[] would be more appropriate if one were not intending to modify the strings.
Maximum length
char strs[][32] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of array 32 of char which is initialized with 5 elements. The 5 elements are zero-filled beyond the strings. One can modify this up to 32 characters, but not add more.
Maximum capacity singleton for constant strings
static struct str_array { size_t size; const char *data[1024]; } strs;
This will pre-allocate the maximum capacity at startup and use that to satisfy requests. In this, the capacity is 1024, but the size can be any number up to the capacity. The reason I've made this static is this is typically a lot to put the stack. There is no reason why it couldn't be dynamic memory, as required.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static struct { size_t size; const char *data[1024]; } strs;
static const size_t strs_capacity = sizeof strs.data / sizeof *strs.data;
/** Will reserve `n` pointers to strings. A null return indicates that the size
is overflowed, and sets `errno`, otherwise it returns the first string. */
static const char **str_array_append(const size_t n) {
const char **r;
if(n > strs_capacity - strs.size) { errno = ERANGE; return 0; }
r = strs.data + strs.size;
strs.size += n;
return r;
}
/** Will reserve one pointer to a string, null indicates the string buffer is
overflowed. */
static const char **str_array_new(void) { return str_array_append(1); }
int main(void) {
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new())) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
return success;
}
Dynamic array
struct str_array { const char **data; size_t size, capacity; };
I think you are asking for a dynamic array of const char *. Language-level support of dynamic arrays is not in the standard C run-time; one must write one's own. Which is entirely possible, but more involved. Because the size is variable, it will probably be slower, but in the limit as the problem grows, by a constant average.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/** A dynamic array of constant strings. */
struct str_array { const char **data; size_t size, capacity; };
/** Returns success allocating `min` elements of `a`. This is a dynamic array,
with the capacity going up exponentially, suitable for amortized analysis. On
resizing, any pointers in `a` may become stale. */
static int str_array_reserve(struct str_array *const a, const size_t min) {
size_t c0;
const char **data;
const size_t max_size = ~(size_t)0 / sizeof *a->data;
if(a->data) {
if(min <= a->capacity) return 1;
c0 = a->capacity < 5 ? 5 : a->capacity;
} else {
if(!min) return 1;
c0 = 5;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) {
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** Returns a pointer to the `n` buffered strings in `a`, that is,
`a + [a.size, a.size + n)`, or null on error, (`errno` will be set.) */
static const char **str_array_buffer(struct str_array *const a,
const size_t n) {
if(a->size > ~(size_t)0 - n) { errno = ERANGE; return 0; }
return str_array_reserve(a, a->size + n)
&& a->data ? a->data + a->size : 0;
}
/** Makes any buffered strings in `a` and beyond if `n` is greater then the
buffer, (containing uninitialized values) part of the size. A null on error
will only be possible if the buffer is exhausted. */
static const char **str_array_append(struct str_array *const a,
const size_t n) {
const char **b;
if(!(b = str_array_buffer(a, n))) return 0;
return a->size += n, b;
}
/** Returns a pointer to a string that has been buffered and created from `a`,
or null on error. */
static const char **str_array_new(struct str_array *const a) {
return str_array_append(a, 1);
}
/** Returns a string array that has been zeroed, with zero strings and idle,
not taking up any dynamic memory. */
static struct str_array str_array(void) {
struct str_array a;
a.data = 0, a.capacity = a.size = 0;
return a;
}
/** Erases `a`, if not null, and returns it to idle, not taking up dynamic
memory. */
static void str_array_(struct str_array *const a) {
if(a) free(a->data), *a = str_array();
}
int main(void) {
struct str_array strs = str_array();
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(&strs, 5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new(&strs))) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
str_array_(&strs);
return success;
}
but will not work to add strings beyond the maximum index in the initializer
To do that, you need the pointer array to be dynamic as well. To create a dynamic array of strings is one of the very few places where using a pointer-to-pointer to emulate 2D arrays is justified:
size_t n = 5;
char** str_array = malloc(5 * sizeof *str_array);
...
size_t size = strlen(some_string)+1;
str_array[i] = malloc(size);
memcpy(str_array[i], some_string, size);
You have to keep track of the used size n manually and realloc more room in str_array when you run out of it. realloc guarantees that previous values are preserved.
This is very flexible but that comes at the cost of fragmented allocation, which is relatively slow. Had you used fixed-size 2D arrays, the code would perform much faster but then you can't resize them.
Note that I used memcpy, not memmove - the former is what you should normally use, since it's the fastest. memmove is for specialized scenarios where you suspect that the two arrays being copied may overlap.
As a side-note, the strlen + malloc + memcpy can be replaced with strdup, which is currently a non-standard function (but widely supported). It seems likely that strdup will become standard in the upcoming C23 version of C, so using it will become recommended practice.

function to clear malloc, and make pointer to null

in my last question, I've asked how to use function to free an malloc'ed array, I wanted to improve my code so that the function won't just free the memory but also will set the pointer to NULL once it finishes the clearing.
Also I want a single function to do both - setting and clearing, depending on the command I'm passing, this is what I've done so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
char **set_arr(int number, char *command);
int command_read(char *command);
void clear_arr(char *arr[], int size);
char set[] = "set";
char clear[] = "clear";
int main() {
int num = // get number from user;
char** my_arr = NULL;
my_arr = set_arr(num, set);
// so far the code works as excepted
set_arr((size_t)&my_arr, clear);
return 0;
}
int command_read(char *command) {
if (strcmp(command, set) == 0)
return 'S';
if (strcmp(command, clear) == 0)
return 'C';
}
char **set_arr(int number, char *command) {
static char **arr = NULL;
static int size;
switch (command_read(command)) {
case 'S':
size = (int)number;
arr = malloc((size + 1) * sizeof(char *));
for (int i = 0; i <= size; i++) {
arr[i] = NULL;
if (i == size)
break;
arr[i] = malloc((string_len) * sizeof(char));
}
break;
case 'C':
clear_arr(arr, size);
free(arr);
uintptr_t value = number;
uint64_t *temp = (void *)value;
*temp = 0x0;
break;
}
return arr;
}
void clear_arr(char *arr[], int size) {
for (int i = 0; i < size; i++) {
free(arr[i]);
arr[i] = NULL;
}
}
I know that there is better methods to clear (and allocate memory?) but my primary question is, did I free all the memory I allocated for the array, and after the clearing, does the pointer my_arr is set correctly to NULL?
Writing a generic function to achieve your goal is not possible in Standard C because pointers to different types of objects may have a different representation so you cannot pass the address of a pointer and expect the function to handle it in a generic manner.
Yet this provision in the C Standard is not used on most current systems today. In particular, the POSIX standard mandates that all pointers have the same representation. Hence your generic function can work on these systems, with some precautions to avoid compilation warnings:
// free an array of allocated things
void free_array(void ***p, size_t count) {
void **array = *p;
for (size_t i = 0; i < count; i++) {
free(array[i]);
array[i] = NULL; // for safety
}
free(array);
*p = NULL;
}
// deal with the non portable conversion with macros
#define FREE_ARRAY(p, n) free_array((void ***)(void *)&(p), n)
// allocate an array of pointers to allocated things of size `size`.
// return a pointer to the array or `NULL` if any allocation failed
void **malloc_array(size_t count, size_t size) {
void **array = malloc(count * sizeof(*array));
if (array) {
for (size_t i = 0; i < count; i++) {
array[i] = calloc(size, 1); // allocate and initialize to all bits zero
if (array[i] == NULL) {
while (i-- > 0) {
free(array[i]);
array[i] = NULL;
}
return NULL;
}
}
}
return array;
}
#define MALLOC_ARRAY(n, type) ((type **)(void *)malloc_array(n, sizeof(type)))
#define MALLOC_2D_ARRAY(n1, n2, type) ((type **)(void *)malloc_array(n1, (n2) * sizeof(type)))
Passing the command as a string is very inefficient. You should use an int or an enum for the command, but you can use the above macros and code in your program this way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
int main() {
int string_len = 100;
int num = 10; // get number from user;
char **my_arr = MALLOC_2D_ARRAY(num, string_len, char);
FREE_ARRAY(my_arr, num);
return 0;
}

Having problems passing in data to a function

Writing a program in C and I am trying to pass two variables into the function kstrextend. Name which is a word or set of characters that is stored in the value kstring and a which is a numeric value, but name is not getting passed into the function at all as far as I can tell and I cannot figure out why. Is something not getting stored correctly? Because the function works just fine I just cannot get name passed in correctly.
Declaration of kstring and name:
kstring name;
char kstring[50];
Typedef:
typedef struct
{
char *data;
size_t length;
} kstring;
Function:
void kstrextend(kstring *strp, size_t nbytes)
{
char *nwData;
int lnth=strp->length;
if(lnth < nbytes)
{
// new array allocate with large size and copy data to new array
nwData = (char *)realloc(strp->data, nbytes);
// call abort in case of error
if(nwData == NULL)
{
abort();
}
//Making strp->data point to the new array
strp->data = nwData;
//Setting strp->length to the new size.
strp->length = nbytes;
for(int i = 0; i <= lnth; i++)
{
printf("\n %s",strp->data);
}
// filled with '\0' in remaining space of new array
for (int lp = lnth; lp < nbytes; lp++)
{
strp->data[lp] = '\0';
printf("\n %s", strp->data[lp]);
}
}
}
Portion of main:
size_t a;
char * k = kstring;
printf("\n Enter number: ");
scanf("%d", &a);
name.data = (char*)calloc(sizeof(k), 1);
strcpy(input, k);
name.length= kstring_length;
kstrextend(&name,a);
First of all, you have misleading variable name kstring. Use something else like kstring_init and assign it a value. I assume you want to initialize the name variable of type kstring with something and then change its length. So this is what it is all about. Then define a constant of type char * and initialize length and data of your kstring with it. Then use realloc to extend the memory of the pointer with the input value a, not with the size of k. That does not make sense. Since the size of k is the size of the pointer, which is constant.
In your function: don't use int if you pass size_t. Use the same datatype where you do the same things.
In your loop from 0 to lnth, you output the same string lnth+1 times, which does not make sense. You probably want to output the characters of the string. So use %c and use an index into the character array and don't set <= lnth but < lnth as upper limit. Take care with data types if signed and unsigned!
Design hint: If you have a if block, that wraps all your code... invert the condition and just exit so that the code is after the if block.
Take care when you work with size_t and int, since int is signed and size_t is not, which can give problems in if statements.
Don't use abort but rather exit. You don't want your program to abort abnormally and core-dump.
A working version of your program is:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
char *data;
size_t length;
} kstring;
kstring name;
char *kstring_init = "blabla";
void kstrextend(kstring *strp, size_t nbytes)
{
char *nwData;
size_t lnth = strp->length;
if ((int) lnth >= (int) nbytes) {
printf("Error, size already larger than requested size.\n");
exit(-1);
}
// new array allocate with large size and copy data to new array
nwData = realloc(strp->data, sizeof(char) * (int) nbytes);
if(nwData == NULL)
{
printf("Error, realloc returned NULL\n");
exit(-1);
}
//Making strp->data point to the new array
strp->data = nwData;
//Setting strp->length to the new size.
strp->length = nbytes;
for(int i = 0; i < lnth; i++)
{
printf("\n %c", strp->data[i]);
}
// filled with '\0' in remaining space of new array
for (int lp = lnth; lp < (int) nbytes; lp++)
{
strp->data[lp] = '\0';
printf("\n %c", strp->data[lp]);
}
}
int main(void)
{
size_t a;
printf("\n Enter number: ");
scanf("%d", &a);
name.length = strlen(kstring_init) + 1;
printf("Length of string is: %d\n", name.length);
name.data = (char*)malloc(sizeof(char) * name.length);
strcpy(name.data, kstring_init);
printf("Old string: %s\n", name.data);
printf("You want to reallocate %d bytes\n", a);
kstrextend(&name, a);
return 0;
}

Generic binary search in C

I am having trouble with this code I wrote for a generic binary search.
when trying to execute the search on an array of strings I noticed that the array of strings, passed to binSearch function does not contain the strings.
can someone suggest a hint?
Much appreciation
#define SIZE 100
typedef unsigned char BYTE
please consider this main:
void main()
{
char ** stringArr, stringToFind[SIZE];
int stringSize;
int res;
stringArr = getStringArr(&stringSize);
// string to find
gets(stringToFind);
res = stringBinSearch(stringArr, stringSize, stringToFind);
if (res == 1)
printf("The string %s was found\n", stringToFind);
else
printf("The string %s was not found\n", stringToFind);
}
char** getStringArr(int* stringSize)
{
int i, size, len;
char** arr;
char temp[SIZE];
scanf("%d", &size);
getchar();
arr = (char**)malloc(size * sizeof(char*));
checkAllocation(arr);
for (i = 0; i < size; i++)
{
gets(temp);
len = strlen(temp);
temp[len] = '\0';
arr[i] = (char*)malloc((len+1) * sizeof(char));
checkAllocation(arr[i]);
strcpy(arr[i], temp);
}
*stringSize = size;
return arr;
}
int stringBinSearch(char** stringArr, int stringSize, char* stringToFind)
{
return binSearch(stringArr, stringSize, sizeof(char*), stringToFind,compare2Strings);
}
int binSearch(void* Arr, int size, int ElemSize, void* Item, int(*compare)(void*, void*))
{
int left = 0, right = size - 1, place;
BOOL found = FALSE;
while (found == FALSE && left <= right)
{
place = (left + right) / 2;
if (compare(Item, (BYTE*)Arr + place*ElemSize) == 0)
found = TRUE;
else if (compare(Item, (BYTE*)Arr + place*ElemSize) < 0)
right = place - 1;
else
left = place + 1;
}
return found;
}
int compare2Strings(void* str1, void* str2)
{
char* elemA, *elemB;
elemA = (char*)str1;
elemB = (char*)str2;
return strcmp(elemA, elemB);
}
When you sort an array of int, the values passed are pointer to int, spelled int *. When you sort an array of strings (spelled char *), the values passed are pointer to string, spelled char **. You comparator is no use for comparing strings. As the inimitable BLUEPIXY said in their incredibly terse style — you need to modify the code to treat the passed void * arguments as char ** and not as char *.
With generic sorting, that's usually the end of the issue. With binary search, there's another issue that you run foul of. That is that the type of the item being searched for needs to be the same as the one of the entries in the array, so you need to pass a pointer to the item, not just the item.
So, adding material to allow the code to compile with minimal changes, changing from gets() to a cover for fgets() (because gets() is too dangerous to be used — ever! and programs that use it produce a warning when its used on macOS Sierra 10.12.5 — warning: this program uses gets(), which is unsafe.), and printing out the input data so you can see what's what, I end up with:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BOOL int
#define TRUE 1
#define FALSE 0
static inline char *sgets(size_t buflen, char *buffer)
{
char *result = fgets(buffer, buflen, stdin);
if (result)
buffer[strcspn(buffer, "\n")] = '\0';
return result;
}
#define checkAllocation(x) assert((x) != 0)
#define SIZE 100
typedef unsigned char BYTE;
char **getStringArr(int *stringSize);
int stringBinSearch(char **stringArr, int stringSize, char *stringToFind);
int binSearch(void *Arr, int size, int ElemSize, void *Item, int (*compare)(void *, void *));
int compare2Strings(void *str1, void *str2);
int main(void)
{
char **stringArr, stringToFind[SIZE];
int stringSize;
int res;
stringArr = getStringArr(&stringSize);
sgets(sizeof(stringToFind), stringToFind);
printf("Strings: %d\n", stringSize);
for (int i = 0; i < stringSize; i++)
printf("[%d] = [%s]\n", i, stringArr[i]);
printf("Search: [%s]\n", stringToFind);
res = stringBinSearch(stringArr, stringSize, stringToFind);
if (res == 1)
printf("The string %s was found\n", stringToFind);
else
printf("The string %s was not found\n", stringToFind);
return 0;
}
char **getStringArr(int *stringSize)
{
int i, size, len;
char **arr;
char temp[SIZE];
scanf("%d", &size);
getchar();
arr = (char **)malloc(size * sizeof(char *));
checkAllocation(arr);
for (i = 0; i < size; i++)
{
sgets(sizeof(temp), temp);
len = strlen(temp);
temp[len] = '\0';
arr[i] = (char *)malloc((len + 1) * sizeof(char));
checkAllocation(arr[i]);
strcpy(arr[i], temp);
}
*stringSize = size;
return arr;
}
int stringBinSearch(char **stringArr, int stringSize, char *stringToFind)
{
return binSearch(stringArr, stringSize, sizeof(char *), &stringToFind, compare2Strings);
}
int binSearch(void *Arr, int size, int ElemSize, void *Item, int (*compare)(void *, void *))
{
int left = 0, right = size - 1, place;
BOOL found = FALSE;
while (found == FALSE && left <= right)
{
place = (left + right) / 2;
if (compare(Item, (BYTE *)Arr + place * ElemSize) == 0)
found = TRUE;
else if (compare(Item, (BYTE *)Arr + place * ElemSize) < 0)
right = place - 1;
else
left = place + 1;
}
return found;
}
int compare2Strings(void *str1, void *str2)
{
char *elemA = *(char **)str1;
char *elemB = *(char **)str2;
return strcmp(elemA, elemB);
}
The key changes are:
compare2Strings() — compare the data in char ** values.
stringBinSearch() — pass the address of stringToFind.
AFAICR, any other change is cosmetic or 'infrastructure'.
Note that the return type of main() should be int — you can get away with void only on Windows where it is allowed.
Example run 1:
Data:
5
Antikythera
albatross
armadillo
pusillanimous
pygmalion
pygmalion
Output:
Strings: 5
[0] = [Antikythera]
[1] = [albatross]
[2] = [armadillo]
[3] = [pusillanimous]
[4] = [pygmalion]
Search: [pygmalion]
The string pygmalion was found
Example run 2:
Data file:
5
armadillo
pygmalion
Antikythera
pusillanimous
albatross
pygmalion
Output:
Strings: 5
[0] = [armadillo]
[1] = [pygmalion]
[2] = [Antikythera]
[3] = [pusillanimous]
[4] = [albatross]
Search: [pygmalion]
The string pygmalion was not found
The difference between the two sets of data is that in the first case, the strings are in correct sorted order — a prerequisite condition for successful (reliable) binary search — and in the second, the data is not in correct sorted order. (That said, I had one non-sorted order that still found 'pygmalion' — I used a different shuffle for the shown results. But the 'reliable' comment applies.)
Hello your problem is the way you send the array of strings to the binary search function. Because you need to pass an array of strings to it your Arr parameter must be void** not void*
int binSearch(void** Arr, int size, int ElemSize, void* Item, int(*compare)(void*, void*))
And in your function whenever you want to acces a string from your array it will be enough to acces it like: (char*) *(Arr+place*ElemSize)
Your approach which is to write a generic binary search is right. However attempting to return early slows down a binary search. It also means you can't use the C++ convention that "less than" is the comparison operator defined. Wait until left and right equal each other, and return that.

C reference gone after for loop

i got a problem with my C code.
int split(char* source, char*** target, char* splitChar) {
int i;
int currentLength;
int splitCharPosition;
char* currentSubstring = source;
int splitCount = charcount(source, splitChar) + 1;
*target = (char**) malloc(splitCount * sizeof(char**));
for(i=0;i<splitCount;i++) {
splitCharPosition = indexOf(currentSubstring, splitChar);
substring(currentSubstring, target[i], 0, splitCharPosition);
currentLength = strlen(currentSubstring);
substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr entLength-splitCharPosition);
}
return splitCount;
}
The problem is that if I use the Debugger, the pointer to splitChar is set to 0x0 after the first run of the for loop.
Does anybody know why it is set to 0x0?
EDIT:
int indexOf(char* source, char* template) {
int i;
int j;
int index;
for (i = 0; source[i]; i++) {
index = i;
for (j = 0; template[j]; j++) {
if (source[i + j] != template[j]) {
index = -1;
break;
}
}
if (index != -1) {
return index;
}
}
return -1;
}
EDIT2:
int charcount(char* source, const char* countChar) {
int i;
int count = 0;
for(i=0;source[i];i++) {
if(source[i] == countChar[0]) {
count++;
}
}
return count;
}
EDIT3:
char* substring(char* source, char** target, int start, int length) {
*target = (char*) malloc(length + 1);
strncpy(*target, source + start, length);
target[length] = '\0';
return *target;
}
EDIT4:
I just noticed that if I add
char* sndfpgjps = splitChar;
to my split() code it does not delete the reference. Anyone know why?
This line:-
substring(currentSubstring, &currentSubstring, splitCharPosition + 1, curr entLength-splitCharPosition);
... will cause a memory leak, as well as being incredibly inefficient. The old substring is left dangling. and never freed.
It would be much better to write
currentSubString += splitCharPosition + 1;
I don't think that's the problem, but it's a problem.
Also, as you're using C library functions like strlen(), why aren't you using strtok or better yet, strtok_r?
I have some reservations about the code, but this works cleanly under valgrind (no leaks, no abuse). I've left the sub-functions largely unchanged except that constant strings are marked constant. The code in split() has been simplified. As I noted in a comment, I suggest writing the main split() function so that you have a local char **string_list; which you allocate and fill. Then, when you're about to return, you assign *target = string_list;. This will make it easier for you to understand what's going on. Triple indirection is nasty. You can justify it here (just), but minimize the time you spend working with triple pointers. The revision adopts that strategy.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int split(const char *source, char ***target, const char *splitStr);
static int
indexOf(const char *source, const char *template)
{
int i;
int j;
int index;
for (i = 0; source[i]; i++)
{
index = i;
for (j = 0; template[j]; j++)
{
if (source[i + j] != template[j])
{
index = -1;
break;
}
}
if (index != -1)
return index;
}
return -1;
}
static int
charcount(const char *source, const char *countChar)
{
int count = 0;
for (int i = 0; source[i]; i++)
{
if (source[i] == countChar[0])
count++;
}
return count;
}
static char *
substring(const char *source, int start, int length)
{
char *target = (char *)malloc(length + 1);
if (target != 0)
{
memmove(target, source + start, length);
target[length] = '\0';
}
return target;
}
int
split(const char *source, char ***target, const char *splitStr)
{
int splitCount = charcount(source, splitStr) + 1;
char **result = (char **)malloc(splitCount * sizeof(*result));
if (result == 0)
return -1;
int splitLength = strlen(splitStr);
char **next = result;
const char *currentSubstring = source;
for (int i = 0; i < splitCount; i++)
{
int splitCharPosition = indexOf(currentSubstring, splitStr);
if (splitCharPosition < 0)
break;
*next++ = substring(currentSubstring, 0, splitCharPosition);
currentSubstring += splitCharPosition + splitLength;
}
*next++ = substring(currentSubstring, 0, strlen(currentSubstring));
*target = result;
return (next - result); /* Actual number of strings */
}
static void print_list(int nstrings, char **strings)
{
for (int i = 0; i < nstrings; i++)
{
if (strings[i] != 0)
printf("%d: <<%s>>\n", i, strings[i]);
}
}
static void free_list(int nstrings, char **strings)
{
for (int i = 0; i < nstrings; i++)
free(strings[i]);
free(strings);
}
int main(void)
{
const char source[] = "This is a string; it is really!";
char **strings;
int nstrings;
nstrings = split(source, &strings, " ");
printf("Splitting: <<%s>> on <<%s>>\n", source, " ");
print_list(nstrings, strings);
free_list(nstrings, strings);
nstrings = split(source, &strings, "is");
printf("Splitting: <<%s>> on <<%s>>\n", source, "is");
print_list(nstrings, strings);
free_list(nstrings, strings);
return 0;
}
Note that in the second example, charcount() returns 6 but there are only 4 strings. This caused a late adjustment to the source code. (You could realloc() the result so it is exactly the right size, but it probably isn't worth worrying about unless the discrepancy is really marked — say 'more than 10 entries'.) The error handling is not perfect; it doesn't access invalid memory after failure to allocate, but it doesn't stop trying to allocate, either. Nor does it report failures to allocate individual strings — it does for failure to allocate the array of pointers.
I'd probably avoid the triple pointer by creating a structure:
typedef struct StringList
{
size_t nstrings;
char **strings;
} StringList;
You can then pass a pointer to one of these into split(), and into the utility functions such as free_list() and print_list(). The free_list() function would then modify the structure so that both elements are zeroed after the data pointed at by the structure is freed.
I'd also be tempted to use a different implementation of indexOf():
int indexOf(const char *haystack, const char *needle)
{
const char *pos = strstr(haystack, needle);
if (pos != 0)
return (pos - haystack);
return -1;
}
I do not know what substring does, nor what signature it has, but in the line
substring(currentSubstring, target[i], 0, splitCharPosition);
target[i] is only defined for i==0. I believe you wanted to write
substring(currentSubstring, (*target)[i], 0, splitCharPosition);
See if your debugger also supports data breakpoints, i.e. break if some place in memory is modified. Then place one at the actual address of splitChar, and another at the address it points to. (Since you didn't specify whether the pointer is null or points to nil.) See where it breaks. It may be that it is a completely unrelated place; that would indicate a buffer overflow.
Also, you could make at least splitChar a pointer to const. You don't actually want to modify it, right? Better idea, make it a char, not a pointer, since its name suggests that there is only one character on which you split, not a string.
The first call to substring does not look correct:
substring(currentSubstring, target[i], 0, splitCharPosition);
I suspect it should be something like the following where it indexes the actual memory that was allocated:
substring(currentSubstring, &((*target)[i]), 0, splitCharPosition);
You first need to get the value that target points at (*target) and then index off of that and pass the address of that array location.

Resources