Deciding how much memory to dynamically allocate based on user input - c

I need to write a program that does the following:
Prompt the user and ask them how many words they wish to enter
Allocate dynamic memory using malloc() to store each individual word
Dynamically allocate another array to store pointers to each of these individual word strings, fill it with the pointers
Dynamically allocate an additional array for temporary storage of an incoming word
Prompt the user to enter a string with the same number of words they previously entered.
Read the users' input into the temporary array one word at a time, once each word has been read, transfer it into one of the dynamically allocated arrays.
After all of this I should be able to print each word individually.
A sample run could look like this:
How many words do you wish to enter? 5
Enter 5 words now:
I enjoyed doing this exercise
Here are your words:
I
enjoyed
doing
this
exercise
When reading the string, the program should read the word into a temporary array of char, use malloc() to allocate enough storage to hold the word, and store the address in the array of char pointers.How to handle this step?
Here is my question: How can I size my temporary array to be large enough to hold any arbitrary word the user may enter, and then how do I transfer it to the storage array?
#include <stdio.h>
#include <stdlib.h>
enum { MAX_WORD_SIZE = sizeof("Supercalifragilisticexpialidocious") };
void resign(char**,int);
int main()
{
char **p;
int n,i;
printf("How many words do you wish to enter?");
scanf("%d",&n);
p=calloc(n, sizeof(char*));
puts("Here are your words:");
resign(p,n);
for (i=0; i<n; i++) {
puts(p[i]);
}
}
void resign(char**p,int n)
{
int i=0,j=0;
char c;
char * temp_word = NULL;
getchar();
temp_word = malloc(MAX_WORD_SIZE);
while ((c=getchar())!='\n') {
if (c!=' ') temp_word[i++]=c;
else {temp_word[i]='\0';
p[j]=temp_word;
temp_word = malloc(MAX_WORD_SIZE);
i=0;j++;}
}
temp_word[i]='\0';
p[j]=temp_word;
free(temp_word);
}

Here is a description of malloc().
It takes a parameter size, in bytes.
So if the user entered 5 words, then you need to first allocate an array big enough to store 5 pointers.
Eg.
char ** words = NULL;
words = malloc(sizeof(char *) * <NUMBER OF CHARACTERS THE USER ENTERED>);
If we assume ASCII is being used and a character is a char, then each letter in each of the words is a byte. We also need to account for any spaces, line feeds, perhaps a carriage return, and a trailing null.
So how many letters are in a word? IDK, that's up to you. But if we assume that "Supercalifragilisticexpialidocious" from Mary Poppins in the longest word we will encounter, then we need to allocate at least 34 characters, plus an extra for a trailing null terminator. So that leaves us with up to 35 bytes per word.
This will work:
enum { MAX_WORD_SIZE = sizeof("Supercalifragilisticexpialidocious") };
This will also take into account the trailing \0 - MAX_WORD_SIZE will be 35.
char * temp_word = NULL;
temp_word = malloc(MAX_WORD_SIZE);
To create all of your other word arrays you'd need to do something similar in a loop:
for(int i = 0; i< <NUMBER OF WORDS THE USER ENTERED>; i++)
{
words[i] = malloc(MAX_WORD_SIZE);
}
To convert the number of words entered by the user into an integer you should use atoi()
After this you could use getchar() to pull the string from stdin one character at a time and manually place them into temporary you allocated, stopping when you get to a ' ' space.
To copy the temporary array into one of your word arrays you can use strcpy(), just ensure your temporary array always has a trailig \0 after the word.
Don't forget to free() that pointer when you're done.
I am assuming you are doing this for some sort of a homework assignment on dynamic memory and that is why your application is defined the way it is. But if you aren't you should consider just reading the input into one buffer first using fgets() and using strtok() to split the string after its been read. That approach will take more memory but it would be cleaner.
Another thing you should consider doing is using calloc() instead of malloc() to do your allocation, that way you can ensure that all of your arrays are initialized with 0's - it could save you from some confusion later on if there is garbage data already in those arrays.
Yet another thing to think about: The temporary array in this example can be allocated automatically or statically using temp_word[MAX_WORD_SIZE]; since I used an enum to store MAX_WORD_SIZE as a constant. There is no direct need to use malloc for this.

Related

How to read in the entire word, and not just the first character?

I am writing a method in C in which I have a list of words from a file that I am redirecting from stdin. However, when I attempt to read in the words into the array, my code will only output the first character. I understand that this is because of a casting issue with char and char *.
While I am challenging myself to not use any of the functions from string.h, I have tried iterating through and am thinking of writing my own strcpy function, but I am confused because my input is coming from a file that I am redirecting from standard input. The variable numwords is inputted by the user in the main method (not shown).
I am trying to debug this issue via dumpwptrs to show me what the output is. I am not sure what in the code is causing me to get the wrong output - whether it is how I read in words to the chunk array, or if I am pointing to it incorrectly with wptrs?
//A huge chunk of memory that stores the null-terminated words contiguously
char chunk[MEMSIZE];
//Points to words that reside inside of chunk
char *wptrs[MAX_WORDS];
/** Total number of words in the dictionary */
int numwords;
.
.
.
void readwords()
{
//Read in words and store them in chunk array
for (int i = 0; i < numwords; i++) {
//When you use scanf with '%s', it will read until it hits
//a whitespace
scanf("%s", &chunk[i]);
//Each entry in wptrs array should point to the next word
//stored in chunk
wptrs[i] = &chunk[i]; //Assign address of entry
}
}
Do not re-use char chunk[MEMSIZE]; used for prior words.
Instead use the next unused memory.
char chunk[MEMSIZE];
char *pool = chunk; // location of unassigned memory pool
// scanf("%s", &chunk[i]);
// wptrs[i] = &chunk[i];
scanf("%s", pool);
wptrs[i] = pool;
pool += strlen(pool) + 1; // Beginning of next unassigned memory
Robust code would check the return value of scanf() and insure i, chunk do not exceed limits.
I'd go for a fgets() solution as long as words are entered a line at a time.
char chunk[MEMSIZE];
char *pool = chunk;
// return word count
int readwords2() {
int word_count;
// limit words to MAX_WORDS
for (word_count = 0; word_count < MAX_WORDS; word_count++) {
intptr_t remaining = &chunk[MEMSIZE] - pool;
if (remaining < 2) {
break; // out of useful pool memory
}
if (fgets(pool, remaining, stdin) == NULL) {
break; // end-of-file/error
}
pool[strcspn(pool, "\n")] = '\0'; // lop off potential \n
wptrs[word_count] = pool;
pool += strlen(pool) + 1;
}
return word_count;
}
While I am challenging myself to not use any of the functions from string.h, ...
The best way to challenge yourself to not use any of the functions from string.h is to write them yourself and then use them.
your program reads the next word in the i-esim position of the buffer chunk, so you are getting the first letters of each word (as long as i doesn't get above the size of chunk) as each time you read, you overwrite the second and rest of the chars of the last word with the ones of the just read one. Then, you are putting all the pointers in wptrs to point to these places, making it impossible to distinguish the end of one string to the next (you overwrote all the null terminators, leaving only the last) so you will get a first string with all the first letters of your words but the last, which is complete. then the second will have the same string, but beginning at the second... then the third.... etc.
Build your own version of strdup(3) and use chunk to store temporarily the string... then make a dynamically allocated copy of the string with your version of strdup(3) and make the pointer to point to it.... etc.
Finally, when you are finished, just free all the allocated strings and voilĂ !!
Also, this is very important: read How to create a Minimal, Complete, and Verifiable example as it is very frequent that your code lacks of some errors that you have eliminated from the posted code (you don't normally know where the error is, or you would have corrected it and no question here, right?)

How to append an array inside a linked list with a character of another array

I need to append a array inside a linked list with a single character of an array also inside a linked list. I tried the strcat function but it didn't work.
for(int k=0; k < strlen(Node2->array); k++){
strcat((char *)Node->array, (char *)Node2->array[k]);
}
The ide gives this warning
cast to pointer from integer of different size -wint-to-pointer-cast
and I when I run the program the program stops, it can't go on.
There's no means to append to an array in C, as arrays are, per definition fixed (at compile time) lists of same type elements stored contiguously. Only if you have enough array size, a string (which is some char array which is partially filled by appending a \0 null character at the end of the valid char list) can be appended by simply moving the \0 char one place further (if there's place in the array for a character more) and putting before it the desired char. An example will illustrate this (I'll somewhat simplify to separate the list problem you point that has nothing to do with the appending problem)
Let's have:
char c = 'A';
char array[100] = "The string to be appended with ";
The second variable is an array initialized with the contents of the string literal indicated, plus \0 chars to complete array size until filling the 100 characters space of the array.
Several ways to append the contents of c variable are:
/* check first that array has space to store data */
int l = strlen(array);
if (l + 1 < sizeof array) {
array[l] = c;
array[l+1] = '\0';
}
Another way (if you don't need to know if the operation succeeded and only append the character if space is allocated) is to snprintf(3) the char at the end of the string value (the snprintf(3) controls correctly the space)
int l = strlen(array); /* number of chars in string */
snprintf(array + l, /* char pointer at string end */
sizeof array - l, /* the number of chars of space available */
"%c", /* format string to print a character */
c); /* data character to append */
By the way, the return value of snprintf(3) is the number of characters printed, so it should be 1 in case it worked successfully, and 0 in case there's no space left in the array to print the c variable and the final \0 character, so you can check the result of the operation.
NOTE
If you have a char * pointer in the node structure, because you allocated dynamically the memory to store the string value (for example, using strdup(3) function) then you have no space to append any character at the end as strdup(3) only allocates enough space to store a full copy (the characters, plus the extra \0 at the end) of the original string (even if that string was originally stored in a bigger character array) In this case, the only way to append a character is to allocate a new array with enough size for the new length and copy all the characters to the new place. Be careful, that if you do this in a per character basis, you'll get efficiency severely affected.

C language printing strings from array in a loop

I am making a console application in C using visualstudio 2015 so that a user can enter the amount of sweets they wish to make and the name of the sweet however there is a problem, the problem is that when the program tries to read a string from the array the program crashes and doesnt print out anything, however if I change it to print a single character using %c it will print out the first character of the string for example if I enter 2 sweets and the strings 'Jawbreaker' and 'Lollipop' It will crash if I use %s for the string however if I use %c for a character it will do its job and print 'J' and 'L' respectively on different lines, Any ideas how I could get this working with the %s specifier for the strings?
The code is below:
#include <stdio.h>
/*Declares the variable needed*/
int sweet_number;
char sweet_name[999];
int main(void) {
/*Prompts the user to enter the number of sweets and saves it to sweet_number*/
printf("Please enter the number of sweets:\n");
scanf("%d", &sweet_number);
/*for loop to enter the name of the sweet into the array*/
for (int i = 0; sweet_number > i; i++) {
printf("What is the name of the sweet?\n");
scanf("%s", &sweet_name[i]);
}
/*Prints array to screen*/
for (int j = 0; sweet_number > j; j++){ /* <- This is where code fails to run*/
printf("%s\n", sweet_name[j]);
}
return 0;
}
You have to use an 2-dimensional array. sweet_name is an array(1-D), each index can store at most one character not a string. Change the following line
char sweet_name[999];
to
char sweet_name[999][100];
OK, I would advice you doing something more efficient and that is to use a double pointer. By doing that, you can solve some problems that the 2D array version has.
The first problem is, what do you do if the user wants to insert more than 999 sweets. Your array can't hold them.
Second, what do you do if the user enters a name that is bigger than 100 characterrs. Again, your 2D array can't hold it. And also, although there is the possibility for the user to enter a name bigger than 100 characters, most users will enter much less than this and now for every string, you have allocated 100 positions when you probably only need about 50. So, let's deal with these problems.
I would probably do something like this:
#include <stdio.h>
#include <string.h> // for strcpy(), strlen()
#include <stdlib.h> // for malloc()
int main(void) {
char **sweet_names; // make a double pointer(or you might see that as array of pointers
char reader[300]; // this variable will help us read every name into the sweet_names
int sweet_number;
int i, j;
// Your code here to get the number of sweet_names
/*Prompts the user to enter the number of sweets and saves it to sweet_number*/
printf("Please enter the number of sweets:\n");
scanf("%d", &sweet_number);
// Now that you know the sweet_number, allocate as much memory as you need.
// And that can be more than 999 sweet names
sweet_names = (char **) malloc(sweet_number * sizeof(char *));
// i.e. make a character pointer to sweet_number character pointers.
// Again, some of your code here
for (i = 0; sweet_number > i; i++) {
printf("What is the name of the sweet?\n");
scanf("%s", reader); // read into the reader
sweet_names[i] = (char *) malloc(strlen(reader) + 1); // allocate as much memory as you need for the current string, the one just read into reader
strcpy(sweet_names[i], reader); // copy contents of reader to sweet_names[i]
}
// Again, your code to print the names
for (j = 0; sweet_number > j; j++){
printf("%s\n", sweet_names[j]);
free(sweet_names[j]); // free every string you allocated, it is good practice
}
// Finally, free the sweet_names pointers. Generally, when you allocate
// dynamically, which is what we do with malloc(), it is a good practice
// to free what you allocate becuase otherwise stays in memory and then
// memory leaks are created. There is a lot to say about C and memory
// but anyway, remember to free
free(sweet_names);
return 0;
}
Finally, now the program again has the limitation to read only names up to 300 characters because of reader, but this is something that you can also deal with, and this is to allocate a crazy amount of memory for reader, like 1000000
characters and then free it.

How to check each character in a dynamically allocated string array, in C?

So what I am trying to ultimately do is search an array for a name, and if the name is found return that name. Well, to do that I need to check for each character in every row and column for a match. And before I could do that I need to know exactly how to go about doing that, so I am trying to figure out how to get the dynamic array to print out the first character, then second and so on in order to compare it with the name being searched. But I am having trouble doing this. So my question is how would I go about checking each character in such an array? I've let out most parts of the code but I think I included the main parts that I am troubled in. Thanks for the help in advance.
I'm a beginner in C, so sorry if I did anything gravely wrong, thanks!
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define STRINGSIZE 20
int main(){
char **firstNames, **lastNames;
int classSize,i;
char sample[21] = "Slack";
printf("Please indicate number of records you want to enter (min 5, max 15):\n");
scanf("%d", &classSize);
firstNames=malloc(classSize*sizeof(char*));
for (i=0; i<classSize; i++) {
firstNames[i]=malloc(STRINGSIZE*sizeof(char));
}
printf("Please input records of students (enter a new line after each record), with following format: first name");
*firstNames="Slack";
printf("\n\n");
printf("%c", *(sample)); //Will print out S
printf("%c", **firstNames); //Will print out S
printf("%c", *(sample+1)); //Will print out l
printf("%c", **(firstNames+1)); //Will give error
printf("%c", **(firstNames)+1); //Will print T (Next ascii char after 'S'
printf("%c", **((firstNames)+1)); //Will give error
}
A C string is an array of characters. Even if dynamically allocated, you can treat it as such, so:
sample[0]; // S
sample[1]; // l
sample[2]; // a
// etc
You are storing multiple pointers to C strings in firstNames. You access it as an array:
firstNames[0]; // first name
firstNames[1]; // second name
firstNames[2]; // third name
// etc.
Now you just combine these, as firstName[0] is just a C string, just like sample:
firstName[0][0]; // first letter in first name
firstName[0][1]; // second letter in first name
firstName[1][0]; // first letter in second na,e
// etc.
You have an array of pointers to char firstNames the size of classSize. Every pointer in this array points to valid memory you allocated with malloc.
The error you make is assigning a string literal to the first pointer of the array firstNames, overwriting the pointer with the address of the string literal. This will lose the memory you allocated and also the string literal cannot be modified, which you do later causing the program to crash.
This line will copy a string "Slack" to the memory the first pointer in the array points to:
strcpy( firstNames[0] , "Slack" ) ; //make sure you have enough space
Note that firstNames[0] equals to *firstNames.
i used in the for loop is not defined anywhere.

scanf strings messed up

struct dict {
int len;
char (*dict0)[MAX_WORD_LEN+1];
char (*dict1)[MAX_WORD_LEN+1];
};
/* the memory allocation */
void createDict(struct dict* myDict)
{
(*myDict).dict0 = malloc((*myDict).len*sizeof(char));
(*myDict).dict1 = malloc((*myDict).len*sizeof(char));
if(((*myDict).dict0==0)||((*myDict).dict1==0))
exit(1);
}
for(int i = 0; i < words; i++)
{
scanf("%s", p_diction->dict0[i]);
scanf("%s", p_diction->dict1[i]);
}
for(int i=0; i<words; i++)
{
printf("%s ", &p_diction->dict0[i]);
printf("%s\n", &p_diction->dict1[i]);
}
p_diction is a pointer to a dict type.
I set words to 11 and input the following:
one lo
two ba
three li
day night
work eat
great terrible
terrible disaster
A a
start delay
finish never
I you
But when I printf to check the strings, they print the following:
one ree
two y
three rk
day eat
work rrible
great terrible
terrible art
A nish
start delay
finish never
I you
Any idea as to why the first scanf reads it perfectly, while the second one just reads random stuff from words that will come later?
It looks like you are using a one-dimensional array to hold multiple strings(words) which is possible if you end each string(word) with a unique character that signifies the end of a word. But it wouldn't be possible to do that using the vanilla functions scanf() and printf() you would have to write your own. For example lets say you end each word with a #. The following loop would print all the words to your screen.
int i = 0;
while((*myDict).dict0[i] != '\0')//While index dose not point to end of array
{
for(; (*myDict).dict0[i] != '#'; i++)//Print one word a single character at a time
{
printf("%c", (*myDict).dict0[i]);
}
printf("\n"); i++;//Print a newline after the word and then increase the index by one
//so that it does not point to '#'.
}
But you could also make your dict0 and dict1 arrays into arrays of pointers in which each element is a pointer that points to a single word.
You're redeclaring i and you don't need to(for the printf loop). for the second loop i already exists so i=0 is enough to 'reinit' the variable.
About the read loop, scanf is 'safe' if you do separation between words with enter/return. If you are separating them with spaces scanf is not a good option. The second scanf is looking for that enter/return, if you don't give it by the stdin it will fail and it will probably storing garbage until it finds the enter/return(\n).
And one more thing, how are you declaring pdiction?
If you do:
struct dict *pdiction = malloc(sizeof(struct dict));
You access its 'inner' variables with:
pdiction->len = 10; /*Setting len to 10*/
But if you declare it, inside the same function as the read/write loop, like this:
struct dict pdiction;
You access the 'inner' variables this way:
pdiction.len = 10;
The second way is how C structures are normally accessed. The so called dot notation.
The first way is kinda like an evoluiton of C: It was so common to use the 'pointed by in the field' for pointers to structures that C gives you a better/easier/more readable way to do it: the arrow notation.
What I've wrote in the first case is exactly the same as:
(*pdiction).len = 10; /*The (structure) pointed by pdiction in the field len*/
So be careful with that.
Just to finish, I think that you've done that on purpose, but the printf is probably printing memory addresses and not the actual strings because you are using the memory address 'selector' ampersand.
[EDIT] - After seing your edit I can see one possible mistake. If in your struct you have declared the strings with a static value:
char (*dict0)[MAX_WORD_LEN+1];
char (*dict1)[MAX_WORD_LEN+1];
You don't need to 'realloc' space for them.
What you need is to have a vector of strings or a vector of dictionaries.
The access pdiction->dict[i] is only a char.
If you want a vector instead, you need to do change your allocation.
I would do it by declaring only pointers in the struct and doing the vector allocation later instead of having a vector of dicts(structs).
You can do that by changing your struct to this:
struct dict {
int len;
char **dict0;
char **dict1;
};
And then you'll have to alloc your vectors in the function:
pdiciton->dict0 = malloc(NUMBER_OF WORDS * sizeof (char*));
for(i=0;i<NUMBER_OF_WORDS;i++)
pdiction->dict0[i] = malloc(MAXIMUM_SIZE_OF_A_WORD*sizeof(char));
The same for dict1.
This might seem odd, but if you think about it is actually logic: if a string is a vector of characters, a vector of strings is a matrix of characters.
The problem with scanf though, seems the same: It is safe to use with enter/return but not with spaces. Try to insert all your data with an enter/return next instead of spaces and see if it works.
Hope this helps.

Resources