58

I want to read input from user using C program. I don't want to use array like,

char names[50];

because if the user gives string of length 10, then the remaining spaces are wasted.

If I use character pointer like,

char *names;

then I need to allocate memory for that in such a way of,

names = (char *)malloc(20 * sizeof(char));

In this case also, there is a possibility of memory wastage.

So, what I need is to dynamically allocate memory for a string which is of exactly same as the length of the string.

Lets assume,

If the user input is "stackoverflow", then the memory allocated should be of 14 (i.e. Length of the string = 13 and 1 additional space for '\0').

How could I achieve this?

1
  • some of the compilers came up with these solution char a[ ] instead which is called dynamic array!
    – niko
    Commented Nov 17, 2011 at 8:29

10 Answers 10

57

Read one character at a time (using getc(stdin)) and grow the string (realloc) as you go.

Here's a function I wrote some time ago. Note it's intended only for text input.

char *getln()
{
    char *line = NULL, *tmp = NULL;
    size_t size = 0, index = 0;
    int ch = EOF;

    while (ch) {
        ch = getc(stdin);

        /* Check if we need to stop. */
        if (ch == EOF || ch == '\n')
            ch = 0;

        /* Check if we need to expand. */
        if (size <= index) {
            size += CHUNK;
            tmp = realloc(line, size);
            if (!tmp) {
                free(line);
                line = NULL;
                break;
            }
            line = tmp;
        }

        /* Actually store the thing. */
        line[index++] = ch;
    }

    return line;
}
3
  • 11
    It's generally more efficient to increase the size by a multiplicative factor (ie 1.5x or double the size) unless you know that your data comes in records of a fixed size. Commented Dec 2, 2011 at 17:31
  • This getln() does not return NULL at end of file. How do you check for end of file?
    – chqrlie
    Commented Jan 4, 2016 at 3:30
  • CHUNK is how many bytes to allocate. You can call it CHUNKSIZE if that makes it clearer.
    – Jet Blue
    Commented Aug 10, 2019 at 5:18
9

You can also use a regular expression, for instance the following piece of code:

char *names
scanf("%m[^\n]", &names)

will get the whole line from stdin, allocating dynamically the amount of space that it takes. After that, of course, you have to free names.

1
  • 8
    The m scanf modifier is non standard. It may or may not be supported by your C library.
    – chqrlie
    Commented Jan 4, 2016 at 3:33
8

You could have an array that starts out with 10 elements. Read input character by character. If it goes over, realloc another 5 more. Not the best, but then you can free the other space later.

1
  • 1
    In the Linux manual for vsnprintf this is exactly what they do in their example (although it's about formatting string not input, but the principle is the same.) Commented Nov 17, 2011 at 8:24
8

If you ought to spare memory, read char by char and realloc each time. Performance will die, but you'll spare this 10 bytes.

Another good tradeoff is to read in a function (using a local variable) then copying. So the big buffer will be function scoped.

2

Below is the code for creating dynamic string :

void main()
{
  char *str, c;
  int i = 0, j = 1;

  str = (char*)malloc(sizeof(char));

  printf("Enter String : ");

  while (c != '\n') {
    // read the input from keyboard standard input
    c = getc(stdin);

    // re-allocate (resize) memory for character read to be stored
    str = (char*)realloc(str, j * sizeof(char));

    // store read character by making pointer point to c
    str[i] = c;

    i++;
    j++;
  }

  str[i] = '\0'; // at the end append null character to mark end of string

  printf("\nThe entered string is : %s", str);

  free(str); // important step the pointer declared must be made free
}
2
  • 3
    Undefined behavior because c is not initialized for first test, infinite loop and final crash on empty input file.
    – chqrlie
    Commented Jan 4, 2016 at 3:24
  • 2
    Moreover, what about free(c)? Should also be necessary imo!
    – knacker123
    Commented Feb 24, 2016 at 23:22
1

Here's a snippet which I wrote which performs the same functionality.

This code is similar to the one written by Kunal Wadhwa.

char *dynamicCharString()
{
    char *str, c;
    int i = 0;
    str = (char*)malloc(1*sizeof(char));

    while(c = getc(stdin),c!='\n')
    {
      str[i] = c;
      i++;
      realloc(str,i*sizeof(char));
    }
    str[i] = '\0';
    return str;
}
0

First, define a new function to read the input (according to the structure of your input) and store the string, which means the memory in stack used. Set the length of string to be enough for your input.

Second, use strlen to measure the exact used length of string stored before, and malloc to allocate memory in heap, whose length is defined by strlen. The code is shown below.

int strLength = strlen(strInStack);
if (strLength == 0) {
    printf("\"strInStack\" is empty.\n");
}
else {
    char *strInHeap = (char *)malloc((strLength+1) * sizeof(char));
    strcpy(strInHeap, strInStack);
}
return strInHeap;

Finally, copy the value of strInStack to strInHeap using strcpy, and return the pointer to strInHeap. The strInStack will be freed automatically because it only exits in this sub-function.

0

This is a function snippet I wrote to scan the user input for a string and then store that string on an array of the same size as the user input. Note that I initialize j to the value of 2 to be able to store the '\0' character.

char* dynamicstring() {
    char *str = NULL;
    int i = 0, j = 2, c;
    str = (char*)malloc(sizeof(char));
    //error checking
    if (str == NULL) {
        printf("Error allocating memory\n");
        exit(EXIT_FAILURE);
    }

    while((c = getc(stdin)) && c != '\n')
    {
        str[i] = c;
        str = realloc(str,j*sizeof(char));
        //error checking
        if (str == NULL) {
            printf("Error allocating memory\n");
            free(str);
            exit(EXIT_FAILURE);
        }

        i++;
        j++;
    }
    str[i] = '\0';
    return str;
}

In main(), you can declare another char* variable to store the return value of dynamicstring() and then free that char* variable when you're done using it.

-1
char* load_string()
 {

char* string = (char*) malloc(sizeof(char));
*string = '\0';

int key;
int sizer = 2;

char sup[2] = {'\0'};

while( (key = getc(stdin)) != '\n')
{
    string = realloc(string,sizer * sizeof(char));
    sup[0] = (char) key;
    strcat(string,sup);
    sizer++

}
return string;

}

int main()
  {
char* str;
str = load_string();

return 0;
  }
1
  • 2
    Please explain the answer and the code that you have written. A description will not only help the person asking the question but anyone stumbling upon it.
    – Prateek
    Commented Aug 4, 2016 at 16:06
-1

realloc is a pretty expensive action... here's my way of receiving a string, the realloc ratio is not 1:1 :

char* getAString()
{    
    //define two indexes, one for logical size, other for physical
    int logSize = 0, phySize = 1;  
    char *res, c;

    res = (char *)malloc(sizeof(char));

    //get a char from user, first time outside the loop
    c = getchar();

    //define the condition to stop receiving data
    while(c != '\n')
    {
        if(logSize == phySize)
        {
            phySize *= 2;
            res = (char *)realloc(res, sizeof(char) * phySize);
        }
        res[logSize++] = c;
        c = getchar();
    }
    //here we diminish string to actual logical size, plus one for \0
    res = (char *)realloc(res, sizeof(char *) * (logSize + 1));
    res[logSize] = '\0';
    return res;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.