I am slamming my head against the wall with this problem.
To summarize:
I need to dynamically add strings to an array, sort them, and then check against another string value.
This needs to work on a SCADA-system that support C as a scripting language, but with limited functionality. I have qsort() available.
However, with the test code I have, I am not able to use qsort on an array, with values that are added dynamically.
To be clear, I can add strings to the array, which works fine.
However when I call qsort() on that array, I can no longer print out the indices.
Heres is the code so far (be kind, I'm not very proficient in C):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int cstring_cmp (const void *a, const void *b)
{
// This function is taken from an online example
const char **ia = (const char **) a;
const char **ib = (const char **) b;
return strcmp (*ia, *ib);
}
int main ()
{
//char *ArchiveKomponents[] = {"R1890L", "F1121D", "F1284Z", "A1238K"};
// If I do the above commented out, it works as intended
char ArchiveKomponents[100][20];
strcpy(ArchiveKomponents[0], "R1890L");
strcpy(ArchiveKomponents[1], "F1284Z");
size_t strLen = sizeof (ArchiveKomponents) / sizeof (char *);
printf ("Len: %zu\n", strLen);
printf ("Before [0]: %s\n", ArchiveKomponents[0]);
printf ("Before [1]: %s\n", ArchiveKomponents[1]);
qsort (ArchiveKomponents, (size_t)strLen, sizeof (char *), cstring_cmp);
printf ("After [0]: %s\n", ArchiveKomponents[0]);
printf ("After [1]: %s\n", ArchiveKomponents[1]);
// When run, the "After" prints are not even printed, the program simply halts
return 0;
}
I feel that I have googled the entire internet, in search of an answer on how to do this, with no luck.
Regards
You are comparing incorrect types. The comparison functions treats 4 or 8 characters from the element as a pointer to a string. Dereferencing this pointer triggers Undefined Behavior, likely a crash.
Note, that the type of a single element is char[20] not char*. Therefore your comparison function could be simply implemented as:
int cstring_cmp (const void *a, const void *b)
{
return strcmp (a, b);
}
Pointers a and b points to arrays of 20 character. The address of array is the same as an address of its first element. So a and b can be used as pointers to chains of char (aka "c-strings").
Moreover, void* is automatically converted to any pointer type without casting.
The qsort invocation should be:
qsort (ArchiveKomponents, // array to be sorted
2, // number of elements in the array
sizeof ArchiveKomponents[0], // size of a single element
cstring_cmp // comparison function
);
Related
I'm trying to figure out how to "transform" strings (char*) to void* and viceversa.
When I execute this my output is just the first printf and ignores the second one, it doesn't even write "after = "
PS This little program is just to understand, I know i could actually use swap(&s[0],&s[1]). I need to know how to properly cast a void pointer into an array of strings.
I'm working on a uni project where I need to create my own quick_sort algorythm and I need the swap function inside of it to work with void pointers.
#include <stdio.h>
#include <stdlib.h>
static void swap(char** x,char** y);
static void swap(char** x,char** y){
char* temp=*x;
*x=*y;
*y=temp;
}
int main()
{
char* s[2];
s[0]="weee";
s[1]="yooo";
void* array=s;
printf("before %s %s\n",s[0],s[1]);
swap((&array)[0],(&array)[1]);
printf("after = %s %s",(char*)array,(char*)array);
return 0;
}
I think I'm missing something big
Thanks in advance :D
In this declaration the array s used as an initializer is implicitly converted to a pointer to its first element of the type char **.
void* array = s;
In the call of the function swap
swap((&array)[0],(&array)[1]);
the first argument can be the pointer array itself that will be implicitly casted to the pointer type of the corresponding parameter
swap( array, (&array)[1]);
But you need to correctly pass the second argument. To do this you need to cast the pointer array explicitly like
swap( array, ( char ** )array + 1 );
In the call of printf you need also correctly to supply argument expressions.
Here is your updated program
#include <stdio.h>
static void swap(char** x,char** y);
static void swap(char** x,char** y){
char* temp=*x;
*x=*y;
*y=temp;
}
int main()
{
char* s[2];
s[0]="weee";
s[1]="yooo";
void* array=s;
printf("before %s %s\n",s[0],s[1]);
swap( array, ( char ** )array + 1 );
printf("after = %s %s", *(char**)array, ( (char**)array )[1]);
return 0;
}
The program output is
before weee yooo
after = yooo weee
void *array = s; declares array to be a void *. Then &array is the address of that void *, so &array[1] would access a void * after it. But there is no void * after it, since void *array defines a single void *.
array could be properly defined to alias s with char **array = s;, after which swap(&array[0], &array[1]); would work as desired.
If you define array as void **array = (void **) s;, then swap(&array[0], &array[1]); will produce diagnostic messages because the types are wrong. You could use swap((char **) &array[0], (char **) &array[1]);.
Then, if you print the strings with printf("after = %s %s", array[0], array[1]);, this will work, although it is not entirely proper code. Using array[0] as an argument passes a void * where printf is expecting a char * for the %s. However, the C standard guarantees that void * and char * have the same representation (encode their values using bytes in memory in the same way), and it further says (in a non-normative note) that this is intended to imply interchangeability as arguments to functions.
The void* doesn't seem to fulfil any particular purpose here, just swap the pointers: swap(&s[0],&s[1]);.
You could also do this:
char** ptr = &s[0];
printf("before %s %s\n",ptr[0],ptr[1]);
swap(&ptr[0],&ptr[1]);
printf("after = %s %s",ptr[0],ptr[1]);
If you for reasons unknown insist on using void* then note that as your code stands, it points at the first char* in your array of char*. However, it isn't possible to perform pointer arithmetic on void* since that would entail knowing how large a "void" is. The void* doesn't know that it points at an array of pointers. Therefore array[i] is nonsense.
Also, the void* are set to point at char* so you simply cannot pass it to a function expecting a char**. You'd have to rewrite the whole program in a needlessly obfuscated way, so just abandon that idea.
I'm having some trouble passing a valid value to the qsort function but can't quite figure out what's going wrong with it. Here is what I have so far:
int main(void)
{
char* strings[4] = {"Onus", "deacon", "Alex", "zebra"};
printf("%zu\n", sizeof(strings));
qsort(strings, 4, 8, scmp);
}
int scmp(const void *p1, const void *p2)
{
printf("%s\n", (char*) p1);
return 0;
// ignore return value -- I'm just looking to print the string.
}
It just seems to print gibberish when I do this. Is this because qsort expects a value a pointer to a value and I'm passing it a pointer to a (char) pointer? What would be the correct way to reference it then?
It seems instead it should be: printf("%s\n", *(char* const*) p1); ? This is from some trial-and-error, though not sure why that works -- i.e., the *(char**).
For example, for passing an int I can do:
const int *v1 = p1;
But then a char* needs to be:
const char *s1 = *(char* const *) p1;
Why not just const char *s1 = (char*) p1; ?
strings is an array of pointers to char. The comparison function for qsort gets passed pointers to the two elements of the array that should be compared. Since the elements of the array are pointers to char, the arguments p1, p2 to scmp are pointers to (const) pointers to char, and should be cast to char ** (or rather char * const *).
What needs to be passed to printf is the pointer to char itself, so you have to dereference the argument to get that pointer. If you wanted to look at the individual characters of the string, you'd have to dereference again. There are two *s in the name of the type, so you have to apply * two times to get back to the underlying primitive type.
printf("The first character of the string is %c\n", **(char * const *)p1);
I am using Mac OS to implement part of function of command sort:
part of my code is below:
int compare(const void *p, const void *q) {
return strcmp(*(char **)p, *(char **)q);
}
void sort_file(char *filename, int unique, int reverse) {
/* TODO: Complete this function */
/* Note: you will probably need to implement some other functions */
char buf[1024][1024];
int i=0;
FILE * fp;
if(!(fp=fopen(filename,"r"))){
perror("Open error!");
exit(0);
}
while(fgets(buf[i],1024,fp)){
printf("%s",buf[i]);
i++;
}
qsort(buf, i, sizeof(char *), compare);
}
The result always show segmentation fault: 11
Anyone can tell me what's the problem and how to modify it?
I still want to know, if I don't know the maximum size of code in one line and in the file, how to define my array?
I get the idea from this page:
http://www.anyexample.com/programming/c/qsort__sorting_array_of_strings__integers_and_structs.xml
You don't have an array of pointers; you have an array of arrays. Each element in your array of arrays has a specific, fixed stride of 1024 char. qsort needs to know that, and you're not telling it. First change this:
qsort(buf, i, sizeof(char *), compare);
to this:
qsort(buf, i, sizeof *buf, compare);
Now qsort knows how big each "thing" is in your array of char arrays.
Next, your comparator should be altered to account for the address being passed and what it is as it pertains to your array of arrays. Each address passed to the comparator is where an element lays. But your elements are each char[1024]. The address of some char[1024] isn't some char**, it is char(*)[1024]. There are no pointers to pointers involved here. Your comparator can simply be:
int compare(const void *p, const void *q)
{
const char (*lhs)[1024] = p;
const char (*rhs)[1024] = q;
return strcmp(*lhs, *rhs);
}
Next, there is no limiter in you control loop to prevent overflowing your array of arrays. In short, this:
while(fgets(buf[i],1024,fp))
should be this:
while(i < 1024 && fgets(buf[i],1024,fp))
and ideally, that 1024 should be expressed as a constant somewhere to avoid magic number sprinkling.
Finally, you're leaking an open FILE* in your function. Not a good plan. Make sure to fclose() your file pointer.
Problems I see:
Wrong argument to qsort
qsort(buf, i, sizeof(char *), compare);
Needs to be:
qsort(buf, i, sizeof(buf[0]), compare);
Since the difference between buf+1 and buf is 1000 chars, it is wrong to use sizeof(char*) as the third argument.
Wrong casting of the arguments in compare
The original pointers are of type char (*)[1000], not char**. Hence you need to use:
int compare(const void *p, const void *q) {
char (*p1)[1000] = (char (*)[1000])(p);
char (*q1)[1000] = (char (*)[1000])(q);
return strcmp(*p1, *q1);
}
I hope i made it short and clear what I'm trying to do below.
The code is pretty complex for a SOF question and i didn't think i could make it simpler while keeping it directly testable by others.
so i cut the relevant parts and put them here.
why do i get this error and can you help me to fix it?
any help is appreciated!
thank you.
char words[100][WORD_LENGTH];
char temp[WORD_LENGTH];
// scan the next %s from stream and put it to temp
while(fscanf(file, "%s", temp) > 0){
// printf("reducer reads: %s\n", temp);
strcpy(words[arr_i], temp);
printf("%d -- %s\n", arr_i, words[arr_i]);
arr_i++;
}
at the second line i get segmentation fault error. (and possibly leak with valgrind)
int thunk = WORD_LENGTH;
qsort_r(&words, sizeof(words)/sizeof(words[0]), sizeof(words[0]), cmpstringp, &thunk);
from "man qsort":
static int cmpstringp(const void *p1, const void *p2) {
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
As per man qsort:
The qsort_r() function is identical to qsort() except that the
comparison function compar takes a third argument
Your comparison function is taking two arguments.
Update: And the real crash reason is following. You are passing to the compare function elements of array of type char[WORD_LENGTH], not char* as in man qsort example. So the parameters passed to the comparison function are p1 = &words[_], which is a pointer to the string to be compared. While in the case of char pointers it would be (char **), just a pointer to a char/string pointer. So the casts in the linestrcmp(* (char * const *) p1, * (char * const *) p2); are unnecessary and harmful, because in your case the leftmost dereference is the cause of the problem. Remove them and leave just strcmp(p1, p2).
As a side note, this issue is emphasizing again the difference between string array declarations as char *[] and char [][].
Suppose I have an array of pointers to char in C:
char *data[5] = { "boda", "cydo", "washington", "dc", "obama" };
And I wish to sort this array using qsort:
qsort(data, 5, sizeof(char *), compare_function);
I am unable to come up with the compare function. For some reason this doesn't work:
int compare_function(const void *name1, const void *name2)
{
const char *name1_ = (const char *)name1;
const char *name2_ = (const char *)name2;
return strcmp(name1_, name2_);
}
I did a lot of searching and found that I had to use ** inside of qsort:
int compare_function(const void *name1, const void *name2)
{
const char *name1_ = *(const char **)name1;
const char *name2_ = *(const char **)name2;
return strcmp(name1_, name2_);
}
And this works.
Can anyone explain the use of *(const char **)name1 in this function? I don't understand it at all. Why the double pointer? Why didn't my original function work?
Thanks, Boda Cydo.
If it helps keep things straight in your head, the type that you should cast the pointers to in your comparator is the same as the original type of the data pointer you pass into qsort (that the qsort docs call base). But for qsort to be generic, it just handles everything as void*, regardless of what it "really" is.
So, if you're sorting an array of ints, then you will pass in an int* (converted to void*). qsort will give you back two void* pointers to the comparator, which you convert to int*, and dereference to get the int values that you actually compare.
Now replace int with char*:
if you're sorting an array of char*, then you will pass in a char** (converted to void*). qsort will give you back two void* pointers to the comparator, which you convert to char**, and dereference to get the char* values you actually compare.
In your example, because you're using an array, the char** that you pass in is the result of the array of char* "decaying" to a pointer to its first element. Since the first element is a char*, a pointer to it is a char**.
Imagine your data was double data[5] .
Your compare method would receive pointers (double*, passed as void*) to the elements (double).
Now replace double with char* again.
qsort is general enough to sort arrays consisting of other things than pointers. That's why the size parameter is there. It cannot pass the array elements to the comparison function directly, as it does not know at compile time how large they are. Therefore it passes pointers. In your case you get pointers to char *, char **.
The comparison function takes pointers to the type of object that's in the array you want to sort. Since the array contains char *, your comparison function takes pointers to char *, aka char **.
Maybe it is easier to give you an code example from me. I am trying to sort an array of TreeNodes and the first few lines of my comparator looks like:
int compareTreeNode(const void* tt1, const void* tt2) {
const TreeNode *t1, *t2;
t1=*(const TreeNode**)tt1;
t2=*(const TreeNode**)tt2;
After that you do your comparison using t1 and t2.
from man qsort:
The contents of the array are sorted in ascending
order according to a comparison function pointed to by
compar, which is called with two arguments that **point**
to the objects being compared.
So it sounds like the comparison function gets pointers to the array elements. Now a pointer to a char * is a char **
(i.e. a pointer to a pointer to a character).
char *data[5] = { "boda", "cydo", "washington", "dc", "obama" };
is a statement asking the compiler for an array of size 5 of character pointers. You have initialized those pointers to string literals, but to the compiler, it's still an array of five pointers.
When you pass that array into qsort, the array of pointers decays into a pointer pointing to the first element, in accordance with C array parameter passing rules.
Therefore you must process one level of indirection before you can get to the actual character arrays containing the constants.
#bodacydo here is a program that may explain what other programmers are trying to convey but this would be in context of "integers"
#include <stdio.h>
int main()
{
int i , j;
int *x[2] = {&i, &j};
i = 10; j = 20;
printf("in main() address of i = %p, address of j = %p \r\n", &i, &j);
fun(x);
fun(x + 1);
return 0;
}
void fun(int **ptr)
{
printf("value(it would be an address) of decayed element received = %p, double dereferenced value is %d \r\n",*ptr, **ptr);
printf("the decayed value can also be printed as *(int **)ptr = %p \r\n", *(int **)ptr );
}
qsort() passes a pointer to the user-defined comparison function and as you have a char * (pointer to char array) hence your comparison function should dereference from pointer to pointer hence char **.