Dynamically prompt for string without knowing string size

后端 未结 2 1055
一个人的身影
一个人的身影 2021-01-19 03:53

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

相关标签:
2条回答
  • 2021-01-19 04:27

    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;
    }
    
    0 讨论(0)
  • 2021-01-19 04:31

    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...

    0 讨论(0)
提交回复
热议问题