Dynamically prompt for string without knowing string size - c

In C, what is the best way of prompting and storing a string without wasted space if we cannot prompt for the string length. For example, normally I would do something like the following...
char fname[30];
char lname[30];
printf("Type first name:\n");
scanf("%s", fname);
printf("Type last name:\n");
scanf("%s", lname);
printf("Your name is: %s %s\n", fname, lname);
However, I'm annoyed with the fact that I have to use more space than needed so I do not want to use char fname[30], but instead dynamically allocate the size of the string. Any thoughts?

You can create a function that dynamically allocates memory for the input as the user types, using getchar() to read one character at a time.
#include <stdio.h>
#include <stdlib.h>
void* safeRealloc(void* ptr, size_t size) {
void *newPtr = realloc(ptr, size);
if (newPtr == NULL) { // if out of memory
free(ptr); // the memory block at ptr is not deallocated by realloc
}
return newPtr;
}
char* allocFromStdin(void) {
int size = 32; // initial str size to store input
char* str = malloc(size*sizeof(char));
if (str == NULL) {
return NULL; // out of memory
}
char c = '\0';
int i = 0;
do {
c = getchar();
if (c == '\r' || c == '\n') {
c = '\0'; // end str if user hits <enter>
}
if (i == size) {
size *= 2; // duplicate str size
str = safeRealloc(str, size*sizeof(char)); // and reallocate it
if (str == NULL) {
return NULL; // out of memory
}
}
str[i++] = c;
} while (c != '\0');
str = safeRealloc(str, i); // trim memory to the str content size
return str;
}
int main(void) {
puts("Type first name:\n");
char* fname = allocFromStdin();
puts("Type last name:\n");
char* lname = allocFromStdin();
printf("Your name is: %s %s\n", fname, lname);
free(fname); // free memory afterwards
free(lname); // for both pointers
return 0;
}

From man scanf:
• An optional 'm' character. This is used with string conversions (%s,
%c, %[), and relieves the caller of the need to allocate a
corresponding buffer to hold the input: instead, scanf() allocates a
buffer of sufficient size, and assigns the address of this buffer to
the corresponding pointer argument, which should be a pointer to a
char * variable (this variable does not need to be initialized before
the call). The caller should subsequently free(3) this buffer when it
is no longer required.
this however is a POSIX extension (as noted by fiddling_bits).
To be portable I think that in your usage case I would prepare a function like the following:
char *alloc_answer() {
char buf[1000];
fgets(buf,sizeof(buf),stdin);
size_t l = strlen(buf);
if (buf[l-1]=='\n') buf[l]=0; // remove possible trailing '\n'
return strdup(buf);
}
even if this solution will break lines longer than 1000 characters (but it prevents buffer overflow, at least).
A fully featured solution would need to read input in chunks and realloc the buffer on every chunk...

Related

How to correctly malloc a struct in C

Here is my full code, it looks like to work, but it's not working very well.
I would accept any code, that is working like this.
Firstly, the code works, but when I want to add the third name to the struct, it crashes.
Is there any other way to do this?
I need struct, because in the future, I want to add some other params, like age, average, gender, etc.
Please, help me out.
//The student table
typedef struct students {
char name[50];
} students;
//Global params
int scount = 0;
students *s;
//Basic functions
void addNewStudent();
int main()
{
int loop = 1;
char in;
int ch;
printf("Willkommen.\n Wahlen Sie bitte von die folgenden Optionen:\n");
while (loop)
{
printf("\t[1] Neue Student eingeben\n");
printf("\t[9] Programm beenden\n");
scanf(" %c", &in);
while ((ch = getchar()) != '\n');
switch (in)
{
case '1':
addNewStudent();
break;
case '9':
loop = 0;
break;
default: printf("------\nOption nicht gefunden.\n------\n");
break;
}
}
free(s);
return 0;
}
void addNewStudent()
{
int index = 0;
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
realloc(s, sizeof(students) * scount);
}
printf("Geben Sie Bitte die Name:\n");
fgets(s[scount].name, sizeof(s[scount].name), stdin);
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
scount++;
}
I'm using Visual Studio.
Thanks for help!
students *mynew= realloc(s, sizeof(students)* (scount+1));
if( mynew != NULL )
s=mynew;
Otehrwise you are having a memory leak. You didn't use the return value of realloc.
Don't cast the return type of malloc.
As per standard §7.22.2.35
void *realloc(void *ptr, size_t size)
The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by size.
It is good not to use the same pointer variable on which you are calling malloc because in case it fails you will lose reference to the old one too (unless it is stored by other means).
Also you didn't check the return value of malloc.
s = malloc(sizeof(students));
if( s == NULL ){
frpntf(stderr,"%s","Memory allocation failed");
exit(1);
}
Also you should check the return value of fgets().
if( fgets(s[scount].name, sizeof(s[scount].name), stdin) == NULL){
fprintf(stderr,"%s","Error in input");
exit(1);
}
Also trying to compile your code it showed this
warning: ignoring return value of ‘realloc’, declared with attribute warn_unused_result [-Wunused-result]
realloc(s, sizeof(students) * scount);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When compiling try not to ignore any warning messages. It showed the problem you had.
Important point: (why scount+1 in realloc?)
When reallocating the general idea is increase the number of students. And for that you need to have extra memory allocated for an student. That's why the scount+1 in the code.(realloc).
Some other points:
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
You can do it like this also
size_t len = strlen(s[scount].name);
if(len){
s[scount].name[len-1]='\0';
}
To understand why from standard §7.21.7.2
char *fgets(char * restrict s, int n,FILE * restrict stream)
The fgets function reads at most one less than the number of
characters specified by n from the stream pointed to by stream into
the array pointed to by s. No additional characters are read after a
new-line character (which is retained) or after end-of-file. A null
character is written immediately after the last character read into
the array.
\0 character was there already in the inputted string. You can get the length of it but you know that the one before the \0 is the \n character 1 that you entered by pressing the Enter key. We are overwriting it with the \0.
1. This is the usual case but not the only one. There are two cases where this might not be the right way to look at the thing.
The input line has n-1 or more characters before the '\n'. The the one before \0 will not be the \n rather it will be some character inputted by the user.
The last line is a stream which may not have a '\n'. (stdin closed). In that case also the input doesn't contain the \n.
So in these cases the idea of removing \n would fail.Discussed in comment. (chux)
A better and safe solution than overwriting this way:
s[scount].name[strcspn(s[scount].name, "\n")] = '\0';
The explanation from the link is that if a \0 is given as input then we will basically write to s[scount].name[SIZE_MAX] which is not desired.
From the standard §7.24.5.3
size_t strcspn(const char *s1, const char *s2)
The strcspn function computes the length of the maximum initial
segment of the string pointed to by s1 which consists entirely of
characters not from the string pointed to by s2.
How to correctly malloc a struct in C ?
p = malloc(sizeof *p);
if (p == NULL) Handle_OutOfMemory();
How to correctly re-allocate a struct in C ?
void *t = realloc(p, sizeof *p * number_of_elements);
if (t == NULL && number_of_elements > 0) {
Handle_OutOfMemory();
} else {
p = t;
}
p points to some struct. Notice no coding of that type in above.
OP' primary problem is not using the return value of realloc() and allocating 1-too-small
// realloc(s, sizeof(students) * scount);
s = realloc(s, sizeof *s * (scount+1)); // or use above code with check for out-of-memory.
realloc returns a new pointer that you need to keep:
students* snew = realloc(s, sizeof(students) * (scount + 1));
if (!snew) {
free(s); // If there is not enough memory, the old memory block is not freed
// handle out of memory
} else {
s = snew;
}
You are not allocating it back! Take a look at how realloc works. You need to assign the pointer back after making the re-allocation like this.
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
students *temp = realloc(s, sizeof(students) * (scount+1));
if(temp == NULL){
free(s);
}
else{
s = temp;
}
}
By Definition, realloc returns a void pointer but you aren't collecting it.
void *realloc(void *ptr, size_t size);
realloc returns a NULL if there's not enough space. So you can re-assign it when you are sure that it is not NULL
Just make a small change above and your code works like a charm!
Cheers!

Why Does my program have Memory Leaks?

I am trying to create a small c program that will read a string with arbitrary size, without having any memory leaks.
According to my research, the function malloc can be used to allocate a number of bytes for whatever data we want to store.
In my program, I start by allocating space for 0 characters, and I make the pointer word point to it. Then whenever I read a single character, I make a pointer oldWord that points to word, which frees the old memory location once I allocate a larger memory location for the new character.
My research shows that the function free can be used to free an old memory location that is no longer needed. However, I am not sure where I am going wrong. Below you can see my code.
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *word = malloc(0);
printf("Enter name: ");
readWord(word);
printf("Your name is: %s\n", word);
free(word);
word = realloc(0);
printf("Enter name: ");
readWord(word);
printf("Your name is: %s\n", word);
free(word);
return 0;
}
void readWord(char *word){
int i = 0;
char *oldWord, c = getchar();
while(c != ' ' && c != '\n'){
oldWord = word;
word = realloc(word, i + 1);
free(oldWord);
word[i++] = c;
c = getchar();
}
oldWord = word;
word = realloc(word, i + 1);
free(oldWord);
word[i] = '\0';
}
The problem as I see it here is with
free(oldWord);
without checking the failure of realloc(). In case realloc() is success, passing the same pointer to free() causes undefined behavior.
That said, some more notes
a syntax like
word = realloc(word, i + 1);
is dangerous, in case realloc() fails, you'll lose the actual pointer, too. You should use a temporary pointer to hold the return value of realloc(), check for success and only then, assign it back to the original pointer, if you need.
In your code, c is of char type, which may not be sufficient to hold all the possible values returned by getchar(), for example, EOF. You should use an int type, that is what getchar() returns.
There are multiple problems in your code:
you free the pointer you passed to realloc(). This is incorrect as realloc() will have freed the memory already if the block was moved.
Otherwise the pointer is freed twice.
The pointer reallocated bu readWord() is never passed back to the caller.
Allocating a 0 sized block has unspecified behavior: it may return NULL or a valid pointer that should not be dereferenced but can be passed to free() or realloc().
You do not test for end of file: there is an infinite loop if the file does not have a space nor a linefeed in it, for example if the file is empty.
you do not have a prototype for readWord() before it is called.
Here is an improved yet simplistic version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *readWord(void);
int main(void) {
char *word;
printf("Enter name: ");
word = readWord();
if (word == NULL) {
printf("Unexpected end of file\n");
else
printf("Your name is: %s\n", word);
free(word);
return 0;
}
char *readWord(void) {
int c;
size_t i = 0;
char *word = NULL;
while ((c = getchar()) != EOF && !isspace(c)) {
word = realloc(word, i + 2);
if (word == NULL)
break;
word[i++] = c;
word[i] = '\0';
}
return word;
}

How to input strings into an array in C?

I tried to get the inputs(strings) from user and store them in an array.But after I ran this code, the program instantly crashed.
#include <stdio.h>
int main() {
int i;
char *word[3];
for(i=0;i<3;i++)
{
printf(" Enter a word: ");
scanf("%s", &word[i]);
}
printf("%s ", word[0]);
return 0;
}
In this line:
scanf("%s", &word[i]);
You need to make sure word[i] is pointing somewhere, and has enough space to occupy the string entered. Since word[i] is a char * pointer, you need to at some time allocate memory for this. Otherwise, it is just a dangling pointer not pointing anywhere.
If you want to stick with scanf(), then you can allocate some space beforehand with malloc.
malloc() allocates requested memory on the heap, then returns a void* pointer at the end.
You can apply malloc() in your code like this:
size_t malloc_size = 100;
for (i = 0; i < 3; i++) {
word[i] = malloc(malloc_size * sizeof(char)); /* allocates 100 bytes */
printf("Enter word: ");
scanf("%99s", word[i]); /* Use %99s to avoid overflow */
/* No need to include & address, since word[i] is already a char* pointer */
}
Note: Must check return value of malloc(), because it can return NULL when unsuccessful.
Additionally, whenever you allocate memory with the use of malloc(), you must use free to deallocate requested memory at the end:
free(word[i]);
word[i] = NULL; /* safe to make sure pointer is no longer pointing anywhere */
Another approach without scanf
A more proper way to read strings should be with fgets.
char *fgets(char *str, int n, FILE *stream) reads a line from an input stream, and copies the bytes over to char *str, which must be given a size of n bytes as a threshold of space it can occupy.
Things to note about fgets:
Appends \n character at the end of buffer. Can be removed easily.
On error, returns NULL. If no characters are read, still returns NULL at the end.
Buffer must be statically declared with a given size n.
Reads specified stream. Either from stdin or FILE *.
Here is an example of how it can be used to read a line of input from stdin:
char buffer[100]; /* statically declared buffer */
printf("Enter a string: ");
fgets(buffer, 100, stdin); /* read line of input into buffer. Needs error checking */
Example code with comments:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMSTR 3
#define BUFFSIZE 100
int main(void) {
char *words[NUMSTR];
char buffer[BUFFSIZE];
size_t i, count = 0, slen; /* can replace size_t with int if you prefer */
/* loops only for three input strings */
for (i = 0; i < NUMSTR; i++) {
/* read input of one string, with error checking */
printf("Enter a word: ");
if (fgets(buffer, BUFFSIZE, stdin) == NULL) {
fprintf(stderr, "Error reading string into buffer.\n");
exit(EXIT_FAILURE);
}
/* removing newline from buffer, along with checking for overflow from buffer */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* checking if nothing was entered */
if (!*buffer) {
printf("No string entered.\n");
exit(EXIT_FAILURE);
}
/* allocate space for `words[i]` and null terminator */
words[count] = malloc(strlen(buffer)+1);
/* checking return of malloc, very good to do this */
if (!words[count]) {
printf("Cannot allocate memory for string.\n");
exit(EXIT_FAILURE);
}
/* if everything is fine, copy over into your array of pointers */
strcpy(words[count], buffer);
/* increment count, ready for next space in array */
count++;
}
/* reading input is finished, now time to print and free the strings */
printf("\nYour strings:\n");
for (i = 0; i < count; i++) {
printf("words[%zu] = %s\n", i, words[i]);
free(words[i]);
words[i] = NULL;
}
return 0;
}
Example input:
Enter a word: Hello
Enter a word: World
Enter a word: Woohoo
Output:
Your strings:
words[0] = Hello
words[1] = World
words[2] = Woohoo
There seems to be a bit of confusion in this area. Your primary problem is you are attempting to write each word to the address of each of pointers you declare with char *word[3];. (not to mention you have no storage allocated at the location pointed to by each pointer -- but you never get there as you attempt to write to the address of each pointer with &word[i] rather than to the pointer itself)
While you can use scanf you will quickly run into one of the many pitfalls with taking user input with scanf that plague all new C programmers (e.g. failing to handle the '\n' left in the input buffer, failing to handle whitespace in strings, failing to limit the number of characters read/written, failing to validate the read or handle EOF, etc...)
A better approach is to simply use fgets and then trim the '\n' that fgets read and includes in the buffer to which it stores the string. A simple example would be:
#include <stdio.h>
#include <string.h>
#define NWDS 3 /* declare a constant for the maximum number of words */
int main (void) {
int i, n = 0;
char word[NWDS][50] = { "" }; /* provide storage or allocate */
for (i = 0; i < NWDS; i++) { /* for a max of NWDS */
printf ("Enter word : "); /* prompt */
if (!fgets (word[i], sizeof word[i], stdin)) /* read/validate */
break; /* protect against EOF */
size_t len = strlen (word[i]); /* get length */
if (word[i][len-1] == '\n') /* check for trailing '\n' */
word[i][--len] = 0; /* overwrite with nulbyte */
}
n = i; /* store number of words read */
putchar ('\n'); /* make it pretty */
for (i = 0; i < n; i++) /* output each word read */
printf (" word[%d] : %s\n", i, word[i]);
#if (defined _WIN32 || defined _WIN64)
getchar(); /* keep terminal open until keypress if on windows */
#endif
return 0;
}
Go ahead and cancel input at any time by generating an EOF during input (ctrl + d on Linux or ctrl + z on windoze), you are covered.
Example Use/Output
$ ./bin/wordsread
Enter word : first word
Enter word : next word
Enter word : last word
word[0] : first word
word[1] : next word
word[2] : last word
Looks things over, consider the other answers, and let me know if you have further questions.
char *word[3]; // <-- this is an array of 3 dangling pointers, of type char*
// they still point nowhere, we later need to set them to some allocated location.
...
for(i=0;i<3;i++) {
word[i] = malloc(some_max_size * sizeof(char)); // <-- allocate space for your word
printf(" Enter a word: ");
scanf("%s", word[i]); // <-- not &word[i]; word[i] is already a char* pointer
}
You are declaring word as array of pointer (char *word[3];). You have to allocate memory to store data. Allocate memory with malloc or similar functions before assigning values.
Yes the code crashes because declaring an array of character
pointers is not enough, you need to set the pointers to point
to memory where the strings can be stored.
E.g.
const int maxLen = 32;
char* word[3] = {NULL,NULL,NULL};
word[i] = malloc(maxLen);
then read the string from keyboard, to ensure that the string is not too
long use fgets and maxLen:
printf("Enter a word:");
fgets(word[i],maxLen,stdin);
#include <stdio.h>
int main(){
int n;
int i=0;
scanf("%d",&n);
char arr[n];
while(n>i){
scanf("%s",&arr[i]);
i+=1;
}
while(n-i<n){
printf(" %c ",arr[n-i]);
i-=1;
}
}
The code char *word[3] made a 3-element array of pointers!
See, you have basically created a character array of pointers, so you cannot put a "string" into each one of them, because the type of a pointer variable is long hexadecimal.

Return a string made with a line read from input

i am trying to code a C function which returns a line read from the input as a char* . I am on Windows and i test my program in the command line by giving files as input and output of my program like this:
cl program.c
program < test_in.txt > test_out.txt
This is my (not working) function:
char* getLine(void)
{
char* result = "";
int i, c;
i = 1;
while((c = getchar()) != EOF)
{
*result++ = c;
i++;
if(c == '\n')
return result - i;
}
return result - i;
}
I was expecting it to work because i previously wrote:
char* getString(char* string)
{
//char* result = string; // the following code achieve this.
char* result = "";
int i;
for(i = 1; *result++ = *string++; i++);
return result - i;
}
And these lines of code have a correct behaviour.
Even if every answers will be appreciated, i would be really thankfull
if any of you could explain me why my getString() function works while my getLine() function doesn't.
Your function does not allocate enough space for the string being read. The variable char* result = "" defines a char pointer to a string literal ("", empty string), and you store some arbitrary number of characters into the location pointed to by result.
char* getLine(void)
{
char* result = ""; //you need space to store input
int i, c;
i = 1;
while((c = getchar()) != EOF)
{
*result++ = c; //you should check space
i++;
if(c == '\n')
return result - i; //you should null-terminate
}
return result - i; //you should null-terminate
}
You need to allocate space for your string, which is challenging because you don't know how much space you are going to need a priori. So you need to decide whether to limit how much you read (ala fgets), or dynamically reallocate space as you read more. Also, how to you indicate that you have finished input (reached EOF)?
The following alternative assumes dynamic reallocation is your chosen strategy.
char* getLine(void)
{
int ch; int size=100; size_t pos=0;
char* result = malloc(size*sizeof(char*));
while( (ch=getchar()) != EOF )
{
*result++ = ch;
if( ++pos >= size ) {
realloc(result,size+=100);
//or,realloc(result,size*=2);
if(!result) exit(1); //realloc failed
}
if( c=='\n' ) break;
}
*result = '\0'; //null-terminate
return result - pos;
}
When you are done with the string returned from the above function, please remember to free() the allocated space.
This alternative assumes you provide a buffer to store the string (and specifies the size of the buffer).
char* getLine(char* buffer, size_t size)
{
int ch;
char* result = buffer;
size_t pos=0;
while( (ch=getchar()) != EOF )
{
*result++ = ch;
if( ++pos >= size ) break; //full
if( c=='\n' ) break;
}
*result = '\0'; //null-terminate
return buffer;
}
Both avoid the subtle interaction between detecting EOF, and having enough space to store a character read. The solution is to buffer a character if you read and there is not enough room, and then inject that on a subsequent read. You will also need to null-ter
Both functions have undefined behaviour since you are modifying string literals. It just seems to work in one case. Basically, result needs to point to memory that can be legally accessed, which is not the case in either of the snippets.
On the same subject, you might find this useful: What Every C Programmer Should Know About Undefined Behavior.
Think of it this way.
When you say
char* result = "";
you are setting up a pointer 'result' to point to a 1-byte null terminated string (just the null). Since it is a local variable it will be allocated on the stack.
Then when you say
*result++ = c;
you are storing that value 'c' in to that address + 1.
So, where are you putting it?
Well, most stacks are to-down; so they grow toward lower addresses; so, you are probably writing over what is already on the stack (the return address for whatever called this, all the registers it needs restore and all sorts of important stuff).
That is why you have to be very careful with pointers.
When you expect to return a string from a function, you have two options (1) provide a string to the function with adequate space to hold the string (including the null-terminating character), or (2) dynamically allocate memory for the string within the function and return a pointer. Within your function you must also have a way to insure your are not writing beyond the end of the space available and you are leaving room for the null-terminating character. That requires passing a maximum size if you are providing the array to the function, and keeping count of the characters read.
Putting that together, you could do something similar to:
#include <stdio.h>
#define MAXC 256
char* getLine (char *s, int max)
{
int i = 0, c = 0;
char *p = s;
while (i + 1 < max && (c = getchar()) != '\n' && c != EOF) {
*p++ = c;
i++;
}
*p = 0;
return s;
}
int main (void) {
char buf[MAXC] = {0};
printf ("\ninput : ");
getLine (buf, MAXC);
printf ("output: %s\n\n", buf);
return 0;
}
Example/Output
$ ./bin/getLine
input : A quick brown fox jumps over the lazy dog.
output: A quick brown fox jumps over the lazy dog.

Getting input into dynamically allocated memory then returning the pointer

Hey guys i've been having some problems with this problem. I have tried it many ways from looping with stdin and saving each letter to scanf(" %s") into an array then declaring the array as a pointer then returning the pointer. I have come close but have mainly had problems when having spaces in my string that I enter. The program needs to call this char* that gets the pointer to the string, twice and then output both strings line after line. Any help greatly appreciated.
char* get_input()
{
char c = 'a';
int counter = 1;
while(c != '\0')
{
c = getc(stdin);
char* d = (char)malloc(sizeof(char)*counter);
*(d+counter) = c;
counter++;
return d;
}
return d;
}
So i was thinking something like this was the best way to do it but i dont know how to return the pointer out of the while loop and then again into the char*. What i really need is some guidance on the best way to store some input straight into an allocated array that will not waste any memory(it will know how long the string you entered is and allocate enough memory) i have also tried with scanf("%s") but i dont know how to stoe the data from scanf into a dynamic memory chunk.
And sorry dont have my actual code atm cause at work and my program is at home. Any ideas pls
your earlier code:
char* get_input()
{
char c = 'a';
int counter = 1;
while(c != '\0')
{
c = getc(stdin);
char* d = (char)malloc(sizeof(char)*counter);
*(d+counter) = c;
counter++;
return d;
}
return d;
}
Oh, no, no, no! You create a separate pointer address pointing to a counter size block of memory with each call to malloc, assign it to d, and then attempt to assign c to *(d+counter) which writes beyond the end of the current block, and which has absolutely no way of knowing what d was during the previous iteration -- sigh...
First, there are as many ways to do dynamic input as there are colors in a rainbow... It can be as simple and witless as using scanf with the %m conversion specifier (older versions and windows use %a). It comes with all the pitfalls that scanf brings with it. However, it can work:
char *get_input_scanf()
{
char *ln = NULL;
scanf ("%m[^\n]%*c", &ln);
return ln;
}
But there are absoutely no limitations on data entry and very little control. (note: unless you pass additional information to any input function or use global #defines or variables, any input method will suffer from this limitation. Consider passing a max length to the input function, or make sure you validate the return)
You can also read character-by-character with getchar() or getc(fp), or you can use line-input methods like fgets or getline. With any of these methods (except getline), you will need to allocate a temporary line buffer to hold the input, and then in order to limit allocation to the amount required, allocate your final buffer based on the strlen + 1 of your input (or use just use strdup). As with all dynamic approaches, you are responsible for tracking it, preserving the start address to the memory block, and freeing the memory when it is no longer needed. Beyond that, the sky is the limit on what you do with the function.
An example of a dynamic input routine with getline could look like the following:
char *get_input()
{
char *ln = NULL; /* line buffer, NULL - getline allocates */
size_t n = 0; /* initial buff size, 0 - getline decides */
ssize_t nchr = 0; /* getline return - actual no. of chars read*/
if ((nchr = getline (&ln, &n, stdin)) != -1)
{
/* strip newline or carriage rtn */
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0;
/* if (!nchr) { // do not accept blank lines
free (ln);
return NULL;
} */
char *input = strdup (ln); /* duplicate ln in input */
free (ln); /* free getline allocated mem */
return input;
}
return NULL;
}
Note: as discussed above, getline imposes no limitation on how long the input string can be, so it is up to you to validate.
There is no limit on the number of different ways you can do dynamic input. (there are probably at least 1000 examples here on SO already) However, it all boils down to either character or line input, so make your choice as to what method you want to use and then write your code. Here is a small working example with the two different functions above. Look over the post and let me know if you have questions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *get_input();
char *get_input_scanf();
int main (void) {
char *line = NULL;
printf ("\nEnter input below, [ctrl+d] to quit\n");
for (;;)
{
printf ("\n input: ");
line = get_input();
if (line)
printf (" value: '%s'\n", line);
else {
printf ("\n value: [ctrl+d] received\n");
break;
}
free (line);
line = NULL;
}
if (line) free (line);
printf ("\n");
line = NULL;
printf ("\nEnter input below to read with get_input_scanf\n\n input: ");
line = get_input_scanf();
if (line) {
printf (" value: '%s'\n", line);
free (line);
}
return 0;
}
char *get_input()
{
char *ln = NULL; /* line buffer, NULL - getline allocates */
size_t n = 0; /* initial , 0 - getline decides*/
ssize_t nchr = 0;
if ((nchr = getline (&ln, &n, stdin)) != -1)
{
/* strip newline or carriage rtn */
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0;
/* if (!nchr) { // do not accept blank lines
free (ln);
return NULL;
} */
char *input = strdup (ln); /* duplicate ln in input */
free (ln); /* free getline allocated mem */
return input;
}
return NULL;
}
char *get_input_scanf()
{
char *ln = NULL;
scanf ("%m[^\n]%*c", &ln);
return ln;
}
Example Use/Output
$ ./bin/getline_getinput
Enter input below, [ctrl+d] to quit
input: some string of input.
value: 'some string of input.'
input: another string that can be any length ........ ........
value: 'another string that can be any length ........ ........'
input:
value: ''
input: a
value: 'a'
input:
value: [ctrl+d] received
Enter input below to read with get_input_scanf
input: some input to show scanf will allocate dynamically as well!
value: 'some input to show scanf will allocate dynamically as well!'

Resources