I am implementing a code in C so as to copy a string in an array of characters ( string ) and then later do a bsearch on it. But unexpectedly the bsearch returns false for results that should be true.
I am guessing it has something to do with how i am inserting the string in the first place during insert. You could consider this as insertion and search on the leaf node of a btree.
I am coding this in a multi - file framework so cant post all code. posting relevant parts
Structure of the array of strings for easier visualization -
array of strings ( array of array of chars )
--
0 -- array of characters ( size may be 5 )
1 -- array of characters ( size may be 7 )
2 -- array of characters ( size may be 10 )
or
keys = [
[ s t r i n g 1 ]
[ s t r i n g T w o ]
[ s t r i n g T H R E E ]
]
function to insert -
void insert_in_leaf_node(struct leaf_node * node, u32 len, u8 * key){
if (node->no_of_keys > 1 ) {
if (!search_in_fat(node, len, key)) {
node->keys[node->no_of_keys] = (char *) malloc(sizeof(char) * len);
strncpy(node->keys[node->no_of_keys], (char *) key, len); // copy key to array
node->keys[node->no_of_keys][len] = '\0';
node->no_of_keys += 1;
qsort(node->keys, (size_t) node->no_of_keys, sizeof(char *), cstring_cmp); // sort alphabetically to enable bsearch later
}
} else {
node->keys[node->no_of_keys] = (char *) malloc(sizeof(char) * len);
strncpy(node->keys[node->no_of_keys], (char *) key, len); // copy key to array
node->keys[node->no_of_keys][len] = '\0';
node->no_of_keys += 1;
qsort(node->keys, (size_t) node->no_of_keys, sizeof(char *), cstring_cmp); // sort alphabetically to enable bsearch later
}
}
code to search -
bool search_in_fat(struct leaf_node * node, u32 len, u8 * key){
if(!bsearch(key,node->keys,(size_t)node->no_of_keys, sizeof( char * ),(int(*)(const void*,const void*)) strcmp)) return false;
return true;
}
cstring_cmp functione used in insert function -
int cstring_cmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
/* strcmp functions works exactly as expected from
comparison function */
}
In case anyone is wondering whats in key, here is how a key array is populated earlier and a set / get function is called with indivisual key each time ( the set / get functions call the above functions )
code for trace load from file to generate array of keys -
( __samples holds the keys )
bool
load_trace(const char * const filename)
{
char * buf = NULL;
size_t size = 0;
FILE * fin = fopen(filename, "r");
if (fin == NULL) return false;
u64 i = 0;
// count for lines
while (getline(&buf, &size, fin) >= 1) {
i++;
}
rewind(fin);
__samples = malloc(sizeof(char *) * i);
__sizes = malloc(sizeof(u32) * i);
__nr_samples = i;
ssize_t len = 0;
i = 0;
while ((len = getline(&buf, &size, fin)) >= 1) {
if (buf[len-1] == '\n') { // remove trailing '\n'
len--;
buf[len] = '\0';
}
__samples[i] = strdup(buf);
__sizes[i] = len;
i++;
}
free(buf);
fclose(fin);
return true;
}
P.S : this part was not coded by me, but by my senior when building the framework.
What could be the problem? I have done quite a bit of googling but no positive results yet.
Thank You!
You can't pass strcmp() as the comparison function for bsearch(). That function needs to take pointers to the elements to compare (In this case a pointer to a pointer to a string, though the actual type of the functions arguments need to be const void *), while strcmp() expects a pointer to a string. There's an extra layer of indirection that it doesn't handle.
You don't show the function, but the cstring_cmp() function you use with qsort() can probably be used.
The first argument of the bsearch() comparison function is the pointer given as the key, the second argument is a pointer to the current element of the array being compared, where with a qsort() comparison function, both arguments are pointers to elements. So if you make the key argument to bsearch() a pointer to the thing you're looking for, both will work with the same function. In other words, bsearch(&key, ...) is good, bsearch(key, ...) isn't. You can also have a bsearch()-specific comparison function that will work with that second case. See https://stackoverflow.com/a/15824981/9952196 for an example.
Related
I want to write a function that adds an Entry to the first free slot of an array list (if there is a slot free) and a function that merges all those entries to one "String" (I know there are no strings) separated by ', '.
Example list contains: "Anna", "Alex", "Anton" in this order
Output:"Anna,Alex,Anton".
This is the .c file (only the toDo's should be done)
/** The function adds an entry name to the array list at the
* first free position, if such a position exists.
* If there is no free space, ie no NULL entry,
* the array remains unchanged. When inserting a name,
* First, the same amount of memory is allocated on the heap using malloc
* (note space for a final \ 0). After that
* we use strncpy to convert the string name into the allocated
* Memory area copied.
* #param list: Array of \0 terminated strings
* #param listsize: size of array
* #param name: \0 terminated string that should be copied into the array
*/
void addNameToList(char **list, size_t listsize, char *name) {
//toDo
}
/** The function adds all entries in the list array to one separated by ','
* zero-terminated string that is stored in destbuffer.
* Entries with the value NULL are not added.
* So that chained calls are possible, the function returns the
* Pointer destbuffer as return value.
* Example: The list contains the names "Anna", "Alex", "Anton" in this
* Sequence. The function returns "Anna, Alex, Anton".
* #param list: array of \0 terminated strings
* #param listsize: size of array
* #param destbuffer: destinationbuffer
* #param buffersize: size of destbuffer
* #return destbuffer
*/
char *printListToString(char **list, size_t listsize, char *destbuffer, size_t buffersize) {
//toDo
return destbuffer;
}
void freeList(char **list, size_t listsize) {
size_t index = 0;
for (index = 0; index < listsize; index++)
if (list[index] != NULL)
free(list[index]);
free(list);
}
char **initList(size_t size) {
return calloc(size, sizeof(char *));
}
This is the given Main (not to change):
int main(void) {
char **names;
char outputbuffer[100];
names = initList(LIST_SIZE);
addNameToList(names, LIST_SIZE, "Alice");
addNameToList(names, LIST_SIZE, "Bob");
addNameToList(names, LIST_SIZE, "Carla");
addNameToList(names, LIST_SIZE, "Dana");
addNameToList(names, LIST_SIZE, "Eve");
printListToString(names, LIST_SIZE, outputbuffer, 100);
printf("%s\n", outputbuffer); //Output: Alice,Bob,Carla,Dana,Eve
freeList(names,LIST_SIZE);
}
What I have tried so far (not working):
char *printListToString(char **list, size_t listsize, char *destbuffer, size_t buffersize) {
int k = 1;
for(int i = 0; i < listsize; i++) {
if(list != NULL) {
strcpy_s(destbuffer, sizeof list, list);
}
if(list == '\0') {
destbuffer[i] =',';
destbuffer[k] = ' ';
}
k++;
}
return destbuffer;
}
The code above:
k is always one step ahead of i, so it can add a space right after the ',' (which is added at i)
I iterate thru the list and check whether an entry is NULL or not if its not NULL it should copy the name from the list into the destbuffer
Since the names end with \0 I thought I can just add , and a space right after I copied the name
void addNameToList(char **list, size_t listsize, char *name) {
malloc(sizeof name);
if(sizeof list != listsize) {
for(int i = 0; i < listsize; i++) {
if(list[i] == NULL) {
list[i] = name;
}
}
}
}
the code above:
saving memory for the name
check if list is full
if not I add the name at the first place thats null
(Note that I dont have any experience in C, only Python and Java. The "code above" section is what the code meant to do, not what its actually doing)
Let's consider the last snippet
void addNameToList(char **list, size_t listsize, char *name) {
// ^^^^^^^^^^
// 'name' is a POINTER. Not an array, nor a C-string (NULL-terminated array
// of char). It may point to the first element of one of those, though.
malloc(sizeof name);
// ^^^^^^^^^^^ This returns the size, in bytes, of a POINTER to char.
// Maybe 8, but surely not the number of characters up to the first '\0'
// from the one pointed by 'name'. To get that, you need
// strlen(name)
// Besides, 'malloc' RETURNS a pointer and you NEED it, because the whole
// point of that function call is to allocate enough memory to store
// your object and you better know WHERE this space is. If only to later
// release that resorce and avoid any leak.
if(sizeof list != listsize) {
// ^^^^^^^^^^^ Again, it's the size of a pointer to a pointer to char...
// Unfortunately, it's also probably different from listsize, so that
// it's harder to catch this error. Also, You don't need this check...
for(int i = 0; i < listsize; i++) {
// ^^^^^^^^^^^^ ... Because there already is this.
if(list[i] == NULL) {
list[i] = name;
// ^ This is a shallow copy, only the value OF the pointer
// (let's say the memory address) is copied, NOT all the elements
// of the string.
// Here is where the memory should be allocated, something like
// size_t size = strlen(name);
// list[i] = malloc(size);
// if ( list[i] == NULL ) {
/* malloc may fail... */
// }
// Then, the array must be copied. Your teacher mentioned
// 'strncpy'.
}
}
}
}
The other function, printListToString is broken too, perhaps even worse than the previous one.
k is always one step ahead of i, so it can add a space right after the ',' (which is added at i)
To do that, i + 1 could be enough, but that's not the point. There should be two different indices, one to itarate over list and one for destbuffer.
I iterate thru the list and check whether an entry is NULL or not if its not NULL it should copy the name from the list into the destbuffer
Unfortunately, in all its occurrences, list is written without an [i], so that no name in the list is actually accessed.
Since the names end with \0 I thought I can just add , and a space right after I copied the name
I'd do something like
char *printListToString( char **list, size_t listsize
, char *destbuffer, size_t buffersize) {
size_t j = 0;
for (size_t i = 0; i < listsize; ++i) {
if ( list[i] != NULL ) {
// Add ", ", but only if it's not the first name.
if ( j != 0 ) {
if ( j + 2 > buffersize )
break;
destbuffer[j++] = ',';
destbuffer[j++] = ' ';
}
// Copy the name. Feel free to use a library function instead.
for ( size_t k = 0; list[i][k] != '\0' && j < buffersize; ++k, ++j ) {
destbuffer[j] = list[i][k];
}
}
}
destbuffer[j] = '\0';
return destbuffer;
}
For this code below that I was writing. I was wondering, if I want to split the string but still retain the original string is this the best method?
Should the caller provided the ** char or should the function "split" make an additional malloc call and memory manage the ** char?
Also, I was wondering if this is the most optimizing method, or could I optimize the code better than this?
I still have not debug the code yet, I am a bit undecided whether if the caller manage the ** char or the function manage the pointer ** char.
#include <stdio.h>
#include <stdlib.h>
size_t split(const char * restrict string, const char splitChar, char ** restrict parts, const size_t maxParts){
size_t size = 100;
size_t partSize = 0;
size_t len = 0;
size_t newPart = 1;
char * tempMem;
/*
* We just reverse a long page of memory
* At reaching the space character that is the boundary of the new
*/
char * mem = (char*) malloc( sizeof(char) * size );
if ( mem == NULL ) return 0;
for ( size_t i = 0; string[i] != 0; i++ ) {
// If it is a split char we at a new part
if ( string[i] == splitChar) {
// If the last character was not the split character
// Then mem[len] = 0 and increase the len by 1.
if (newPart == 0) mem[len++] = 0;
newPart = 1;
continue;
} else {
// If this is a new part
// and not a split character
// we make a new pointer
if ( newPart == 1 ){
// if reach maxpart we break.
// It is okay here, to not worry about memory
if ( partSize == maxParts ) break;
parts[partSize++] = &mem[len];
newPart = 0;
}
mem[len++] = string[i];
if ( len == size ){
// if ran out of memory realloc.
tempMem = (char*)realloc(mem, sizeof(char) * (size << 1) );
// if fail quit loop
if ( tempMem == NULL ) {
// If we can't get more memory the last part could be corrupted
// We have to return.
// Otherwise the code below can seg.
// There maybe a better way than this.
return partSize--;
}
size = size << 1;
mem = tempMem;
}
}
}
// If we got here and still in a newPart that is fine no need
// an additional character.
if ( newPart != 1 ) mem[len++] = 0;
// realloc to give back the unneed memory
if ( len < size ) {
tempMem = (char*) realloc(mem, sizeof(char) * len );
// If the resizing did not fail but yielded a different
// memory block;
if ( tempMem != NULL && tempMem != mem ){
for ( size_t i = 0; i < partSize; i++ ){
parts[i] = tempMem + (parts[i] - mem);
}
}
}
return partSize;
}
int main(){
char * tStr = "This is a super long string just to test the str str adfasfas something split";
char * parts[10];
size_t len = split(tStr, ' ', parts, 10);
for (size_t i = 0; i < len; i++ ){
printf("%zu: %s\n", i, parts[i]);
}
}
What is "best" is very subjective, as well as use case dependent.
I personally would keep the parameters as input only, define a struct to contain the split result, and probably return such by value. The struct would probably contain pointers to memory allocation, so would also create a helper function free that memory. The parts might be stored as list of strings (copy string data) or index&len pairs for the original string (no string copies needed, but original string needs to remain valid).
But there are dozens of very different ways to do this in C, and all a bit klunky. You need to choose your flavor of klunkiness based on your use case.
About being "more optimized": unless you are coding for a very small embedded device or something, always choose a more robust, clear, easier to use, harder to use wrong over more micro-optimized. The useful kind of optimization turns, for example, O(n^2) to O(n log n). Turning O(3n) to O(2n) of a single function is almost always completely irrelevant (you are not going to do string splitting in a game engine inner rendering loop...).
I am currently using the following way to copy an array of strings:
void copy_sentinel_array(char ** buffer, char ** list) {
while (*list) {
*buffer = malloc(sizeof(char) * strlen(*list) + 1);
*buffer++ = *list++;
}
*buffer = NULL;
return;
}
It's simple enough and works fine. However, I'm having more trouble doing the same thing copying the words over letter by letter (so I can, for example, upper or lowercase the letters). For example:
void copy_sentinel_array(char ** buffer, char ** list) {
while (*list) {
*buffer = malloc(sizeof(char) * strlen(*list) + );
for (int i=0; i<=strlen(*list); i++)
(*buffer)[i] = tolower((*list)[i]); // let's lowercase it
buffer++;
list++;
}
*buffer = NULL;
return;
}
What would be the proper way to do this copy per-word and then per-letter?
The problem is operator precedence. *buffer[i] is equivalent to *(buffer[i]), but what you want is (*buffer)[i] (and the same for list).
If you don't need to process each character you can just use
strcpy(*buffer, *list);
I am trying to recreate the Linux command ls in C. I have the program working (going through a list of directories passed in as command line arguments, and pushing all the contents to an array of strings).
Now I'm trying to implement the quicksort flag for the command (e.g. ls -s /dev), which should print out all the contents in a lexicographical order. The problem is that the qsort() method in stdlib.h only "sorts" one element (basically swaps the first and the last element in the array) for my program.
I have no idea what's wrong, as all my pointers are properly set up as well. I'm adding the relevant snippet codes below, please let me know if something catches your eye that has been escaping mine for two days.
Compare function for qsort:
int normalCompare (const void *stringOne, const void *stringTwo) {
return strcmp((const char *)stringOne, (const char *)stringTwo);
}
Actual function call:
void execute_ls(char **directoryList, Flags flags) {
//Create a buffer for directories' file names
char **fileNamesList;
fileNamesList = malloc(MAX_FILES * sizeof (*fileNamesList));
int fileBufferCurrentPointer = 0;
//Fill the buffer out by calling execute_ls_one_dir on all the directories
int i = 0;
while(directoryList[i] != NULL) {
execute_ls_one_dir(directoryList[i], fileNamesList, &fileBufferCurrentPointer);
i++;
}
fileNamesList[fileBufferCurrentPointer] = NULL;
//Process the array
if(flags.s == 1) {
qsort(fileNamesList, fileBufferCurrentPointer, sizeof (char *), normalCompare);
}
else if(flags.r == 1) {
qsort(fileNamesList, fileBufferCurrentPointer, sizeof (char *), reverseCompare);
}
//Print to user
for(i = 0; i < fileBufferCurrentPointer; i++) {
if(((*fileNamesList[i] == '.') && (flags.a == 1)) || (*fileNamesList[i] != '.')) {
printf("%s\n", fileNamesList[i]);
}
}
//Deallocate fileNamesList
for(i = 0; i < MAX_FILES; i++) {
free(fileNamesList[i]);
}
free(fileNamesList);
}
Updating the fileBufferCurrentPointer:
while((oneDirEntryPtr = readdir(currentDirPtr)) != NULL) {
// Push the file name onto the fileNamesList array
fileNamesList[*fileBufferCurrentPointer] = malloc(MAX_LEN_NAME * sizeof (char));
strcpy(fileNamesList[*fileBufferCurrentPointer], oneDirEntryPtr->d_name);
*fileBufferCurrentPointer += 1;
}
I'm confused as to why qsort is only working once (technically isn't even passing through the array once instead of recursively multiple times to complete the algorithm).
you've made a common mistake in thinking the comparison function is taking two elements from your array - it's actually taking two pointers to elements in your array so you should call strcmp like this
int normalCompare (const void *stringOne, const void *stringTwo) {
return strcmp(*(const char **)stringOne, *(const char **)stringTwo);
}
I am new with .ini files and thus this qn(which might seem silly) .I have created a .ini file and access it via my C program. The ini file looks like this:
[key]
title = A,H,D
The C program accesses it using:
LPCSTR ini ="C:\\conf.ini;
char var[100];
GetPrivateProfileString("key", "title", 0, var, 100, ini);
printf("%s", var);
char* buffer = strtok(var, ", ");
do{
printf("%s", buffer);
if (strcmp(buffer, "A")==0)
printf("Hello");
puts("");
}while ((buffer=strtok(NULL, ", "))!= NULL);
output looks as :
A H D F G IAHello
H
D
F
G
Now what I need to do is use these individual tokens again to form an array with indices within my C program. For example:
char x[A, H, D, F, G]
so that when I refer to the index 2, x[2] should give me 'D'. Could somebody suggest a way to do this. I have never used strtok before and thus very confused. Thank you in advance.
This question is quite similar to others regarding getting external information and storing it in an array.
The problem here is the amount of elements in your array to store.
You could use Link-lists, but for this example, I would scan the file, getting the total amount of items needed for the array - and then parse the file data again - storing the items in the array.
The first loop, goes through and counts the items to be store, as per your example posted. I will do the second loop just as an example - please note in my example you would of created nTotalItems and have counted the amount of items, storing that in nTotalItems ... I am assuming you want to store a string, not just a char...
Also please note this a draft example, done at work - only to show a method of storing the tokens into an array, therefore there is no error checking ec
// nTotalItems has already been calculated via the first loop...
char** strArray = malloc( nTotalItems * sizeof( char* ));
int nIndex = 0;
// re-setup buffer
buffer = strtok(var, ", ");
do {
// allocate the buffer for string and copy...
strArray[ nIndex ] = malloc( strlen( buffer ) + 1 );
strcpy( strArray[ nIndex ], buffer );
printf( "Array %d = '%s'\n", nIndex, strArray[ nIndex ] );
nIndex++;
} while ((buffer=strtok(NULL, ", "))!= NULL);
Just use an INI parser that supports arrays.
INI file:
[my_section]
title = A,H,D
C program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
#define MY_ARRAY_DELIMITER ','
struct configuration {
char ** title;
size_t title_length;
};
static char ** make_strarray (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
*arrlen = ini_array_get_length(src, MY_ARRAY_DELIMITER, ini_format);
char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
if (!dest) { return NULL; }
memcpy(dest + *arrlen, src, buffsize);
char * iter = (char *) (dest + *arrlen);
for (size_t idx = 0; idx < *arrlen; idx++) {
dest[idx] = ini_array_release(&iter, MY_ARRAY_DELIMITER, ini_format);
ini_string_parse(dest[idx], ini_format);
}
return dest;
}
static int ini_handler (IniDispatch * this, void * v_conf) {
struct configuration * conf = (struct configuration *) v_conf;
if (this->type == INI_KEY && ini_string_match_si("my_section", this->append_to, this->format)) {
if (ini_string_match_si("title", this->data, this->format)) {
/* Save memory (not strictly needed) */
this->v_len = ini_array_collapse(this->value, MY_ARRAY_DELIMITER, this->format);
/* Allocate a new array of strings */
if (conf->title) { free(conf->title); }
conf->title = make_strarray(&conf->title_length, this->value, this->v_len + 1, this->format);
if (!conf->title) { return 1; }
}
}
return 0;
}
static int conf_init (IniStatistics * statistics, void * v_conf) {
*((struct configuration *) v_conf) = (struct configuration) { NULL, 0 };
return 0;
}
int main () {
struct configuration my_conf;
/* Parse the INI file */
if (load_ini_path("C:\\conf.ini", INI_DEFAULT_FORMAT, conf_init, ini_handler, &my_conf)) {
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
}
/* Print the parsed data */
for (size_t idx = 0; idx < my_conf.title_length; idx++) {
printf("my_conf.title[%d] = %s\n", idx, my_conf.title[idx]);
}
/* Free the parsed data */
if (my_conf.title_length) {
free(my_conf.title);
}
return 0;
}
Output:
my_conf.title[0] = A
my_conf.title[1] = H
my_conf.title[2] = D