I have an array of pointers to string:
char *TAB[3] = { "dafafa", "alfkasf", "bafgr" };
I would like to sort characters in in each of those strings.
My compare function:
int cmp(const void *a, const void *b)
{
return *(char *)a - *(char *)b;
}
and while trying qsort on one of these:
qsort(TAB[0], 6, sizeof(char), cmp);
The program doesn't work.
After many efforts I found that the reason of the problem is in delivering TAB[0] to qsort().
Can anyone explain why it doesn't work and how to fix that?
If you want to sort characters inside each string, the first thing you must ensure is that your strings can be written to. As it currently stands, your strings are read-only, so you cannot sort their characters without copying their content into memory that allows writing.
Next thing is that you need a loop. Since you are sorting each string individually, you need to loop through the array, and call qsort on each item. The initial item is TAB[i], and the length is strlen(TAB[i]). Your cmp function will work.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have this code:
char** SplitToWords(char* str);
int main()
{
char** wordarr;
char str[] = "This is a sentence";
wordarr = SplitToWords(str);
return 0;
}
After the main comes the function implementation.
I am not sure the following does what I want it to do (i.e. receive an array of strings from a function):
wordarr = SplitToWords(str);
I somehow managed to convince the compiler that it's ok, but I assume it just does something else.
If it does, how do I find out the length of the array (the number of strings in it).
Thanks
I'll try to quickly visit all aspects you might not yet fully understand:
A string in C is described as a contiguous sequence of chars, ending with a char of value 0 (as a literal: '\0'). It is not a first class object, therefore hasn't its own type. So what you use to hold a string is an array of char. Therefore, taking your question by the word, "receive an array of strings from a function" is not possible.
An array is a contiguous sequence of objects of the same type. In C, the identifier of an array doesn't have a value itself; when it's evaluated, it decays as a pointer to the array's first element instead. This is especially important when passing arrays to functions or returning them from functions -- you can't actually pass the array, you always pass a pointer
e.g. you could write:
char x[] = "foo"; // initialize a char array from a string literal
char *xp = x; // here, x evaluates as a pointer to the first element of the array
You already use pointer types for your function's argument and return value, I just think it's quite important to understand what happens entirely.
You write char** SplitToWords(char* str); and ask whether this returns an "array of strings" -- well, sort of, as you should understand after reading 1. and 2. -- What it does is returning a pointer to char *. This pointer could be a pointer to the first element of an array. So in this case, it would return a pointer to an array of char * pointers. Each of these pointers could itself be a pointer to an array of chars, therefore point to a string. But what's very important is to understand you never return an array, you always return a pointer to it. It's so important because:
You might get the idea to do something like this:
char** SplitToWords(char* str)
{
char *words[16];
// code to fill `words` with pointers to the actual words
return words; // WRONG!
}
Here, because you're not returning the array words but a pointer to it (see point 2), you return a pointer to an object that no longer exists. words is in the scope of your function and has automatic storage duration, that means it only lives as long as the execution is inside of the function. One solution would be to declare words with the static storage class specifier. This way, it lives for the entire execution time of the program. But be aware that this also means there's only a single instance ever, it's always the same object. This will be a major headache for threaded programs, for example. The other way around is to dynamically allocate words using malloc(). But then, the caller of the function must free() it later.
As for your second question, how to let the caller know the number of words -- it's in the comments already, but just for completeness, a typical approach to solve this is to append another entry that is a NULL pointer. So the caller can iterate over the pointers until it finds NULL.
Regarding your comment, of course you can create the array outside the function and pass a pointer to the function, so the function only fills it. This is a common idiom in C (e.g. think about fgets(), which takes a pointer to the char array that's filled with a string by the function).
Functions working this way will need an additional size_t parameter, so they know the size of the array they should fill through the pointer, otherwise you'd have the risk of buffer overflows (this is why gets() was finally removed from the C standard). If you decide that the caller provides the storage, your function should have this prototype:
// returns the number of words found, up to `nwords`
size_t SplitToTwords(char **words, size_t nwords, char *str);
It should be called e.g. like this:
char *words[16];
size_t nwords = SplitToWords(words, 16, "the quick brown fox"); // returns 4
Remember that the strings holding the words themselves need storage as well. You can either manipulate the bytes in str to insert a '\0' after each word, overwriting the first whitespace character (this is what strtok() does) or you can copy the words to new strings, but then you would have to malloc() each of them again and the caller has to free() them later.
Yes, you could solve it by using a function with return value char **. However, there's no way to find out how many words there are afterwards.
You can solve this by allocating one more element for the return pointer and set it to NULL. Then you can get the number of words with this code:
wordarr = SplitToWords(str);
char **ptr=wordarr;
int noWords=0;
while(!*(ptr+noWords))
noWords++;
But if you want to return multiple data in C, you either need to define a return struct or using return arguments. In this case, it could look like this for the first option:
typedef struct wordList {
char **wordarr;
int noWords;
}
wordList SplitToWords(char* str);
And the second:
char** SplitToWords(char* str, int *noWords);
or
void SplitToWords(char* str, char*** wordarr, int *noWords);
Note that there's three *. That's because we want it to be a pointer to char **
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSTRINGS 5000
int main(int argc, char *argv[]) {
char *stringTable[MAXSTRINGS];
char sentence[] = "This is a sentence";
char *token = NULL;
int i = 0;
while ((token = strtok(token == NULL ? sentence : NULL, " ")) != NULL)
{
printf("%s\n\r", token);
stringTable[i] = (char *)malloc(strlen(token) + 1); //have no "plain" C compiler - VS C++ used so cast needed :)
strcpy(stringTable[i++], token);
}
stringTable[i] = NULL; // if you need to iterate through later
printf("%d tokens found\n\r", i);
for (int y = 0; y < i; y++)
free(stringTable[y]);
}
I am creating a program and part of it needs to take a word, and jumble the letters.
I know that there is officially no string data type within C, so technically the characters in the word are already in array? They just need sorting. (That is my understanding anyway).
I also know that C isn't very good for actual random numbers, I normally use the time as the seed, not sure if this would affect shuffling the letters.
For instance:
The word Hello
Split into Characters H/E/L/L/O
Shuffled E/L/O/H/L
New word Elohl
technically the characters in the word are already in array?
You can treat them like a null-terminated array of characters. Apply your favorite shuffle algorithm to the portion of the string between 0, inclusive, and strlen(str), exclusive to produce a shuffled string.
The only catch here is that not all strings can be shuffled in place. Specifically, strings representing string literals are not writable. Trying to change them would lead to undefined behavior.
For example, if you do
char *word = "hello";
shuffle(word);
and try to modify word's characters inside shuffle, you would get undefined behavior. You need to copy the content into a writable array before you can shuffle the content - for example, like this:
char word[] = "hello";
shuffle(word);
int compare(const void *a, const void *b){
return *(const char *)a - *(const char *)b;
}
char arr[] = "dbaurjvgeofx";
printf("Unsorted: %s\n", arr);
qsort(arr, strlen(arr), 1, compare);
printf("Sorted: %s\n", arr);
am trying to write a function to parser command line arguments into a vector. The problem is i can't seem to eliminate the use of a global array of pointers as the vector.
The code is:
/** parse command line arguments into a global vector */
char vects[8][32] = {0};
int str_tokenizer(const char str[], char delim)
{
extern char vects[8][32];
int i,num_delim=0, num_vects;
int str_len=strlen(str);
for(i=0;i<str_len;i++)
{
if(str[i]==delim)
num_delim++;
}
num_vects=num_delim+1;
int x=0;
vects[num_vects][32];
for(i=0;i<num_vects;i++)
{
//printf("%i", i);
int y=0;
while(str[x]!=delim && str[x]!='\0')
{
//printf("%c", str[x]);
vects[i][y++]=str[x++];
}
vects[i][y]='\0';
x++;
}
return (num_vects);
}
Any help will be appreciated
I think you're doing too much in one function. You should separate the tasks of parsing (just isolating the substrings of the original string, ie. discovering the starting and ending indices) on the one hand, and adding strings to a list on the other hand. To replace a 2D array, you can use a linked-list. But the parsing code should not have to worry about this, just scan for the next delimiter. Subtract the ending position from the starting position to get the length, and call an append_string_to_list() function, passing the full source string and the starting index and length.
By the way, you refer to this array
char vects[8][32] = {0};
as a "global array of pointers". It is not an array of pointers. It is an array of arrays of characters. To be sure, if you use a slice of the array, by not specifying the second index, then it will work as if it were a pointer -- and it will decay to a pointer in a function-call -- but a real array of pointers would look like this:
char *vects[8];
As a learning technique, i'm suppose to make my own copy of the following string function in
char * mystrcpy(char *a, char *b);
// string copy. destroys a but not b.
// identical to strcpy in <string.h>
// running time O(mystrlen(b))
I've come with this
char * mystrcpy(char *a, char *b){
a = b;
return a;
}
since string a is a random chuck in memory I'm thinking to assign just to string b ... is my interpretation correct ?
accessing a specific char [in index i] in string is done using a[i], just like an array. [remember that in C, a string is actually an array of chars].
You should iterate the strings until you "see" a '\0' char - which indicate the end of string.
Yes, comparing to chars with operator< is comparing them by their ascii value - which is probably what you need.
Trying to follow this example. (Section String sorting...)
Is there anything obvious that would make this crash in stdlib's qsort.c?
I also tried cstring_cmp with strncmp specifying 30 chars max, much more than I have.
*fileArray[20] seems to be correctly populated with strings.
Thanks.
char* ptr_fileName;
char* fileArray[20];//number of files
size_t strings_len;
ptr_fileName = (char*)malloc((strlen(FindFileData.cFileName)+1)*sizeof(char));
memcpy(ptr_fileName, FindFileData.cFileName, strlen(FindFileData.cFileName)+1);
fileArray[i] = ptr_fileName;
strings_len = sizeof(fileArray) / sizeof(char *);
qsort(fileArray, strings_len, sizeof(char *), cstring_cmp);
//crashing in qsort.c
qsort c-string compare function:
/* qsort C-string comparison 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 */
}
You say you are only filling fileArray with 10 strings, leaving 10 entries uninitialized.
When you call qsort, you pass 20 as the strings_len argument.
This will, of course, result in undefined behavior.
You have to give qsort accurate information.
If you are passing 10 strings in the array, you must also pass the number 10 as the number of elements to be sorted.
Note:
If you follow my earlier answer, setting a breakpoint on cstring_cmp, you would quickly see when the compare method is called with invalid data, leading directly to the crash.
How do you populate:
char* fileArray[20];
as it stands, it's an array of uninitialised pointers.
Set a breakpoint in cstring_cmp, and watch as it is called each time.
See if the ultimate crash happens inside cstring_cmp, or in qsort.
Check the state of the fileArray just before the crash.
*fileArray[20] seems to be correctly populated with strings.
The asterisk before fileArray makes me veeeery suspicious about the correctness of the way you populated the array.
I don't observe anything else that might break your code.
As you are not initialising the contents of fileArray, it legal contains random memory, not legal char pointers.