qsort() - sorting an array of strings - c

Can't seem to find the answer on any question here.
Basically I have this line of code:
qsort(Code_Lines, number_of_lines, sizeof(char*), myCmp);
when Code_Lines is a char** - points to an array of char*s when each one contains a string OR pointing to NULL. What I want to do is to sort all of the strings alphabetically when the strings containing NULL will be at the END of the array.
Each string within the Code_Lines will be sorted by its 4 first characters (it's unequal in length of each string - and the first four characters are always different - mentioning a number from 0001 to 9999), if it's NULL it will just put it in the end of the array.
The variable number_of_lines is containing the number of lines (code lines) that are in the array, AKA - number of elements (strings, in this case) in the array.
myCmp is my compare function and I wrote it this way:
int myCmp(const void* element1, const void* element2)
{
int return_value;
if(!element1) //element1 of them is NULL
{
return_value = 1;
}
else if(!element2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(*((char**)element1), *((char**)element2), 4);
}
return return_value;
}
Any idea what the problem might be ? The program just crashes there.
The function works when the array isn't containing NULL but fails when it does...

In a qsort comparison function, the arguments are pointers to the elements of the array you provided, not the elements themselves. The elements being compared obviously exist, so these pointers can never by NULL by definition. What you want to check is whether a particular element is NULL, not the pointer to element:
int myCmp(const void* element1, const void* element2)
{
int return_value;
char * e1 = *(char **) element1;
char * e2 = *(char **) element2;
if(!e1) //element1 of them is NULL
{
return_value = 1;
}
else if(!e2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(e1, e2, 4);
}
return return_value;
}

Related

C: Pointer struction comparing doesn't work [duplicate]

This question already has an answer here:
Comparing char* to string without strcmp
(1 answer)
Closed 2 years ago.
I should compare a struction with anothter without using strcmp(). But everytime the firstName on both sides are the same it doesn't go in the if function. I know the function gets the names bc in the printf I see clearly the names.
typedef struct{
char firstName[MAXC];
char familyName[MAXC];
char gender;
} TPerson;
....
int comparePeopleByFirstName(TPerson *a, TPerson *b){
if(a->firstName == b->firstName){
if(a->familyName < b->familyName){
return 1;
} else if (a->familyName > b->familyName){
return -1;
}
}else if(a->firstName < b->firstName){
return 1;
} else if (a->firstName > b->firstName){
return -1;
}
return 0;
}
In your example code, the structure contains arrays as members, not pointers. Therefore, it can never be the case that (a != b) && (a->firstName == b->firstname). It does not matter whether the contents of a->firstName and b->firstname match, because you are comparing the addresses of their first elements.
You could conceivably use memcmp() instead of strcmp() to perform the comparison, provided that you don't mind comparing the data after any string terminator, too. But maybe that would be cheating.
If you cannot use any library function then you'll need to compare char by char, with a loop. For example,
_Bool equal = 0;
for (int i = 0; ; ++i) {
if (a->firstName[i] != b->firstName[i]) {
// unequal
break;
}
if (a->firstName[i] == '\0') {
// b->firstName[i] == '\0' too, else control would not reach this point
equal = 1;
break;
}
}
That assumes properly terminated strings. If that's not a safe assumption then you can add a limit on i in the termination condition of the loop.

Passing pointer to pointer for reallocation in function

I'm a beginner C programmer and have issues implementing an (ordered) dynamic array of structs.
Before adding an element to the array, I want to check if it is full and double it's size in that case:
void insert_translation(dict_entry **dict, char *word, char *translation){
if( dictionary_entries == dictionary_size ){
dict_entry *temp_dict;
temp_dict = realloc(&dict, (dictionary_size *= 2) * sizeof(dict_entry) );
// printf("Increased dict size to %d\n", dictionary_size);
// if(temp_dict == NULL){
// fprintf(stderr, "Out of memory during realloc()!\n");
// /*free(dict);
// exit(EXIT_OUT_OF_MEMORY);*/
// }
//free(dict);
//*dict = temp_dict;
}
dictionary_entries++;
printf("Inserted %s into dict - %d of %d filled.\n", word, dictionary_entries, dictionary_size);
}
I call the function from the main function like this:
dictionary_size = 2; //number of initial key-value pairs (translations)
dictionary_entries = 0;
dict_entry *dictionary = malloc(dictionary_size * sizeof(dict_entry));
[...]
insert_translation(&dictionary, "bla", "blub");
In my understanding, dictionary is a pointer to a space in memory. &dictionary is a pointer to the pointer, which I pass to the function. In the function, dict is said pointer to pointer, so &dict should be the pointer to the area in memory? However, when I try to compile, I get the following error message:
pointer being realloc'd was not allocated
Edit
I expanded the code sample to show more of the code in the main function.
The problem is in this statement
temp_dict = realloc(&dict, (dictionary_size *= 2) * sizeof(dict_entry) );
The parameter dict has the type
dict_entry **dict
in the statement that reallocs the memory you have to use the value of the pointer *dic but you are uisng an expression &dict that has the type dict_entry ***.
Compare the type of the left side of the assignment
ict_entry *temp_dict
with the type of the reallocated pointer. They should be the same (except in C one of them can have the type void *)
So you need to write
temp_dict = realloc(*dict, (dictionary_size *= 2) * sizeof(dict_entry) );
^^^^^
In C arguments are passed by value. If you want to change the original value of an argument you should to pass it by reference through a pointer to the argument. In the function you need to dereference the pointer that to change the object pointed to by the pointer.
&dict -> *dict. You can simplify the code by using a return type, to avoid such bugs:
dict_entry* insert_translation(dict_entry* dict, char *word, char *translation)
{
...
if( dictionary_entries == dictionary_size )
{
dictionary_size *= 2;
dict_entry *tmp = realloc(dict, sizeof(dict_entry[dictionary_size]));
if(tmp == NULL)
{
// error handling, free(dict) etc
}
else
{
dict = tmp;
}
}
...
return dict;
}

bsearch() on an array of strings in C

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.

qsort() function in c used to compare an array of strings

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);
}

How to check if empty array in C

I have the following code in C:
int i = 0;
char delims[] = " \n";
char *result = NULL;
char * results[10];
result = strtok( cmdStr, delims );
while( result != NULL )
{
results[i] = result;
i++;
result = strtok(NULL, " \n");
}
if(!results[1])
{
printf("Please insert the second parameter...\n");
}
else
{
...
}
It always executes the else condition even if the results[1] is empty.
I've tried with results[1] == NULL but no success.
How do I can check if it is empty or not?
Initialize the results array so all elements are NULL:
char* results[10] = { NULL };
In the posted code the elements are unitialized and will be holding random values.
Additionally, prevent going beyond the bounds of the results array:
while (i < 10 && result)
{
}
There's no such thing as an "empty array" or an "empty element" in C. The array always holds a fixed pre-determined number of elements and each element always holds some value.
The only way to introduce the concept of an "empty" element is to implement it yourself. You have to decide which element value will be reserved to be used as "empty value". Then you'll have to initialize your array elements with this value. Later you will compare the elements against that "empty" value to see whether they are... well, empty.
In your case the array in question consist of pointers. In this case selecting the null pointer value as the reserved value designating an "empty" element is an obvious good choice. Declare your results array as
char * results[10] = { 0 }; // or `= { NULL };`
an later check the elements as
if (results[i] == NULL) // or `if (!results[i])`
/* Empty element */

Resources