92

If I don't know how long the word is, I cannot write char m[6];,
The length of the word is maybe ten or twenty long. How can I use scanf to get input from the keyboard?

#include <stdio.h>
int main(void)
{
    char  m[6];
    printf("please input a string with length=5\n");
    scanf("%s",&m);
    printf("this is the string: %s\n", m);
    return 0;
}

please input a string with length=5
input: hello
this is the string: hello

showkey
  • 482
  • 42
  • 140
  • 295

11 Answers11

120

Enter while securing an area dynamically

E.G.

#include <stdio.h>
#include <stdlib.h>

char *inputString(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
    char *str;
    int ch;
    size_t len = 0;
    str = realloc(NULL, sizeof(*str)*size);//size is start size
    if(!str)return str;
    while(EOF!=(ch=fgetc(fp)) && ch != '\n'){
        str[len++]=ch;
        if(len==size){
            str = realloc(str, sizeof(*str)*(size+=16));
            if(!str)return str;
        }
    }
    str[len++]='\0';

    return realloc(str, sizeof(*str)*len);
}

int main(void){
    char *m;

    printf("input string : ");
    m = inputString(stdin, 10);
    printf("%s\n", m);

    free(m);
    return 0;
}
klutt
  • 30,332
  • 17
  • 55
  • 95
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • 12
    Multiplying by `sizeof(char)`? Ugh. – Jens Jun 01 '13 at 11:06
  • 7
    @Jens Pfff, that will probably be optimised away. No problems. But if you were to do a global find-and-replace of `char` with `wchar_t`, this solution would still work, unlike other solutions, that would need more tinkering! – Mr Lister Jun 01 '13 at 11:12
  • @Jens I like to write in this way as is defined as `sizeof(char)` is `1`. – BLUEPIXY Jun 01 '13 at 11:14
  • @MrLister It isn't assumed `size` specified for the first is 0 because there is nonsense. And though we can check it. – BLUEPIXY Jun 01 '13 at 11:17
  • If at all, you should multiply by `sizeof (*str)`. But still, it's rampant paranoia and a code smell. – Jens Jun 01 '13 at 11:57
  • 16
    @MrLister Which is why the proper way, if at all, is to multiply with `sizeof (*str)` so you don't even have to *edit* the multiplication when the type changes. – Jens Jun 01 '13 at 12:11
  • 1
    It's definitely the case that `str[len]='\0'; return realloc(str, len);` will result in the terminator being discarded. I think you meant `str[len++] = '\0'`. – sh1 Jun 01 '13 at 12:12
  • @MrLister i had been misunderstood. Fixed. Thanks – BLUEPIXY Jun 01 '13 at 12:13
  • Also, the same error with `if (len>size)`. And `sizeof(char)` is always 1. – sh1 Jun 01 '13 at 12:14
  • OK, now this (and any `malloc()`-based solution) faces the problem of what to do when we run out of memory. `main()` needs some error handling for when `inputString()` returns `NULL`. – sh1 Jun 01 '13 at 12:25
  • 1
    @sh1 `main() needs some error handling for when inputString() returns NULL.` I leave it to him(her). – BLUEPIXY Jun 01 '13 at 12:28
  • 1
    (size *= 2) is better. – KIM Taegyoon Apr 09 '14 at 13:31
  • it depends on the characteristics of the data handled by the optimization. – BLUEPIXY Apr 09 '14 at 18:08
  • why is `size_t size = 10` ? – galois Dec 24 '15 at 23:32
  • @jaska An expected value which is roughly of the required initial value but may be any value. – BLUEPIXY Dec 24 '15 at 23:36
  • It is possible to avoid such a cost if the processing within the size that is expected since because costly as such is carried out in realloc. – BLUEPIXY Dec 24 '15 at 23:42
  • 1
    Isn't `realloc(NULL, sizeof(char)*size);` the same as `malloc(sizeof(char) * size)`? What is the reason that `realloc` is preferred here? – Sri Harsha Chilakapati Feb 27 '16 at 17:25
  • 1
    @SriHarshaChilakapati Just means the unification of the format. There is no problem with `malloc(sizeof(char) * size)`. – BLUEPIXY Feb 27 '16 at 18:19
  • @BLUEPIXY no, `realloc` can't be replaced by `malloc`. If you changed it you would have to `free` the memory before. – germanfr Oct 16 '16 at 14:34
  • @BLUEPIXY `malloc` creates memory. If you created new memory and didn't free the old you'd have a memory leak. `realloc` takes care of that while `malloc` doesn't. – germanfr Oct 16 '16 at 14:47
  • @germanfr It does not have an associated. I say about `realloc(NULL, sizeof(char)*size); the same as malloc(sizeof(char) * size)` – BLUEPIXY Oct 16 '16 at 14:51
  • @BLUEPIXY no, it's not the same. Replacing the first with just the second would lead to memory leaks. – germanfr Oct 16 '16 at 14:53
  • @BLUEPIXY what memory chunk? I am talking about what you said. You said replacing `realloc(...)` with `malloc(...)` leads to the same behavior, while this is false. When you `realloc` successfully, it frees the previous memory, while `malloc` doesn't. So they are not interchangeable as you said. – germanfr Oct 16 '16 at 15:00
  • 2
    @germanfr No. I said about `realloc(NULL, sizeof(char)*size); the same as malloc(sizeof(char) * size)`. I don't say the same of `malloc` and `realloc`. – BLUEPIXY Oct 16 '16 at 15:01
  • @BLUEPIXY ok I get you. I didn't notice the NULL there :) – germanfr Oct 16 '16 at 15:04
  • @germanfr What do you say pointing to anything to do with __old__ ( "the old you'd have a memory leak")? – BLUEPIXY Oct 16 '16 at 15:05
  • It was a waste of time. – BLUEPIXY Oct 16 '16 at 15:07
  • @BLUEPIXY nothing. I didn't notice the NULL so I thought you where talking about replacing every `realloc` with `malloc`, not just the first. I'm sorry. – germanfr Oct 16 '16 at 15:07
  • is the last `realloc` in `inputString` really necessary ? – artm Nov 06 '16 at 10:55
  • @artm No. It just makes the size of the buffer equal to the size of the input string (plus one for the null character). – GriffinG Dec 22 '16 at 01:36
  • 1
    Can you explain why `inputString()` doesn't just return `str`? Is this to free the unused bytes if the length string input from stdin is less than size (initial size)? – Minh Tran Mar 22 '17 at 22:01
  • @MinhTran Yup, It is intended to no include unused areas. – BLUEPIXY Mar 22 '17 at 22:29
  • 1
    @MrLister I do love me some wide wchar_tacters using `wwchar_t_t`! I use them all the time in my battery wchar_tger to display an error message if the battery is too far diswchar_tged or has an unwchar_tacteristic wchar_tging curve. (Damaged batteries are dangerous to rechar_tge and may cost a surwchar_tge to recycle) – user253751 Nov 02 '17 at 01:51
  • @immibis You forgot to press Alt+W to search for whole words only. – Mr Lister Nov 02 '17 at 07:00
  • It may leak memory if the `realloc` fails in `return realloc(str, sizeof(char)*len);`. That call is a waste of time anyway since I doubt any system has finer malloc granularith than 16 bytes – M.M Apr 12 '19 at 02:38
  • @BLUEPIXY There is memory leak in the code, you are assigning value to a unallocated memory cell at `str[len++]=ch;` Your code will try to assign a value to 11th Cell(index 10) even though you originally allocated memory only for 10 characters at the start. – Mysterious Jack Mar 31 '22 at 08:13
25

With the computers of today, you can get away with allocating very large strings (hundreds of thousands of characters) while hardly making a dent in the computer's RAM usage. So I wouldn't worry too much.

However, in the old days, when memory was at a premium, the common practice was to read strings in chunks. fgets reads up to a maximum number of chars from the input, but leaves the rest of the input buffer intact, so you can read the rest from it however you like.

in this example, I read in chunks of 200 chars, but you can use whatever chunk size you want of course.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* readinput()
{
#define CHUNK 200
   char* input = NULL;
   char tempbuf[CHUNK];
   size_t inputlen = 0, templen = 0;
   do {
       fgets(tempbuf, CHUNK, stdin);
       templen = strlen(tempbuf);
       input = realloc(input, inputlen+templen+1);
       strcpy(input+inputlen, tempbuf);
       inputlen += templen;
    } while (templen==CHUNK-1 && tempbuf[CHUNK-2]!='\n');
    return input;
}

int main()
{
    char* result = readinput();
    printf("And the result is [%s]\n", result);
    free(result);
    return 0;
}

Note that this is a simplified example with no error checking; in real life you will have to make sure the input is OK by verifying the return value of fgets.

Also note that at the end if the readinput routine, no bytes are wasted; the string has the exact memory size it needs to have.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • 1
    Needs error handling for `realloc()` returning `NULL` (and, consequently, for `readinput()` returning `NULL`). – sh1 Jun 01 '13 at 12:29
  • Need to check the return value of `fgets()`, else code may enter infinite loop. – chux - Reinstate Monica Dec 06 '15 at 20:18
  • @chux OK, added a note about that. – Mr Lister Dec 06 '15 at 20:27
  • 3
    I think there's an issue with this on the first realloc (realloc when input is NULL). This could point to arbitrary memory, and thus the strcat may not have the intended result (i.e. input should be just the contents of the buffer). Instead, rather than trying to store a string of length templen which has been allocated, it tries to store a string of strlen(arbitrary data) + templen, and gives a "malloc() memory corruption" error. – Brendan Hart Oct 15 '19 at 17:47
  • 3
    @BrendanHart Aw, nobody saw that in six years. Fixed by doing a strcpy rather than a strcat. – Mr Lister Oct 15 '19 at 19:03
  • 1
    To lose the line feed add [`tempbuf[strcspn(tempbuf, "\n")] = 0;`](https://stackoverflow.com/a/28462221) after `fgets` line. – Nae Nov 22 '20 at 17:25
16

I've seen only one simple way of reading an arbitrarily long string, but I've never used it. I think it goes like this:

char *m = NULL;
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
    fprintf(stderr, "That string was too long!\n");
else
{
    printf("this is the string %s\n",m);
    /* ... any other use of m */
    free(m);
}

The m between % and s tells scanf() to measure the string and allocate memory for it and copy the string into that, and to store the address of that allocated memory in the corresponding argument. Once you're done with it you have to free() it.

This isn't supported on every implementation of scanf(), though.

As others have pointed out, the easiest solution is to set a limit on the length of the input. If you still want to use scanf() then you can do so this way:

char m[100];
scanf("%99s",&m);

Note that the size of m[] must be at least one byte larger than the number between % and s.

If the string entered is longer than 99, then the remaining characters will wait to be read by another call or by the rest of the format string passed to scanf().

Generally scanf() is not recommended for handling user input. It's best applied to basic structured text files that were created by another application. Even then, you must be aware that the input might not be formatted as you expect, as somebody might have interfered with it to try to break your program.

sh1
  • 4,324
  • 17
  • 30
  • Note that `"%ms"` is not standard C --- it's probably either a POSIX extension or a GNU extension. – Tim Čas Feb 12 '15 at 21:36
  • 4
    @TimČas: It's part of Posix 2008, which is a standard. There was an earlier similar GNU extension and a similar BSD extension; the Posix standard is intended to unify the various implementations. It's quite possible that it will find its way into a future C standard. – rici Feb 12 '15 at 22:37
10

There is a new function in C standard for getting a line without specifying its size. getline function allocates string with required size automatically so there is no need to guess about string's size. The following code demonstrate usage:

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    while ((read = getline(&line, &len, stdin)) != -1) {
        printf("Retrieved line of length %zu :\n", read);
        printf("%s", line);
    }

    if (ferror(stdin)) {
        /* handle error */
    }

    free(line);
    return 0;
}
Parham Alvani
  • 2,305
  • 2
  • 14
  • 25
  • 5
    Actually, it's not in the C standard, but it does exist in POSIX, so it's quite widely available – klutt Feb 26 '21 at 15:04
6

If I may suggest a safer approach:

Declare a buffer big enough to hold the string:

char user_input[255];

Get the user input in a safe way:

fgets(user_input, 255, stdin);

A safe way to get the input, the first argument being a pointer to a buffer where the input will be stored, the second the maximum input the function should read and the third is a pointer to the standard input - i.e. where the user input comes from.

Safety in particular comes from the second argument limiting how much will be read which prevents buffer overruns. Also, fgets takes care of null-terminating the processed string.

More info on that function here.

EDIT: If you need to do any formatting (e.g. convert a string to a number), you can use atoi once you have the input.

Nobilis
  • 7,310
  • 1
  • 33
  • 67
3

Safer and faster (doubling capacity) version:

char *readline(char *prompt) {
  size_t size = 80;
  char *str = malloc(sizeof(char) * size);
  int c;
  size_t len = 0;
  printf("%s", prompt);
  while (EOF != (c = getchar()) && c != '\r' && c != '\n') {
    str[len++] = c;
    if(len == size) str = realloc(str, sizeof(char) * (size *= 2));
  }
  str[len++]='\0';
  return realloc(str, sizeof(char) * len);
}
KIM Taegyoon
  • 1,917
  • 21
  • 18
1

Take a character pointer to store required string.If you have some idea about possible size of string then use function

char *fgets (char *str, int size, FILE* file);

else you can allocate memory on runtime too using malloc() function which dynamically provides requested memory.

Xiddoc
  • 3,369
  • 3
  • 11
  • 37
Dayal rai
  • 6,548
  • 22
  • 29
1

Read directly into allocated space with fgets().

Special care is need to distinguish a successful read, end-of-file, input error and out-of memory. Proper memory management needed on EOF.

This method retains a line's '\n'.

#include <stdio.h>
#include <stdlib.h>

#define FGETS_ALLOC_N 128

char* fgets_alloc(FILE *istream) {
  char* buf = NULL;
  size_t size = 0;
  size_t used = 0;
  do {
    size += FGETS_ALLOC_N;
    char *buf_new = realloc(buf, size);
    if (buf_new == NULL) {
      // Out-of-memory
      free(buf);
      return NULL;
    }
    buf = buf_new;
    if (fgets(&buf[used], (int) (size - used), istream) == NULL) {
      // feof or ferror
      if (used == 0 || ferror(istream)) {
        free(buf);
        buf = NULL;
      }
      return buf;
    }
    size_t length = strlen(&buf[used]);
    if (length + 1 != size - used) break;
    used += length;
  } while (buf[used - 1] != '\n');
  return buf;
}

Sample usage

int main(void) {
  FILE *istream = stdin;
  char *s;
  while ((s = fgets_alloc(istream)) != NULL) {
    printf("'%s'", s);
    free(s);
    fflush(stdout);
  }
  if (ferror(istream)) {
    puts("Input error");
  } else if (feof(istream)) {
    puts("End of file");
  } else {
    puts("Out of memory");
  }
  return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

I know that I have arrived after 4 years and am too late but I think I have another way that someone can use. I had used getchar() Function like this:-

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//I had putten the main Function Bellow this function.
//d for asking string,f is pointer to the string pointer
void GetStr(char *d,char **f)
{
    printf("%s",d);

    for(int i =0;1;i++)
    {    
        if(i)//I.e if i!=0
            *f = (char*)realloc((*f),i+1);
        else
            *f = (char*)malloc(i+1);
        (*f)[i]=getchar();
        if((*f)[i] == '\n')
        {
            (*f)[i]= '\0';
            break;
        }
    }   
}

int main()
{
    char *s =NULL;
    GetStr("Enter the String:- ",&s);
    printf("Your String:- %s \nAnd It's length:- %lu\n",s,(strlen(s)));
    free(s);
}

here is the sample run for this program:-

Enter the String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!!
Your String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!! 
And It's length:- 67
0

i also have a solution with standard inputs and outputs

#include<stdio.h>
#include<malloc.h>
int main()
{
    char *str,ch;
    int size=10,len=0;
    str=realloc(NULL,sizeof(char)*size);
    if(!str)return str;
    while(EOF!=scanf("%c",&ch) && ch!="\n")
    {
        str[len++]=ch;
        if(len==size)
        {
            str = realloc(str,sizeof(char)*(size+=10));
            if(!str)return str;
        }
    }
    str[len++]='\0';
    printf("%s\n",str);
    free(str);
}
  • return value of `scanf` should be compared for the number of elements you read, not `EOF` – M.M Apr 12 '19 at 02:34
0

I have a solution using standard libraries of C and also creating a string type (alias of char*) like in C++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef char* string;

typedef struct __strstr {
    char ch;
    struct __strstr *next;
}Strstr;

void get_str(char **str) {
    char ch, *buffer, a;
    Strstr *new = NULL;
    Strstr *head = NULL, *tmp = NULL;
    int c = 0, k = 0;
    while ((ch = getchar()) != '\n') {
        new = malloc(sizeof(Strstr));
        if(new == NULL) {
            printf("\nError!\n");
            exit(1);
        }
        new->ch = ch;
        new->next = NULL;
        new->next = head;
        head = new;
    }

    tmp = head;
    while (tmp != NULL) {
        c++;
        tmp = tmp->next;
    }

    if(c == 0) {
        *str = "";
    } else {
        buffer = malloc(sizeof(char) * (c + 1));
        *str = malloc(sizeof(char) * (c + 1));
        if(buffer == NULL || *str == NULL) {
            printf("\nError!\n");
            exit(1);
        }
        tmp = head;
        while (tmp != NULL) {
            buffer[k] = tmp->ch;
            k++;
            tmp = tmp->next;
        }
        buffer[k] = '\0';
        for (int i = 0, j = strlen(buffer)-1; i < j; i++, j--) {
            a = buffer[i];
            buffer[i] = buffer[j];
            buffer[j] = a;
        }
        strcpy(*str, buffer);
        // Dealloc
        free(buffer);
        while (head != NULL) {
            tmp = head;
            head = head->next;
            free(tmp);
        }
    }
}

int main() {
    string str;

    printf("Enter text: ");
    get_str(&str);

    printf("%s\n", str);

    return 0;
}
Blackjack
  • 1,322
  • 1
  • 16
  • 21