0

I know how to make function with int, double, float with user input inside(im currently using scanf).

int getData(){
    int a;
    scanf("%i",&a);
    return a;
}

but how to make function with string type and user input inside, then we return that value with type string?

  • 1
    There are more than one way to "return a string" in C. – user202729 Sep 21 '18 at 10:33
  • i know basic return, but without user input inside. im currently confusing if we deal with user input. – Faviansyah Arianda Sep 21 '18 at 10:35
  • 1
    Possible duplicate of [Reading a string with scanf](https://stackoverflow.com/questions/5406935/reading-a-string-with-scanf) – Alexandru Clonțea Sep 21 '18 at 10:51
  • no, its different question. this question about string and user input inside a function, and return it value. thats topic about just reading a string. – Faviansyah Arianda Sep 21 '18 at 10:54
  • The question linked above is poorly-titled, it is actually about a why you pass `buffer` instead of `&buffer` to `scanf`, while normally you have `&integer` etc. Don't use the code shown there as an example of how to use `scanf` for reading strings – in reality you _must_ guard against buffer overflow, but that question (and the answer) omits that part since it is not relevant for the actual question. – Arkku Sep 21 '18 at 12:01

2 Answers2

3

A C string is an array of char terminated by a NUL (zero) byte. Arrays are normally passed around as pointers to the first element. The problem with returning that from the function is that the address pointed to must remain valid beyond the lifetime of the function, which means it needs to be either a static buffer (which is then overwritten by any subsequent calls to the same function, breaking earlier returned values) or allocated by the function, in which case the caller is responsible for freeing it.

The scanf you mention is also problematic for reading interactive user input, e.g., it may leave the input in an unexpected state such as when you don't consume the newline at the end of a line the next call to scanf (maybe in an unrelated function) may surprisingly fail to give the expected result when it encounters the newline.

It is often simpler to read input into a buffer line-by-line, e.g., with fgets, and then parse the line from there. (Some inputs you may be able to parse without a buffer simply by reading character by character, but such code often gets long and hard to follow quickly.)

An example of reading any string, which may contain whitespace other than the newline, would be something like:

/// Read a line from stdin and return a `malloc`ed copy of it without
/// the trailing newline. The caller is responsible for `free`ing it.
char *readNewString(void) {
    char buffer[1024];
    if (!fgets(buffer, sizeof buffer, stdin)) {
        return NULL; // read failed, e.g., EOF
    }
    int len = strlen(buffer);
    if (len > 0 && buffer[len - 1] == '\n') {
        buffer[--len] = '\0'; // remove the newline
        // You may also wish to remove trailing and/or leading whitespace
    } else {
        // Invalid input
        //
        // Depending on the context you may wish to e.g.,
        // consume input until newline/EOF or abort.
    }
    char *str = malloc(len + 1);
    if (!str) {
        return NULL; // out of memory (unlikely)
    }
    return strcpy(str, buffer); // or use `memcpy` but then be careful with length
}

Another option is to have the caller supply the buffer and its size, then just return the same buffer on success and NULL on failure. This approach has the advantage that the caller may decide when a buffer is reused and whether the string needs to be copied or simply read once and forgotten.

Arkku
  • 41,011
  • 10
  • 62
  • 84
0

Extending Arkku's approach to an unlimited size (in fact it is limited to SIZE_MAX - 1 characters) as input:

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

#define BUFFER_MAX (256)

int read_string(FILE * pf, char ** ps)
{
  int result = 0; 

  if (!ps)
  {
    result = -1;
    errno = EINVAL;
  }
  else
  {
    char buffer[BUFFER_MAX];
    size_t len = 0;

    *ps = NULL;
    while (NULL != fgets(buffer, sizeof buffer, pf))
    {
      len += strlen(buffer);

      {
        void * p = realloc(*ps, len + 1);
        if (!p)
        {
          int es = errno;
          result = -1;
          free(*ps);
          errno = es;
          break;
        }

        *ps = p;
      }

      strcpy(&(*ps)[len], buffer);
    }

    if (ferror(pf))
    {
      result = -1;
    }
  }

  return result;
}

Call it like this:

#include <stdlib.h>
#include <stdlio.h>

int read_string(FILE * pf, char ** ps);

int main(void);
{
  char * p;

  if (-1 == read_string(stdin, &p)) /* This read from standard input, 
                                       still any FILE* would do here. */
  {
    perror("read_string() failed");
    exit(EXIT_FAILURE);
  }

  printf("Read: '%s'\n", p);

  free(p); /* Clean up. */
}
alk
  • 69,737
  • 10
  • 105
  • 255
  • Good for demonstrating the use of `realloc`, but for the specific case of reading user input from `stdin` I would choose a fairly small maximum supported line length and fail beyond that, rather than allow the program to read an (almost) arbitrarily long string into memory. =) – Arkku Sep 21 '18 at 16:51
  • @Arkku: Thanks. "*I would choose ...*" Why? Standard Input could be redirected ... – alk Sep 21 '18 at 18:11
  • Because the thing that is being read is never "arbitrary string without linefeeds or NUL bytes", but something like "username", "URL", "choice from given options", or "shell command", all of which have maximum lengths several orders of magnitude smaller than `SIZE_MAX`. So while _supporting_ an arbitrary upper limit in a library function is good, I would much prefer to also be able to supply a call-specific maximum, instead of wasting memory and CPU reading potentially gigabytes of text from a redirected `stdin` just to reject it once it is returned. Better to fail early. – Arkku Sep 21 '18 at 18:40