Trying to print a string in a qsort function - c

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

Related

type + pointer array size + paranthesis meaning in syntax

I have a midterm in Saturday, so our teacher gave us the previous year's midterm to help. So there is a problem that I have trouble to understand. The question is 'fill the question marks so that program outputs the string 'accioccium'.
For who do not want to spend on solving, the answers are 1,2 and spell2.
So what is happening here? Especially, what is char* (*a[3]) (char *, char *)? I mean we are creating a char pointer array but what is that paranthesis at the end?
Also, a[0] = fun1 is weird too. Yes, you can assign variables by functions. However, there is no parameter here for fun1 function. Let us say parameters are constant as *s and *p. But this time, what are they?
#include <stdio.h>
#include <string.h>
char* fun1(char *s, char *p) {
return s+strspn(s,p);}
char* fun2(char *s,char *p) {
return strcat(s,p);}
char* fun3(char *s,char *p){
return strstr(s,p);}
char* (*a[3]) (char *,char *);
int main(void) {
int i;
char spell1[10] = "accio";
char spell2[10] = "aparecium";
char* result;
a[0] = fun1;
a[1] = fun2;
a[2] = fun3;
result = (*a[?]) (spell1, "c");
printf("%s",result);
result = (*a[?]) (?, "c");
printf("%s",result);
return 0;}
Thank you for your time and help.
Especially, what is char* (*a[3]) (char *, char *) ? I mean we are creating a char pointer array but what is that paranthesis at the end?
It's an array of 3 pointers to function that takes 2 arguments pointer to char and returns a pointer to char.
The assignment a[0] = fun1 doesn't need the arguments, fun is not executed, only assigned to the compatible pointer a[0], you only add the parameters later when you actually want to execute the function, which is done for example in the line result = (*a[?]) (spell1, "c");
char* (*a[3]) (char *,char *);
Starting from a and "going clockwise" or "picking things on the right first":
a
[ is array
3]) of 3
(* pointers
(...) at end, to function
char *,char * taking two parameters of type char*
char* at the start, returning value of type char*
This matches signatures of fun1, fun2 snd fun3. and indeed array is filled with pointers to these 3 functions. Function name without () means pointer to function, it does not call the function, and using & to get address is not needed.

Passing pointer arguments in C functions

#include <stdio.h>
// this works
void print_stuff (void* buf) {
printf ("passed arg as buf*: %s\n", buf);
}
/* This works */
void print_stuff_3 (char* buf) {
printf ("passed arg as char*: %s\n", buf);
}
// this does not work
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}
int main () {
char s [] = "hi";
printf ("s = %s\n", s);
// these work
print_stuff (&s);
print_stuff_3 (&s);
// this results in a Segfault
print_stuff_2(&s);
return 0;
}
I am a bit confused about the way things are passed around in C. I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behaviour happen?
In particular, print_stuff_2 segfaults, whereas I thought that print_stuff_3 would give an error.
EDIT: To clarify, I expected print_stuff(&s) and print_stuff_3(&s) to fail (while they succeed), while print_stuff_2(&s) fails, whereas I feel it should succeed.
You need to remember that strings are not fundamental types in C. They are arrays of characters. Therefore
char s [] = "hi";
makes s a char * (in terms of variable type), i.e. a pointer to the first character of a 3 character array (h, i and NUL).
So in order to pass a pointer to the string, you what to use your print_stuff_3, as printf()'s %s argument takes exactly that (a pointer to the string, i.e. a pointer to the first character). Call this with print_stuff_3(s).
print_stuff works because a pointer is a pointer. It will be translated to a void * pointer on calling print_stuff, then printf()'s %s will convert it back to a char *. Call this with print_stuff(s).
print_stuff_2 doesn't work because you are taking the address of where s is stored. Had you written char *s = "hi"; that would work if you used print_stuff_2(&s). You'd pass the address of the pointer, then dereference that (to get the value of the pointer, i.e. the pointer to the first character) in by using *buf. Except buf then would be a poor choice of name, as you would be passing a pointer to a pointer to characters.
The complication is as follows. As it is, you are doing &s which just returns s when you have
char s [] = "hi";
(see How come an array's address is equal to its value in C? ), but returns the address at which the pointer variable s is stored on the stack if you have:
char *s = "hi";
Taking the address of an array doesn't really make sense (so evaluates to the address of the first element). You need to use char *s = "hi"; if you want to take the address of the pointer.
In C, array names are decays to pointer to its first element when passed to a function in most cases. When passing s to the function print_stuff, s decays to pointer to h. No need to pass it with &. &s is of pointer to array (char (*)[3]) type, i.e, it is giving the address of the entire array s.
In function call
print_stuff_3 (&s);
your compiler should warn you
[Warning] passing argument 1 of 'print_stuff_3' from incompatible pointer type [enabled by default]
I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behavior happen?
No. You thought wrong. &s is of type char (*)[3].
void print_stuff (void* buf) & void print_stuff_3 (char* buf) In both functions, buf is of char * taking address as argument. Which should be print_stuff (s) & print_stuff_3 (s) respectively as s is the base address of char array s. So you shouldn't pass &s which is address of s.
As the below function buf is of type char **, it will expect address of address like print_stuff_2(&s) provided your declaration is char *s = "hi",
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}

QSORT function in C

In the following code, once I remove the commented part which compares strings, I am getting a seg 11 fault. I am unable to understand why! Rest of the code is working fine. Any help is appreciated!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_scores_desc(const void* scorea, const void* scoreb){
int a = *(int*)scorea;
int b = *(int*)scoreb;
return a-b;
}
int compare_names(const void* namea, const void* nameb){
char** a = *(char**)namea;
char** b = *(char**)nameb;
return strcmp(*a,*b);
}
int main(int argc, char* argv[]){
int scores[7] = {456,234,65,563,67,19,100};
int i;
qsort(scores,7,sizeof(int),compare_scores_desc);
puts("\nThese are the scores in order : \n");
for(i=0;i<7;i++)
printf("%i\n",scores[i]);
char *names[] = {"Krishna","Rama","Bhishma","Arjuna"};
/*qsort(names,4,sizeof(char*),compare_names);*/
puts("------------------");
puts("The names in order are : \n");
for(i=0;i<4;i++)
printf("%s\n",names[i]);
return 0;
}
In compare_names(), you are inappropriately dereferencing the arguments after the cast. The types for the local variables are type char **, but you are casting the arguments as char ** and dereferencing that results in a char *.
namea and nameb are pointers to the elements of your array names[] declared in main(). That means, their types are actually pointer to char *. When you dereferenced these arguments but assigned them to a char **, you cause the local variable to treat the char * as a char ** (your compiler should have issued a diagnostic warning you about this problem). Now, you take a pointer value that is a char *, and dereference it when you pass it to strcmp(). This causes the program to treat sizeof(char *) bytes of the string as a pointer value for the strcmp() function. Since 4 or 8 (or whatever sizeof(char *) is) bytes consisting of printable characters reinterpreted as a pointer value rarely yields a valid pointer, when strcmp() tries to use those pointers, a segmentation fault occurs.
One possible fix is to not dereference when you initialize your local variables. However, the arguments are const void *, so you can avoid the cast altogether if you declare your local variables to be a pointer to a const type:
int compare_names(const void* namea, const void* nameb){
char* const * a = namea;
char* const * b = nameb;
return strcmp(*a,*b);
}
Note that your implementation of compare_scores_desc() fails if a - b results in signed integer overflow. For example, if a is INT_MAX and b is -1. You should fix your implementation to work for all cases.
int compare_scores_desc(const void* scorea, const void* scoreb){
const int *a = scorea;
const int *b = scoreb;
return (*a > *b) - (*a < *b);
}
The problem is in your string comparison function, and here is probably the minimal way to fix it:
int compare_names(const void* namea, const void* nameb){
char* a = *(char**)namea;
char* b = *(char**)nameb;
return strcmp(a,b);
}
The namea and nameb arguments are pointers into the string vector. You understand this, which is why you used the char ** type.
However, all you have to do in the function is retrieve the char * pointers from that array. These char * pointers are already strings. You do not have to dereference them again; just pass them to strcmp.
Your original code has a constraint violation which requires a diagnostic. That should have tipped you off:
/* originally */
char** a = *(char**)namea; /* error: initialization from incompatible type */
You're dereferencing a char **, which produces char *, but you're storing that in a char ** again and dereferencing again, thereby wrongly treating the character data as a pointer.

Confusing pointers and arrays in C

I'm trying to understand the mistake in the following code. The code is supposed to switch between two arrays.
What I saw is that it switches only the first 4 bytes. Is the following correct?
Passing &num1 or num1 is the same (both pass the address of the first element in the array).
The (char**) casting is wrong. That's because when you pass and array you pass the address it's laid in. So you actually pass here a void*.
How can I actually switch between these two arrays only by pointers? Is thatpossible?
I know it is possible if from the beginning I had defined char **num1 and char **num2. But I want it to stay with the array notation!
#include <stdio.h>
void fastSwap (char **i, char **d)
{
char *t = *d;
*d = *i;
*i = t;
}
int main ()
{
char num1[] = "hello";
char num2[] = "class";
fastSwap ((char**)&num1,(char**)&num2);
printf ("%s\n",num1);
printf ("%s\n",num2);
return 0;
}
Passing &num1 or num1 is the same (both pass the address of the first element in the array). Am I correct?
No. The first one is a pointer to the array itself (of type char (*)[6]), whereas in the second case, you have a pointer to the first element (of type char *; an array decays into a pointer to its first element when passed to a function).
The (char*) casting is wrong
Indeed, you are casting a char (*)[6] to a char **.
So you actualy pass here a void (Am i correct?).
No. Non sequitur. I don't see how the void type is relevant here. You have pointers, arrays, and eventually pointers to arrays.
Arrays are not pointers. Your code is trying to swap arrays, which does not make sense, since assignment to arrays is not permitted. What you probably want is
I. either get pointers to the first character of each string, and then swap the pointers themselves, like this:
void swap_pointers(const char **a, const char **b)
{
const char *tmp = *b;
*b = *a;
*a = tmp;
}
const char *p1 = "hello";
const char *p2 = "world";
swap_pointers(&p1, &p2);
II. Or use actual arrays, and you swap their contents:
void swap_contents(char *a, char *b, size_t n)
{
for (size_t i = 0; i < n; i++) {
char tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
char a1[] = "hello";
char a2[] = "world";
swap_contents(a1, a2, strlen(a1));
Also, you may want to read this.
1. Passing &num1 or num1 is the same (both pass the address of the first element in the array)
Not true, &num1 gives you a pointer to a pointer that points to the entire character string "hello" (char*[6]) while num1 is just a pointer to the character block "hello" (char[6]).
2. The (char*) casting is wrong. That's because When you pass and array you pass the address it's laid in. So you actualy pass here a void (Am i correct?).
It is still not a void, it's just a pointer to a character pointer. If it were void, then it would be perfectly valid to do something like void** myVoid = &num1. This will cause a syntax error unless you explicitly typecast your char** to a void** before you assign it.
The problem is your explicit type casting &num1 as a char** which is not correct, it is a char*[6]. But of course, you can't declare a variable as a char*[6] so it can't be used in this way. To fix it you need to declare your num1 and num2 as:
char* num1 = "hello";
char* num2 = "class";
instead and keep everything else the same. In fact, with this change there is no need to typecast your &num1 as a char** because it already is that.

How to qsort an array of pointers to char in C?

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 **.

Resources