How do I make this function take arbitrary strings? - c

So basically, right now, this function can only take 9 words with 10 characters each. How do i make it so that it can take an arbitrary amount of words and characters and sort them accordingly in alphabetical order?
int sortText(){
char name[10][9], tname[10][9], temp[10];
int i, j, n;
printf("Enter the amount of words you want to sort (max 9):");
scanf("%d", &n);
printf("Enter %d words: ",n);
for (i = 0; i < n; i++)
{
scanf("%s", name[i]);
strcpy(tname[i], name[i]);
}
for (i = 0; i < n - 1 ; i++){
for (j = i + 1; j < n; j++){
if (strcmp(name[i], name[j]) > 0){
strcpy(temp, name[i]);
strcpy(name[i], name[j]);
strcpy(name[j], temp);
}
}
}
printf("\n------------------------------------------\n");
printf("%-3s %4s %11s\n", "Input","|", "Output");
printf("------------------------------------------\n");
for (i = 0; i < n; i++)
{
printf("%s\t\t%s\n", tname[i], name[i]);
}
printf("------------------------------------------\n");
}

You have two problems, that each needs to be solved separately, but they can still be solved in a similar way, namely using dynamic memory allocations and more importantly reallocation.
There are two important aspects to remember here, and the first is that a string is an array of characters (with a special terminating character) and that you can have a pointer to an array located anywhere in memory.
If we start with the data-types and how you should store your strings, what you want is an array of arrays, much like you have right now, but allocated dynamically which means you want an array of pointers (to the strings), but since the array of string also needs to be dynamic you need a pointer to an array which contains pointers to other arrays, i.e. a pointer to a pointer to char: char **.
Now when we know what data-type to use, lets think about how to allocate it. To allocate space for a single string in your array, you allocate one char * using the malloc function:
char **strings = malloc(1 * sizeof(char *));
That was the simple part. Now before we start reading the actual string, lets think about how to add a new string to your collection: This is done by reallocating the array of strings you have, using the realloc function:
char **temp_strings = realloc(strings, current_count + 1 * sizeof(char *));
if (temp_string == NULL)
{
// Allocation failed, handle error appropriately
}
strings = temp_strings;
++current_count;
Here the variable current_count is the current length of the array of strings, it should be initially initialized to 1 (as we only have a single string in the array).
Now for the reading of the actual strings, and this is a little more complicated since we actually can't read whole strings (since we don't know how long each line is). Instead we read one character at a time, and end when we hit a newline. We also need to reallocate the string for each character.
Maybe something like this:
int ch;
char *s = NULL;
size_t current_length = 0; // Current length of string
while ((c = fgetc(stdin)) != EOF)
{
if (c == '\n')
break; // Newline, done with the current string
if (s == NULL)
{
s = malloc(2); // Allocate two character: One for c and one for the terminator
}
else
{
// The current length is not including the terminator
// that's why we add two characters
char *temp_s = realloc(s, current_length + 2);
if (temp_s == NULL)
{
// Handle error
}
s = temp_s;
}
s[current_length++] = c;
s[current_length] = '\0'; // Terminate as a string
}
if (s != NULL)
{
// "Add" the string to the array of strings
strings[current_count] = s;
}

For C only you should use char pointer located dynamic.
You can make list by implement a linked list. Then strcmp still work well.
See about linked list here:
http://www.cprogramming.com/tutorial/c/lesson15.html

Related

Print an array that was re-allocated several times in C

Basically:
i'm fetching characters out of a text file. Each word ends with a
\n in the text file.
I want to create an char ** array that will include ONLY the first character of each word.
To do that, i'm scanning the text file and running on my chars array.
If the current char is a \n replace it with a null terminator \0 when inserting to the chars array.
In that way, i'll be able to seperate between words when i'll run on that array with my char **words_arr.
I want the **words_arr to point at every first character of a word, which means one char after each \0 which indicates on an end of a word.
size_t words = 1, total_characters = 30;
char **original_address, **words_arr, *chars, character;
chars = (char *)malloc(sizeof(char) * total_characters);
words_arr = original_address = (char **)malloc(sizeof(char *) * (words);
while (i < total_characters)
{
character = fgetc(text_file);
if (character == EOF)
{
break;
}
if (character == '\n')
{
*chars = '\0';
*words_arr = chars + 1;
++words;
words_arr = (char **)realloc(original_address, sizeof(char *) * (words));
words_arr += words-1;
}
else
{
*chars = character;
}
++chars;
++i;
}
Also, I have no idea how many words will I have, and that is why I use the realloc function, although i'm not sure i'm doing it right.
For now, i'm able to print the chars array and see all the words and each word starts at a new line.
But when I try to print the words array, i'm getting a segmentation fault.
I'm trying to print them using:
i = 0;
chars -= total_characters;
while (i < total_characters)
{
runner = chars + i;
if (*runner == '\0')
{
printf("\n");
}
else
{
printf("%c", *runner);
}
++i;
}
printf("\n\n");
words_arr -= words;
i = 0;
while (i < words)
{
printf("%s", *(words_arr + i));
++i;
}
As I said, the first print, or chars array successes.
The second print, of words_arr returns a Segmentation fault.
Thanks.
This part:
words_arr = (char **)realloc(original_address, sizeof(char *) * (words));
is wrong (for a number of reasons).
When realloc returns with succes (i.e. new memory allocated), the value of original_address is no longer valid. But in your code you keep use original_address again inside the loop.
You need to update original_address after each realloc so it is valid for next realloc.

How to store a string in a multidimensional array in C

I'm new at C and I'm trying to do an exercise which asks to insert some strings and then store them. First it requests a multidimensional array where we have for every row of the array a string, and then as an array of pointers.
Here's the code for the first part. I don't know how to store into an array some strings that are not already written.
For the second one I have no idea since I've never done exercises with pointers before.
#include <stdio.h>
int main(){
int n; //number of strings
int x; //number of characters per string
printf("How many strings do you want to insert?");
scanf("%d", &n);
if ((n >= 1) && (n <= 20)){
printf("How many characters per string?");
scanf("%d", &x);
char str[x];
if (x <= 10){
for(int i = 0; i < n; i++){
printf("Insert a string:");
scanf("%s", str);
for(int j = 0; j < x; j++){
char arr[j];
arr[j] = str[x];
printf("%s", arr);
}
}
}
else {
printf("Error:the number of characters must be < 10");
}
}
else {
printf("Error: the number must be < 20");
}
return 0;
}
... requests a multidimensional array where we have for every row of the array a string, and then as an array of pointers.
After getting the qualified number of strings, allocate an array of pointers to char.
if ((n >= 1) && (n <= 20)){
char **string_list = calloc(n, sizeof *string_list);
assert(string_list); // or other error checking
(Notice no type in = calloc(n, sizeof *string_list);. Easier to code right, review and maintain.)
Read the strings in a working temp buffer. As "How many characters per string?" likely means the number of characters not including the null character, our str[] needs a +1 in size.
// char str[x]; // too small
char str[x+1];
Yet we know x <= 10 and can use a fixed buffer size and limit input length
for(int i = 0; i < n; i++){
char str[10+1];
printf("Insert a string:");
scanf("%10s", str); // Notice the 10 - a width limit
// TBD check if scanf() returned 1 and if str is longer than x
Now allocate a copy of the str
string_list[j] = strdup(str);
assert(string_list[j]); // or other error checking
}
Later, when done with string_list[], clean-up and free allocations.
for (int i=0; i<n; i++) {
free(string_list[i]);
}
free(string_list);
What is weak about this:
It uses scanf() rather than fgets() and then parses, has minimal error checking, does not take in strings with spaces, does not handle over-long input, strdup() is not standard -yet, etc.
So the above is a baby step. Better code would handle the weak issues.

Error using 'free' on char array

I'm new to C and trying to write a command line program with it. I'm trying to free a char array right before the program terminates. But I'm getting a "debug assertion failed" run-time error when it reaches the free command. Before it reaches that point, the program is removing characters in that array up until the first whitespace. I'm using the incrementing technique on the array since I read that was a way to remove chars one by one from an array. Here's that piece of code:
char command[140];
char * input = getInput(); //prompt function for user input
//get string up to first whitespace to separate the command and its parameters
for (i = 0; i < strlen(input); i++)
{
if (input[i] == ' ' || input[i] == '\0')
break;
command[i] = input[i];
}
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
command[i] = '\0'; //null terminate char array
numParams = getNumParams(input);
free(input); //should've added this line earlier to avoid confusion.
My getInput() function does this:
char * getInput()
{
int n, size = 260;
char * input = (char*)malloc(size);
if (!input) //make sure memory allocation worked
return NULL;
do
{
printf("cmd> "); //prompt
fgets(input, 256, stdin); //get user input/commands
n = strlen(input);
} while (n <= 1);
if (input[n - 1] == '\n') //remove new line from input array
input[n - 1] = '\0';
return input;
}
So after the rest of the program ends I want to be able to free the memory that was allocated in the getInput() function. I'm thinking the way I have the input returning to a char pointer is messing that up. But I'm not sure how to fix it. Any help is appreciated.
You haven't posted the line that calls free. I am assuming that you are calling:
free(input);
I can see why that would be a problem.
You are changing the value of input in the lines:
for (j = 0; j <= i; j++)
input++;
When you call free, the value of the pointer must be what was returned by malloc, calloc, or realloc. If you use any other pointer value, the program is subject to undefined behavior.
Make sure that you keep the value that was returned, so you can call free using it.
char* input = getInput();
char* saved_ptr = input;
// ...
// Change input
// ...
// Deallocate memory using the original pointer
free(saved_ptr);
The problem is most likely these two lines:
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
Here you modify the pointer, making you loose the original pointer that you should pass to free. You need to save the original pointer in a temporary variable, and pass that to free.
You get an error because you are modifying the input pointer here:
for (j = 0; j <= i; j++) //removes command and space and leaves parameters
input++;
After this operation, input no longer points to the start of memory, allocated by malloc. Thus giving you the error. Instead, copy input to another pointer variable.
Also, consider doing allocation outside of getInput() since it is considered a good practice to allocate and free the memory in the same function, if possible.

strings of characters in C

I am trying to solve a C problem where I have to sort n strings of characters using pointers.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sort (char *s, int n)
{
int i,j; char aux[]="";
for (i=1;i<=n-1;i++)
{
for (j=i+1;j<=n;j++) //if s[i]> s[j] switch
{
if (strcmp(*(s+i),*(s+j))==1)
{
strcpy(aux,*(s+i);
strcpy(*(s+i),*(s+j));
strcpy(*(s+j),*(s+i));
}
}
}
}
void show(char *s, int n)
{
int i;
for (i=1;i<=n;i++)
{
printf("%s",*(s+i));
}
}
int main()
{
int i,n; char *s;
printf("give the number of strings:\n");
scanf("%d",&n);
s=(char*)calloc(n,sizeof(char));
for (i=1;i<=n;i++)
{
printf("s[%d]= ",i);
scanf("%s",s+i);
}
sort(s,n);
show(s,n);
return 0;
}
The warnings that I get are when I use strcmp to compare and when I use strcpy to switch *(s+i) and *(s+j) values.
"passing argument 2 of strcmp makes pointer from integer without a cast"
The warnings that I get are when I use strcmp to compare and when I use strcpy to switch *(s+i) and *(s+j) values.
"passing argument 2 of strcmp makes pointer from integer without a cast"
You are wrong argument to strcmp and strcpy. Signature of strcmp and strcpy are
int strcmp(char *string1, char *string2);
char *strcpy(char *dest, const char *src)
But you are passing it the arguments of char type. Removefrom(s+i)and*(s+j)`. It should be
if (strcmp((s+i), (s+j))==1)
{
strcpy(aux, (s+i));
strcpy((s+i), (s+j));
strcpy((s+j), (s+i));
}
Another problem is you have not allocated memory for the pointer s. For characters, you can declare it as
s = malloc ( (n + 1)*sizeof(char) );
or simply
s = malloc ( n + 1 ); // +1 is for string terminator '\0'
You seem to have mis-understood the return value of strcmp(); it's an integer that will be 0 (zero) if and only if the two string arguments are equal. You're testing for 1, which it will only be rarely; it has no specific meaning.
Consider just using qsort().
You're allocating space for n strings of 1 byte each. The easiest approach is to assume each string will be less than some set length, like 40 bytes. Then you would allocate the memory like this:
s=(char*)calloc(n*(40),sizeof(char));
Then your scanf needs to be modified:
scanf("%s",s+(i*40));
Now, string 1 will be at *s, string 2 will be at *(s+40), etc. Keep in mind that a string ends with a null character (0x00), so the string can only contain 39 chars. Any unused data will also be 0x00.
Do the same s+(i*40) for the sorting algorithm, compare to >0, not ==1, and strcmp, strcpy expect pointers. Then you should be good.
In your code there are a lots of mistakes: in variable declarations, memory allocation and pointer usage.
First remember that you should always reserve enough space to store your string values, and be aware to don't try to store a string with a length greater then the reserved space.
Also you should be very careful managing pointers (s+1 doesn't point to the first string but to the second one, s+n points out of the allocated memory)
So, as mentioned in other answers you should decide the maximum size of your strings an then allocate for them the right amount of memory.
Then I suggest you to use a pointer to strings, i.e. a char** to access your strings and manage the sort, in this way the code is more readable and the sort is faster (you don't need to copy strings, but only to switch pointers)
So your main function should be:
int main()
{
int i, n;
// Declare the pointer to the memory where allocate the space for the strings
char* a; // points to a char ( == is a string)
// Declare the pointer to the memory where store the pointer to the strings
char** s; // points to a char* ( == is a pointer to a string)
printf("give the number of strings:\n");
scanf("%d", &n);
// Allocate the memory for n strings of maximum 40 chars + one more for the null terminator
a=(char*)calloc(n*41, sizeof(char));
// Allocate memory for n pointers to a string
s=(char**)calloc(n, sizeof(char*));
// Notice the 0-based loop
for (i=0; i<n; i++)
{
s[i] = a + i*41; // set the i-th element of s to point the i*41-th char in a
printf("s[%d]= ", i);
scanf("%40s", s[i]); // read at least 40 characters in s[i],
// otherwise it will overflow the allocated size
// and could generate errors or dangerous side effects
}
sort(s,n);
show(s,n);
// Remember always to free the allocated memory
free(s);
free(a);
return 0;
}
the sort function sorts an array of pointers, without copying strings:
void sort (char** s, int n)
{
int i, j;
char* aux;
// Notice the 0-based loop
for (i=0; i<n; i++)
{
for (j=i+1; j<n; j++) //if s[i]> s[j] switch
{
if (strcmp(s[i], s[j])>0)
{
// You don't need to copy strings because you simply copy pointers
aux = s[i];
s[i] = s[j];
s[j] = aux;
}
}
}
}
finally the correct show function:
void show(char** s, int n)
{
int i;
// Notice the 0-based loop
for (i=0; i<n; i++)
{
printf("%s", s[i]);
}
}
I can't test the code but it should works
Added other options
1) If you have troubles with dynamic memory allocation you can use instead static allocation, your main function then will be:
int main()
{
int i, n;
// Declare an array of fixed lenght strings
char s[100][41]; // You manage max 100 strings
printf("give the number of strings:\n");
scanf("%d", &n);
// Ensure that n is less or equal maximum
if (n>100)
{
printf("you'll be asked for a maximum of 100 strings\n");
n=100;
}
// Notice the 0-based loop
for (i=0; i<n; i++)
{
printf("s[%d]= ", i);
scanf("%40s", s[i]); // read at least 40 characters in s[i],
// otherwise it will overflow the allocated size
// and could generate errors or dangerous side effects
}
sort(s,n);
show(s,n);
// You don't need to free any memory
return 0;
}
all other functions remain unchanged.
2) Whereas, if you want the maximum freedom in the memory you need you can choose a two allocate memory separately for each string only for the needed size, in this case your main funcion will be:
int main()
{
int i, n;
char buffer[1000]; // You need a buffer to read input, before known its actual size
// Declare the pointer to the memory where store the pointer to the strings
char** s; // points to a char* ( == is a pointer to a string)
printf("give the number of strings:\n");
scanf("%d", &n);
// Allocate memory for n pointers to a string
s=(char**)calloc(n, sizeof(char*));
// Notice the 0-based loop
for (i=0; i<n; i++)
{
printf("s[%d]= ", i);
scanf("%999s", buffer); // read at least 999 characters in buffer,
// otherwise it will overflow the allocated size
// and could generate errors or dangerous side effects
// Allocate only the memory you need to store the actual size of the string
s[i] = (char*)calloc(strlen(buffer)+1, sizeof(char)); // Remember always 1 more char for the null terminator
// Copy the buffer into the newly allocated string
strcpy(s[i], buffer);
}
sort(s,n);
show(s,n);
// Remember always to free the allocated memory
// Now you have first to free the memory allocated for each string
for (i=0; i<n; i++)
{
free(s[i]);
}
// Then you can free the memory allocated for the array of strings
free(s);
return 0;
}
all other functions remains unchanged too.

How to input a string in a 2d array in C?

If i want to take an input in a 2d array with each string in one row, and the next in the other(i.e. change the row on pressing enter). How can i do that in C. C doesnt seem to have convenient "String" Handling. I obviously mean doing so without the use of getchar().
3 ways are there which are mentioned below.
If you know the maximum number of strings and maximum number of chars, then you can use the below way to declare a 2D character array.
char strs[MAX_NO_OF_STRS][MAX_NO_CHARS] = {0};
for (i = 0; i < MAX_NO_OF_STRS; i++)
{
scanf("%s", strs[i]);
}
If you know the maximum number of strings, and you dont want to waste the memory by allocating memory for MAX_NO_CHARS for all strings. then go for array of char pointers.
char temp[MAX_NO_CHARS] = {0};
char *strs[MAX_NO_OF_STRS] = NULL;
for (i = 0; i < MAX_NO_OF_STRS; i++)
{
scanf("%s", temp);
strs[i] = strdup(temp);
}
If you know the maximum number of strings during run time means, you can declare a double pointer of char. Get the number of strings n from user and then allocate memory dynamically.
char temp[MAX_NO_CHARS] = {0};
char **strs = NULL;
int n = 0;
scanf("%d", &n);
strs = malloc(sizeof(char*) * n);
for (i = 0; i < n; i++)
{
scanf("%s", temp);
strs[i] = strdup(temp);
}
#include<stdio.h>
main()
{
char student_name[5][25];
int i;
for(i=0;i<5;i++)
{
printf("\nEnter a string %d: ",i+1);
scanf(" %[^\n]",student_name[i]);
}
}
u can read strings using 2d array without using getchar() by putting space in scanf(" %[^\n]")
; before %[^\n]!
An alternative to using malloc and filling up an array of pointers with buffers of a fixed size, would be to allocate a 2d array (in static storage or on the stack) and fill it up. KingsIndian modifed code example would than look like this:
#include <stdio.h>
int main()
{
char str[2][256] = {{0}};
int i = 0;
for(i=0;i<2;i++)
{
scanf("%255s", &str[i][0]);
}
return 0;
}
If all strings you expect to get are no longer than some size, than this approach will spare you the need to deal with freeing the memory yourself. It is less flexible however, meaning that you can't fit the size of an individual buffer to the string it contains.
EDIT
Adding to the information in the comment, to read a string that is terminated only by a new-line, rather than by any whitespace:
scanf("%255[^\n]", str[i]);

Resources