Why this qsort() doesn't work? - c

Am sorting an array of strings (case insensitive).
qsort causes segmentation fault, probably my casting isn't proper.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *string1, const void *string2) {
char *a = (char*)(string1);
char *b = (char*)(string2);
printf("comparing %s AND %s\n", a, b);
return strcasecmp(a,b);
}
void sortListName(char **fileList, int noOfFiles) {
printf("Sorting\n");
qsort(fileList, noOfFiles, 260*sizeof(char), compare);
return;
}
**fileList = array of strings (filenames)
P.S. main() is obvious and works fine.

If this is all the code you have related to the qsort, it looks like you declared the comparePtr function pointer, but it's still not initialized; it's not pointing to your compare function (which is what I assume you wanted it to point to).
After that, a few more things:
1) comparePtr has the correct types, but compare does not. It needs to take in two const void*, but you have two const void**.
2) Once you fix the types, you could just pass compare to qsort, instead of making a function pointer and passing that.
3) I'm not convinced the first argument to qsort is correct. You want to be passing in the pointer to the first element in the array, which ought to just be fileList (I'm assuming it points to the first string in your array).
4) The third argument isn't correct either. fileList is a char**, meaning you're passing in an array of char *s, and hence the third argument should just be sizeof(char*), not the strlens of the strings.

I would adjust things so that you're just sorting a simple array, in this case of pointers to char - qsort will arrange for you to get pointers to two elements in that array (that is, char ** pointers), and some basic dereferencing is needed to get you to the "pointers to char" comparable via strcasecmp. #Mark likely has sussed out the source of the 260 in your unseen calling code, but I'm not a big fan of those kinds of 2d arrays in C.
The following functions for me, with an example main() to exercise it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *v1, const void *v2){
char *a = *(char **)v1;
char *b = *(char **)v2;
printf("comparing %s to %s\n", a, b);
return strcasecmp(a,b);
}
void sortListName(char **fileList, int noOfFiles){
printf("Sorting\n");
qsort(fileList, noOfFiles, sizeof(*fileList), compare);
return;
}
int
main(void)
{
char *filenames[] = {
"/var/www/icons/comp.gray.png",
"/var/www/error/HTTP_SERVICE_UNAVAILABLE.html.var",
"/var/www/icons/right.gif",
"/var/www/error/HTTP_NOT_IMPLEMENTED.html.var",
"/var/www/icons/pie3.png",
"/var/www/icons/pie2.png",
"/var/www/htdocs/manual/mod/mod_proxy_balancer.html",
"/var/www/htdocs/manual/programs/rotatelogs.html",
"/var/www/htdocs/manual/vhosts/mass.html",
"/var/www/icons/movie.png",
"/var/www/htdocs/manual/images/caching_fig1.png",
"/var/www/htdocs/htdig/search.html",
"/var/www/icons/generic.gif",
"/var/www/htdocs/manual/mod/quickreference.html",
"/var/www/icons/small/blank.png",
"/var/www/icons/image2.gif"
};
int i, nf = (int) (sizeof(filenames) / sizeof(filenames[0]));
puts("Unsorted:");
for (i = 0; i < nf; i++) {
puts(filenames[i]);
}
sortListName(filenames, nf);
puts("Sorted:");
for (i = 0; i < nf; i++) {
puts(filenames[i]);
}
return 0;
}

Related

How to sort an array of pointers in alphabetical order, and then use qsort?

I am trying to write a function in which I sort the pointers inside of wptrs, an array of pointers to strings in another array. I am challenging myself not to use string.h for this exercise, as I want to understand how a sorting algorithm could work in C. I am using qsort(), however, but I am trying to write a comparison function for it called mycharptrcompare().
I have looked at how strcmp() works, and I have tried to mimic that with mycharptrcompare(). However, I notice the difference that strcmp() expects a char*, while the mycharptrcompare() function expects a char**. I've written a method called dumpwptrs to show me the contents and how they are organized within wptrs. Thus far, I have the following code:
UPDATE:
I have also tried:
int mycharptrcompare(const void *a, const void *b)
{
//Need to convert a void * to a more specific type to dereference
const char *aPtr = a;
const char *bPtr = b;
const char **pa = &aPtr;
const char **pb = &bPtr;
while (*pa && *pa == *pb) {
pa++;
pb++;
}
return *pa - *pb;
}
and my output I got was:
(null)
jumps
world
is
dog
blue
Which is still incorrect, because my list should be sorted in alphabetical order, and the first input (the word "hello"), has not been read in.
FYI here is an example use of qsort().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp(const void *a, const void *b)
{
const char **pa = a;
const char **pb = b;
return strcmp(*pa, *pb);
}
int main(void)
{
char *wptrs[] = { "hello", "jumps", "world", "is", "dog", "blue" };
size_t len = sizeof wptrs / sizeof wptrs[0];
qsort(wptrs, len, sizeof wptrs[0], cmp);
for(size_t i = 0; i < len; i++) {
printf("%s\n", wptrs[i]);
}
return 0;
}
Program output:
blue
dog
hello
is
jumps
world

Swapping strings WITHOUT using double pointers

This is actually just a small part of the original assignment I have in algorithms and data structure. At some point I'm supposed to write a function that swaps two elements in an array of strings.
The prototype we have to use is:
void swap(char *a, char *b);
To me it makes much more sense passing double pointers to this function (if swapping strings). In fact, I don't know how to make their prototype work without using double pointers. Am I missing something or is this wrong?
I've done my research on this a while ago and there are many answered questions here, I'm just wondering whether the person who was designing this assignment made a mistake.
You can swap two strings by creating a temporary char array of length equal to either one of the two strings, and by using strcpy().
Of course, you need to make sure that swapping does not overflow one of the two strings/arrays. Here is a sample example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swap(char *a, char *b)
{
char *tmp = malloc(strlen(a) + 1);
strcpy(tmp, a);
strcpy(a, b);
strcpy(b, tmp);
free(tmp);
}
int main(void)
{
char str_a[50] = "Hello world";
char str_b[50] = "What's up";
swap(str_a, str_b);
puts(str_a);
puts(str_b);
return 0;
}
Another approach is to use a loop with a single char variable, thus avoiding dynamic memory allocation:
void swap(char *a, char *b)
{
char tmp;
while (*a && *b)
{
tmp = *a;
*a++ = *b;
*b++ = tmp;
}
if (!*a)
{
size_t n = 0;
while (b[n])
{
*a++ = b[n++];
}
}
else if (!*b)
{
size_t n = 0;
while (a[n])
{
*b++ = a[n++];
}
}
*a = '\0';
*b = '\0';
}

Swap without worrying about the data type

Is that implementation valid and safe to swap two array or variable without worrying about their data type? Or should I use a function pointer?.
This type of code is focusing about using a good implementation of void pointers to swap without worring about the data type.
#include <stdio.h>
#include <string.h>
void swap(void *, void *, int);
int main(void) {
char a[] = "home";
char b[] = "door";
printf("%s %s\n", a, b);
swap(&a, &b, sizeof(a));
printf("%s %s \n", a, b);
return 0;
}
void swap( void *a, void *b, int siz){
char buff[siz]; // I voluntarily omitted dynamic allocation.
memcpy(buff,a,siz);
memcpy(a,b,siz);
memcpy(b,buff,siz);
}
In C, this approach is usually okay (and it's used by standard functions such as qsort()). The contra-indications that indicate you can't use this are when there are any pointers to your objects or their members. Also, take care in multi-threaded code.
Note that in C++, we have std::swap() which will respect user-defined copy/move constructors; copying a C++ object by simply copying its memory is not (in general) valid.
This swap() function is as good/bad as the memcpy() it uses.
If the data structures are just some data structures (of int, float, etc.), it works like a charm.
If you pass two pointers to different structures, all hell will break loose. Offending code:
Foo* myFoo = ...;
Bar* myBar = ...;
swap(myFoo, myBar, sizeof(*myFoo));
Note that your compiler won't complain on this as both pointer types are implicitly convertible to the void*s that swap() expects. But the result of compilation will be bullshit.
If the structure you copy contains a pointer into itself, that pointer will point into the other object after the swap(). The following struct would be an offender of this:
typedef struct {
char* data;
size_t length, allocatedLength;
char shortStringBuffer[32];
} myString;
The idea behind this is, that short strings will be stored in the shortStringBuffer, and data will point to the shortStringBuffer. Strings longer than 31 characters will be stored in dynamically allocated memory, and again be accessible via the data member. It is left as an exercise to the reader to figure out, what precisely happens when you try to copy this thing with a memcpy().
What you must understand, is that memcpy() really only copies bytes, and some data is not invariant to where it is stored. So, each and every use of memcpy() must be accompanied with a proof that it does the correct thing in this particular case. Well, it should. I've never seen such a proof in a comment for some reason...
You can just swap their addresses without further operation to swap two variables/arrays. I tried this and it works:
#include <stdio.h>
int main(void) {
char *a = "home";
char *b = "root";
char *c = a, *d = b;
printf("%s %s\n", a, b);
a = d;
b = c;
printf("%s %s \n", a, b);
return 0;
}
Outputs:
home root
root home
https://ideone.com/MMCOpf
It is safe to use generic pointers to swap, however you must make sure you get the sizes right, and that you don't overflow any array or object:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int swap(void *a, void *b,
size_t a_size, size_t b_size);
int main(void) {
char a[] = "home";
char b[] = "door";
printf("%s %s\n", a, b);
int ret = swap(a, b, sizeof(a), sizeof(b));
if(ret) {
printf("%s %s \n", a, b);
}
return ret;
}
int swap(void *a, void *b,
size_t a_size, size_t b_size)
{
if (b_size != a_size ) {
return 0;
}
void *tmp = malloc(a_size);
if(!tmp) {
return 0;
}
memcpy(tmp, a, a_size);
memcpy(a, b, b_size);
memcpy(b, tmp, a_size);
free(tmp); // tmp no longer needed.
return 1;
}
I solved this by C, code for your reference.
Using uint16_t is neccessary to hold the carry bit when two large uint8_t variables plus.
int main(){
uint8_t tmp[9] = {0x0};
for (int i=9-1; i>=0 ; i--) *(tmp+i) = 0xff-i;
uint16_t tmp2[9];
for (int i=0; i<9; i++) tmp2[i] = tmp[i];
byte_swap_uint8_data(tmp2, 9);
}
void byte_swap_uint8_data(uint16_t* data, int w) {
if (w < 2) return;
for (int i=0; i<w/2; i++) {
data[i] += data[w-1-i];
data[w-1-i] = data[i] - data[w-1-i];
data[i] = data[i] - data[w-1-i];
}
}

Why strcpy doesn't work but direct assign works?

In the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char** tab;
int n;
}slist;
void print(slist* p);
void add(slist* p, const char* s);
void add(slist* p, const char* s)
{
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
strcpy(p->tab[p->n],s);
p->n=p->n+1;
}
void print(slist* p)
{
int i;
printf("[");
for(i=0;i<p->n;i++)
printf(" %s",p->tab[i]);
printf(" ]");
}
int main()
{
char s1[25] = "Picsou";
char s2[25] = "Flairsou";
slist* p = (slist*)malloc(sizeof(slist));
p->n=0;
p->tab=NULL;
add(p,s1);
add(p,s2);
print(p);
return 0;
}
the function add() doesn't work, but if I change it to:
void add(slist* p, const char* s)
{
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
p->tab[p->n]=s;
p->n=p->n+1;
}
it seems to work perfectly well. In the first case the output is only " [";
in the second case it is what is should be: " [ Picsou Flairsou ] ".
I cannot understand why.
I also tried this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char** tab;
int n;
}slist;
void print(slist* p);
void add(slist* p, const char* s);
void print(slist* p)
{
int i;
printf("[");
for(i=0;i<p->n;i++)
printf(" %s",p->tab[i]);
printf(" ]");
}
void add(slist* p, const char* s)
{
slist* tmp = (slist*)malloc(sizeof(slist));
tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
int i;
for(i=0;i<p->n;i++)
tmp->tab[i]=(char*)malloc(sizeof(char));
strcpy(tmp->tab[p->n],s);
tmp->n=p->n+1;
p = tmp;
}
int main()
{
char* s1 = "Picsou";
char* s2 = "Flairsou";
slist* p = (slist*)malloc(sizeof(slist));
p->n=0;
p->tab=NULL;
add(p,s1);
add(p,s2);
print(p);
return 0;
}
Lots of errors here, which is common with people who are new to dealing with pointers. Where to begin... I'll just go in the order things appear in code.
This is not a "list".
It's an array... sort of. If you say "list" to a C programmer, they will think you mean a linked-list.
Incorrect allocation of array.
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
Here you have allocated enough to store a single pointer. The second time you call add, you're going to access memory off the end. You have also incorrectly cast the result (in C, you don't cast the return value from malloc). Additionally, you have given confusing information to the reader, because you intend to allocate an array that will hold elements of type char*, NOT char**.
You must either allow the array to expand dynamically when required (not appropriate for your abilities right now - maybe try that in a few days), or set a maximum size. Let's do that.
const int MAX_SIZE = 100;
if( p->n==0 )
{
p->tab = malloc( MAX_SIZE * sizeof(char*) );
}
else if( p->n == MAX_SIZE )
{
printf( "Maximum size exceeded!\n" );
return;
}
You could use calloc instead of malloc if you like. It will zero-initialise the block after allocating it: calloc( MAX_SIZE, sizeof(char*) )
Copying to an uninitialized pointer.
strcpy(p->tab[p->n],s);
You allocated memory for tab, but you did not allocate memory that is pointed to by its elements, and here you have undefined behaviour (most likely resulting in a segmentation fault, but could do anything).
Make sure you have a valid pointer, and the location it points has enough storage reserved for the data you are copying into it:
p->tab[p->n] = malloc( strlen(s) + 1 );
strcpy( p->tab[p->n], s );
Storing potentially invalid pointer.
Your alternative that "works perfectly well" uses:
p->tab[p->n]=s;
However, the only reason this works is because those pointers remain valid for the whole time that you use the "list" (but actually the program does not "work" because of reasons I highlighted in number 2.
Sometimes we desire this behaviour in a program, and design our data structures to index pointers that they do not own. But more often, and especially for a beginner, you are better off copying the data (instead of simply copying the pointer). And so you'll instead use the approach I've suggested in number 3 above.
No comment!!
There are so many things wrong with the following code, that I'm not going to pull it apart or explain them.
void add(slist* p, const char* s)
{
slist* tmp = (slist*)malloc(sizeof(slist));
tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
int i;
for(i=0;i<p->n;i++)
tmp->tab[i]=(char*)malloc(sizeof(char));
strcpy(tmp->tab[p->n],s);
tmp->n=p->n+1;
p = tmp;
}
However, it appears like you were attempting to do something similar to realloc. This is the option I mentioned in number 2 that I said you're maybe not ready for. But read up on it anyway: realloc

How to reverse an array of strings and reverse each string in place using pointers?

I am learning C and I'm trying to reverse each string in an array in place with pointers. When i run the code below, I get the warnings that passing argument 1 and 2 of 'swapChars' makes pointer from integer without a cast. When I run it, I get a "bus error". Does anyone know why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swapChars(char *a, char *b) {
char temp = *a;
*a = *b;
*b = temp;
}
void reverse(int size, char* arr[]) {
for(int w = 0; w < size; w++) {
int length = strlen(arr[w]);
for(int i = 0; i < length/2; i++) {
swapChars(arr[w][i], arr[w][length-i]);
}
}
}
EDIT: thank you! I don't get any errors/warnings anymore, but do you know why it doesn't print anything when i run it with "hello world" ?
this is my main():
int main(int argc, char* argv[]) {
int numWords = argc-1;
char** words = argv+1;
reverse(numWords,words);
for (char** word = words; word < words+numWords; word++) {
printf("%s ",*word);
}
printf("\n");
return 0;
}
swapChars( &(arr[w][i]), &(arr[w][(length-1)-i]));
You need to pass in the address of ( or "pointer to" ) the array element, not the array element itself. Without this the compiler is warning you that it will try to convert the array element's value into a pointer - which it suspects is the wrong thing to do. So it warns you about this. And indeed - it is not what you intended.
#Pi is more right than I am. Fixed it. Add the -1 in the second argument because otherwise the null at the end of the string gets reversed also. That is why your string doesn't print. Probably.
When you are passing your arguments, you are dereferencing both values that you are passing in.
To correct this, the line should read
swapChars(&arr[w][i], &arr[w][length-1-i]);
This will pass in the addresses, rather than the values.

Resources