Sorting an array of strings in C - c

I have an assignment I've been working on for a few hours now, and I can't seem to get it quite right. The assignment is to take a random number of names (from stdin), sort them, and then output them in alphabetical order. I can't find any sites online that handle this kind of sorting specifically, and have had no luck trying to implement qsort() into my code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int stringcmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
void main(int argc, char *argv[])
{
char *input[] = {" "};
char temp[20][20];
int i = 0;
int num = 0;
int place = 0;
int stringlen = sizeof(temp) / sizeof(char);
printf("How many names would you like to enter? ");
scanf("%d", &num);
while (place < num)
{
printf("Please input a name(first only): ");
scanf("%s", input[place]);
printf("The name you entered is: ");
printf("%s\n", input[place]);
place++;
}
//qsort(temp, stringlen, sizeof(char *), stringcmp); <-- just an idea I was messing with
qsort(input, stringlen, sizeof(char *), stringcmp);
printf("Names:\n");
for(i=0; i<place; i++)
printf("%s\n", input[i]);
system("PAUSE");
return(EXIT_SUCCESS);
}
The main problem is, when I go to output my code, I cannot use the char *input variable because of how its declared. The temp[] will display, but will not be sorted by qsort because it is not declared as a pointer. Any ideas?

You can't declare your input array like that. Since you know how many the user requires, you can dynamically allocate the array:
char **input = malloc(num * sizeof(char*));
Likewise, when you read your strings in, they need somewhere to go. Simply passing an uninitialized pointer to scanf is not okay. I suggest you define the maximum length of a name and have a temporary buffer for reading it:
const size_t MAX_NAME = 50;
char name[MAX_NAME];
...
for( i = 0; i < num; i++ )
{
printf("Please input a name(first only): ");
scanf("%s", name);
input[i] = strdup(name);
}
[Note this does not prevent the user from overflowing the 'name' buffer. I used scanf for illustrative purposes only]
You seem to be passing the wrong array length to qsort. Try this:
qsort(input, num, sizeof(char *), stringcmp);
When you are finished, you need to release memory for all the names and the array.
for( i = 0; i < num; i++ ) free(input[i]);
free(input);
could you explain
the ** declarations throughout the code? I'm not sure what they're
used for, although I know the function for stringcmp is a widely used
algorithm, I have no idea how it works; I'm thrown off by the double
de-reference markers.
Yep, in the case where I used it, I am telling C that to get a single character, I have to dereference a pointer twice. When you index a pointer, it's dereferencing. So I allocated an array by requesting a block of memory containing num * sizeof(char*) bytes. Because I assigned that pointer to a char**, the compiler knows that I am pointing to a chunk of memory that contains char* values.
If I ask for input[0] (this is the same as *input) it should look at the very start of that memory and pull out enough bytes to form a char*. When I ask for input[1], it skips past those bytes and pulls out the next bunch of bytes that form a char*. Etc... Likewise, when I index a char*, I am pulling out single characters.
In your stringcmp function, you have the following situation. You passed a void* pointer to qsort so it doesn't actually know the size of the data values stored in your array. That's why you have to pass both the array length AND the size of a single element. So qsort just blindly rips through this arbitrary-length array of arbitrary-sized values and fires off memory addresses that ought to contain your data for comparison. Because qsort doesn't know anything else about your array elements except where they are located, it just uses void*.
But YOU know that those pointers are going to be the memory addresses of two of your array elements, and that your array elements are char*. So you need the address of a char* (hence you cast the pointers to char**). Now you need to dereference these pointers when you call strcmp() because that function requires a char* (ie a value that points directly to the memory containing your string characters). That is why you use the * in strcmp(*ia, *ib).

One quick way to fix your program is to declare input as an array of pointers, like this:
char *input[20];
When you read names in, use tmp[place] for your buffer, and store the pointer into input, like this:
scanf("%19s", tmp[place]);
input[place] = tmp[place];
Now sorting the input should work fine.
This has a limitation of being limited to 20 lines of 20 characters max. If you learned about malloc in the class, you should be able to fix that by allocating your strings and the string array dynamically.

Related

Best way to initialize an array of strings to pass it to a function

I need to intialize an empty array of strings with fixed size ( 3 by 100 for example), pass it to a function to fill it with data and perform things like strcpy(), strcmp(), memset() on it. After the function is terminated I need to be able to read the data from my main().
What I tried so far:
char arrayofstrings[3][100] = {0};
char (*pointer)[3][100] = &arrayofstrings;
function(pointer);
Initalizing an (empty?) array of strings and initializing a pointer on the first element.
int function (char (*pointer)[3][100])
{
strcpy((*pointer)[i], somepointertostring);
strcmp((*pointer)[i], somepointertostring)
memset((*pointer)[i], 0, strlen((*pointer)[i]));
}
Is this a good way to do it? Is there an easier way to do it? Whats up with the brackets around the pointer?
C string functions expect a buffer to be null-terminated. Your arrayofstrings allocation happens on the stack. Depending on your compiler it might be initialized to all zeros or might contain garbage.
The simplest way in your case to make sure string functions won't overrun your buffers is to set the first character of each to 0 (null)
arrayofstrings[0][0] = 0x00;
arrayofstrings[1][0] = 0x00;
arrayofstrings[2][0] = 0x00;
This will give you 3, 100-char buffers that contain a valid empty "string". Note that you can only store 99 "characters" because the last character must be 0x00 (null-terminator).
char (*pointer)[3][100] = &arrayofstrings;
This is unnecessary.
Something to keep in mind about arrays in C is that the [] index is really only there to make things easier for the human programmer. Any array definition is simply a pointer to memory. The values inside the [][]...[] indexes and the type are used by the compiler to allocate the right amount of memory on the stack and do some simple math to come up with the right memory address for the element you want to access.
char arrayofstrings[3][100];
This will allocate sizeof(char)*3*100 bytes on the stack and give you a char* called 'arrayofstrings'. There's nothing special about the char* itself. It would be the same pointer if you had char arrayofstrings[300] or char arrayofstrings[3][10][10] or even long arrayofstrings[75] (char is 1 byte, long is 4 bytes).
Because you declared it as a multidimensional array with [a][b], when you ask for arrayofstrings[x][y], the compiler will calculate ((x*b)+y)*sizeof(type) and add it to the arrayofstrings pointer to get the address of the value you want. But because it's just a pointer, you can treat it like any other pointer and pass it around or cast it to other types of pointer or do pointer math with it.
You don't need the extra level of indirection.
An array, when passed to a function, is converted to a pointer to its first member. So if you declare the function like this:
int function(char (*pointer)[100])
Or equivalently:
int function(char pointer[][100])
Or:
int function(char pointer[3][100])
You can pass the array directly to the function:
function(arrayofstrings);
Then the body could look something like this:
strcpy(pointer[0], "some string");
strcpy(pointer[1], "some other string");
strcpy(pointer[2], "yet another string");
Best way to initialize an array of strings ...
char arrayofstrings[3][100] = {0}; is fine to initialize an array of strings.
In C, initialization is done only at object definition, like above.
Later code like strcpy(), assigns data to the array.
Best way to ... pass it to a function
When the C compiler supports variable length arrays, use function(size_t n, size_t sz, char a[n][sz]).
Add error checks.
Use size_t for array sizing and indexing.
#define somepointertostring "Hello World"
int function(size_t n, size_t sz, char arrayofstrings[n][sz]) {
if (sz <= strlen(somepointertostring)) {
return 1;
}
for (size_t i = 0; i < n; i++) {
strcpy(arrayofstrings[i], somepointertostring);
if (strcmp(arrayofstrings[i], somepointertostring)) {
return 1;
}
// Drop this it see something interesting in `foo()`
memset(arrayofstrings[i], 0, strlen(arrayofstrings[i]));
}
return 0;
}
void foo(void) {
char arrayofstrings[3][100] = {0};
size_t n = sizeof arrayofstrings / sizeof arrayofstrings[0];
size_t sz = sizeof arrayofstrings[0];
if (function(n, sz, arrayofstrings)) {
puts("Fail");
} else {
puts("Success");
puts(arrayofstrings[0]);
}
}
Initalizing an (empty?) array of strings and initializing a pointer on the first element.
The type of &arrayofstrings is char (*)[3][100] i.e. pointer to an object which is a 2D array of char type with dimension 3 x 100. So, this initialisation
char (*pointer)[3][100] = &arrayofstrings;
is not initialisation of pointer with first element of arrayofstrings array but pointer will point to whole 2D array arrayofstrings. That why, when accessing the elements using pointer you need bracket around it -
`(*pointer)[0]` -> first string
`(*pointer)[1]` -> second string and so on..
Is this a good way to do it? Is there an easier way to do it?
If you want pointer to first element of array arrayofstrings then you can do
char (*p)[100] = &arrayofstrings[0];
Or
char (*p)[100] = arrayofstrings;
both &arrayofstrings[0] and arrayofstrings are equivalent1).
Pass it to a function and access the array:
function() function signature should be -
int function (char (*pointer)[100])
// if you want the function should be aware of number of rows, add a parameter for it -
// int function (char (*pointer)[100], int rows)
this is equivalent to
int function (char pointer[][100])
and call it in from main() function like this -
function (p);
In the function() function you can access array as p[0], p[1] ...:
Sample program for demonstration:
#include <stdio.h>
#include <string.h>
#define ROW 3
#define COL 100
void function (char (*p)[COL]) {
strcpy (p[0], "string one");
strcpy (p[1], "string two");
strcpy (p[2], "string three");
}
int main(void) {
char arrayofstrings[ROW][COL] = {0};
char (*pointer)[COL] = &arrayofstrings[0];
function (pointer);
for (size_t i = 0; i < ROW; ++i) {
printf ("%s\n", arrayofstrings[i]);
}
return 0;
}
When you access an array, it is converted to a pointer to first element (there are few exceptions to this rule).

How to pass a 1D array of 500 pointers to a function in C

The programm is compiling ok but once the user inputs something to the array it crashes. Any help will be appreciated :)
#include <stdio.h>
#include <stdlib.h>
void Register(char *arr[],char arr2[]);
int main() {
char *Username[500];
char table1[20];
Register(Username,table1);
return 0;
}
void Register(char *Username[],char table1[]){
int i;
for(i = 0; i < 500; i++){
scanf("%s",&table1);
Username[i] = table1;
printf("for i = %d username[%d] is %s\n\n",i,i,Username[i]);
}
}
The problem is that when you pass your arrays they decay to pointers to their first element. For "array" arguments something like e.g. char table1[] is actually char *table1. And this is a problem when you try to use the address-of operator & in scanf.
When you use &table1 in the scanf call you get a pointer to the pointer, which has the type char **, not the expected char *. This mismatch between the format %s and the expected type leads to undefined behavior and probably your crash.
The solution to this crash is to never use the address-of operator for reading strings (with e.g. the %s format), as it's even wrong when you have an actual array:
scanf("%s",table1);
As for the problem of making all elements of Username point to the single string in table1, I recommend that you use arrays of arrays for Username instead:
char Username[500][20];
This array decays to a pointer to an array, with the type of char (*)[20], which needs to be part of the Register function declaration:
void Register(char (*Username)[20]);
Then you can use this directly in the call to scanf:
scanf("%19s", Username[i]);
Also note how I limited the length of the input string, so you can't read more than the arrays can handle (and it's 19 because the array need to fit the string null-terminator as well).
In this statement
scanf("%s",&table1);
instead of reading a string into the array pointed to by the pointer table1 the string is read into the pointer itself.
You have to write
scanf("%s",table1);
Also the function Register does not make great sense because all elements of the array Username will point to the first character of the array table1. That is all elements of the array will point to the first character of the last string that was read.
You need to allocate memory dynamically. Also the second parameter of the function is redundant.
The function cam look the following way
size_t Register( char *Username[], size_t n )
{
char record[20];
int i = 0;
for ( ; i < n && scanf( "%19s", record ) == 1; i++ )
{
Username[i] = malloc( strlen( record ) + 1 );
strcpy( Username[i], record );
printf( "for i = %zu username[%zu] is %s\n\n", i, i, Username[i] );
}
return i;
}
And in main the function can be called like
size_t n = Register( Username, 500 );
\Of course you will need to free all allocated memory.

Function that prints reverse of a string/char array in C

I am rather new to the C language right now and I am trying some practice on my own to help me understand how C works. The only other language I know proficiently is Java. Here is my code below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char * reverse(char word[]);
const char * reverse(char word[]) {
char reverse[sizeof(word)];
int i, j;
for (i = sizeof(word - 1); i <= 0; i--) {
for (j = 0; j > sizeof(word - 1); j++) {
reverse[i] = word[j];
}
}
return reverse;
}
int main() {
char word[100];
printf("Enter a word: ");
scanf("%s", word);
printf("%s backwards is %s\n", word, reverse(word));
return 0;
}
When the user enters a word, the program successfully prints it out when i store it but when i call the reverse function I made it doesnt return anything. It says on my editor the address of the memory stack is being returned instead and not the string of the array I am trying to create the reverse of in my function. Can anyone offer an explanation please :(
sizeof(word) is incorrect. When the word array is passed to a function, it is passed as a pointer to the first char, so you are taking the size of the pointer (presumably 4 or 8, on 32- or 64-bit machines). Confirm by printing out the size. You need to use strlen to get the length of a string.
There are other problems with the code. For instance, you shouldn't need a nested loop to reverse a string. And sizeof(word-1) is even worse than sizeof(word). And a loop that does i-- but compares i<=0 is doomed: i will just keep getting more negative.
There are multiple problems with your reverse function. C is very different from Java. It is a lot simpler and has less features.
Sizes of arrays and strings don't propagate through parameters like you think. Your sizeof will return wrong values.
reverse is an identifier that is used twice (as function name and local variable).
You cannot return variables that are allocated on stack, because this part of stack might be destroyed after the function call returns.
You don't need two nested loops to reverse a string and the logic is also wrong.
What you probably look for is the function strlen that is available in header string.h. It will tell you the length of a string. If you want to solve it your way, you will need to know how to allocate memory for a string (and how to free it).
If you want a function that reverses strings, you can operate directly on the parameter word. It is already allocated outside the reverse function, so it will not vanish.
If you just want to output the string backwards without really reversing it, you can also output char after char from the end of the string to start by iterating from strlen(word) - 1 to 0.
Edit: Changed my reverse() function to avoid pointer arithmetic and to allow reuse of word.
Don't return const values from a function; the return value cannot be assigned to, so const doesn't make sense. Caveat: due to differences between the C and C++ type system, you should return strings as const char * if you want the code to also compile as C++.
Arrays passed as params always "decay" to a pointer.
You can't return a function-local variable, unless you allocate it on the heap using malloc(). So we need to create it in main() and pass it as a param.
Since the args are pointers, with no size info, we need to tell the function the size of the array/string: sizeof won't work.
To be a valid C string, a pointer to or array of char must end with the string termination character \0.
Must put maximum length in scanf format specifier (%99s instead of plain %s — leave one space for the string termination character \0), otherwise vulnerable to buffer overflow.
#include <stdio.h> // size_t, scanf(), printf()
#include <string.h> // strlen()
// 1. // 2. // 3. // 4.
char *reverse(char *word, char *reversed_word, size_t size);
char *reverse(char *word, char *reversed_word, size_t size)
{
size_t index = 0;
reversed_word[size] = '\0'; // 5.
while (size-- > index) {
const char temp = word[index];
reversed_word[index++] = word[size];
reversed_word[size] = temp;
}
return reversed_word;
}
int main() {
char word[100];
size_t size = 0;
printf("Enter a word: ");
scanf("%99s", word); // 6.
size = strlen(word);
printf("%s backwards is %s\n", word, reverse(word, word, size));
return 0;
}

Value was not retained outside of a function

I'm writing a program that should get its inputs from a text file by using input redirection in a function called GetInput. (The text file contains 10 words.) The code should then be able to print the contents of ListWord in the Print function.
This is what I have so far.
I keep on getting errors while trying to run this code. I tried to remove * before ListWord and the code works but it does not retain the word (string) that was stored in it. But removing * before ListWord does not make sense to me. What am I doing wrong?
void GetInput( char** ListWord)
{
int i=0;
char word[30]; //each word may contain 30 letters
*ListWord = malloc(sizeof(char*)*10); //there are 10 words that needs to be allocated
while(scanf("%s", word)==1) //Get Input from file redirection
{
*ListWord[i]= (char *)malloc(30+1);
printf("%s\n", word); //for checking
strcpy(*ListWord[i], word);
printf("%s\n", *ListWord[i]); //for checking
i++;
}
}
void Print(char *ListWord)
{
//print ListWord
int i;
for (i=0; i<10; i++)
{
printf("%s", ListWord[i]);
}
}
int main()
{
char * ListWord;
GetInput(&ListWord);
printf("%s\n", ListWord[0]);
Print(ListWord);
free(ListWord);
return 0;
}
(Note: This is a homework. Thank you and sorry if it's unclear)
Due to *operator precedence the expression *ListWord[i] doesn't do what you think it does. In fact you should be getting errors or warnings from the code you have.
The compiler thinks that *ListWord[i] means *(ListWord[i]), which is not right. You need to use (*ListWord)[i].
Unfortunately that's only the start of your problems. A bigger problem is that the pointer you pass to the function GetInput is not a pointer to what could become an array of strings, but a pointer to a single string.
For a dynamic allocated array of strings, you need a pointer to a pointer to begin with, and then emulate pass-by-reference on that, i.e. you need to become a three star programmer which is something you should avoid.
Instead of trying to pass in the array to be allocated as an argument, have the GetInput return the array instead. Something like
char **GetInput(void)
{
// Allocate ten pointers to char, each initialized to NULL
char **ListWords = calloc(10, sizeof(char *));
if (ListWords == NULL)
return NULL;
char word[31];
for (int i = 0; i < 10 && scanf("%30s", word) == 1; ++i)
{
ListWords[i] = strdup(word);
}
return ListWords;
}
The above code adds some security checks, so you will not go out of bounds of either the temporary array you read into, or the ListWords array. It also makes sure the ListWords array is initialized, so if you read less then 10 words, then the remaining pointers will be NULL.
Of course you need to change your main function accordingly, and also your Print function, because now it only takes a single string as argument, not an array of strings. You also of course need to free every single string in the array because freeing the array.

Pointers and Reallocation in C

I'm fairly new to C; been at it for 3 weeks in a class. I am having a bit of trouble with pointers, and am sure there is probably an easy fix. So basically, this program is supposed to read a word from an input file, store it in an array of pointers with memory allocation, print the word and the normalized form of the word (irrelevant process), and then reallocate the space so that the pointer array will grow as more words are inputted. However, I am having a bit of trouble getting the words to print and the array to reallocate (I currently have it set to a fixed size just to troubleshoot the whole printing aspect). Let me know if there is something wrong with my variable declarations, or if I am just making a stupid mistake please (I am sure it is the probably a combination of the two). Again, I'm very new to C, so I apologize if this is an easy question.
char * word_regular[100];
char * word_norm[100];
int main (int argc, char * argv[])
{
if (argc != 2){
printf("You have not entered a valid number of files.\n");
exit(1);
}
FILE * f_in = fopen(argv[1],"r");
int i = 0;
char word[512];
char norm_word[512];
while(fscanf(f_in, "%s", word) != EOF) {
if (is_valid_entry(word)) {
word_regular[i] = malloc(sizeof(char) * strlen(word) + 1);
strcpy(word_regular[i],word);
printf("%s\n",*word_regular[i]);
word_norm[i] = malloc(sizeof(char) * strlen(norm_word) + 1);
normalize(word, norm_word);
strcpy(word_norm[i],norm_word);
printf("%s\n", *word_norm[i]);
i++;
Some problems that are with your current code (ignoring the dynamic size need as opposed to fixed since you already said you are using that to debug),
printf("%s\n",*word_regular[i]);
%s takes a char * for printing, so it should be
printf("%s\n",word_regular[i]);
For the second printf, since norm_word itself is a char array,
you should simply use
printf("%s\n", &norm_word[i]);
If you want to print string starting from the ith index.
Update:
A quick tip is to pay attention whether you are copying the \0 with strings or not. Because your api calls, such as strlen would go beyond string crashing (or worst silently), unless it is null terminated.
The problem with your printf call is that you pass a char (*word_regular[i], *norm_word[i]) instead of char * (word_regular[i], word_norm[i]) when trying to print a string.
If you want to dynamically grow the array, you need to dynamically allocate it in the first place, so instead of declaring arrays of pointers:
char * word_regular[100];
char * word_norm[100];
You need to declare pointers to pointers:
char ** word_regular;
char ** word_norm;
Allocate an initial buffer for them (in a function, main for example):
word_regular = malloc(sizeof(char *) * INITIAL_AMOUNT);
Then reallocate them as needed.
word_regular = realloc(word_regular, sizeof(char *) * new_amount);
You will need to keep track of the amount of pointers in the arrays, and free them properly of course...

Resources