0

I want to make a C program that reads a string, but it doesn't require the max length of the array.

I tried this, but it gives me an error:

#include<stdio.h>

int main(){
  char a[];
  scanf("%s",&a[]);
}

Can I put the length of the array based on the array input, maybe using something like length_of ?

nielsen
  • 5,641
  • 10
  • 27
Marco
  • 11
  • 1
  • 1
    Well you can only measure a length of a string after having read it. The computer will have to store it somewhere. Hence you are going to need a buffer to read (parts of) the input and the assemble the entire string from the read values afterwards. Usually you have a notion what the user is going to enter which will help you to define a buffer size that is most likely to be large enough to hold the entire string. – Ronald Feb 02 '22 at 13:25
  • The number of characters stored by `scanf` can be limited: https://stackoverflow.com/questions/12306591/read-no-more-than-size-of-string-with-scanf – nielsen Feb 02 '22 at 13:35
  • 1
    Just make a buffer of 128 bytes or so then use fgets. Saving some 50 bytes give or take is nonsense on hosted systems. – Lundin Feb 02 '22 at 13:35
  • "but it doesn't require the max length of the array." --> Sane systems always have a max. Just select one for your input. – chux - Reinstate Monica Feb 02 '22 at 13:43

1 Answers1

1

I was about to tell you that it was impossible because you need to have an allocate array to read into it when I realized that C++ extractor could do it (inside a std::string). It is far from trivial and will require that you start with an arbitrary allocated array and realloc it when you need more space.

Here could be a possible code that reads a word of arbitrary size until the first space character (or EOF):

static char* resize(char* buf, size_t* size) {
    size_t new_size = *size * 2;
    if (new_size <= *size) { // size_t overflow
        free(buf);
        return NULL;
    }
    char* newbuf = realloc(buf, *size);
    if (!newbuf) {
        free(buf);
        return NULL;
    }
    return newbuf;
}

char* getWord(FILE* fd, size_t* size) {
    size_t len = 8, cur = 0;
    char* buf = malloc(len);
    if (!buf) return NULL;

    for (;;) {
        int c = fgetc(fd);
        if (c == EOF || isspace(c)) {
            break;
        }
        if (cur >= len) {
            if (!(buf = resize(buf, &len))) {
                return NULL;
            }
        }
        buf[cur++] = c;
    }
    if (cur >= len) {
        if (!(buf = resize(buf, &len))) {
            return NULL;
        }
    }
    buf[cur] = '\0';
    if (size) {
        *size = len;
    }
    return buf;
}

It could be used that way:

int main() {
    size_t sz;
    char* buf = getWord(stdin, &sz);
    printf("%s (%d)\n", buf, sz);
    return 0;
}

and when passed exactly 8 characters (abcdefgh), it correctly displays:

abcdefgh (16)

because it allocated one extra position for the terminating null.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • OK until `*size * 2` overflows - code lacks detection. Hopefully OP can tolerate such a limit rather than "doesn't require the max length of the array." – chux - Reinstate Monica Feb 02 '22 at 13:58
  • Differences between this code and `scanf("%s" ...` 1) `"%s"` discards leading white-space. This code stop reading on the first white-space. 2) `"%s"` does not discard the first white-space after non WS input. This code discards that WS. Perhaps `unget()`? 3) This code could return a `""`, unlike `"%s"`. Still illustrative idea to OP's goal. – chux - Reinstate Monica Feb 02 '22 at 14:04
  • @chux-ReinstateMonica I could say that I assumed that memory will be exhausted before the int overflow, but I must admit that I just forgot to test it. That being said I know no system with more RAM than the size of an int... BTW I did not try to implement all of the `scanf("%s"...)` feature but just to show how it was possible to read a space delimited string of unknown size in C. I have made it explicit in my answer. – Serge Ballesta Feb 02 '22 at 14:05
  • re: "no system with more RAM than the size of an int" --> I recall the days of 16-bit `int` and pondering when systems will exceed the _huge_ amount of 64K. Systems exceeding 4G memory will centrally be very common this decade - 64-bit RAM systems perhaps in another few decades. – chux - Reinstate Monica Feb 02 '22 at 14:10
  • @chux: I am old enough to have played with 16 bits systems! But if I correctly remember using more than 32767 in one single char array was not that simple... Anyway, you are right, and I have added the overflow test ;-) – Serge Ballesta Feb 02 '22 at 14:14
  • I do recall DOS 16 bit systems with a few meg of memory breaking _long_ `strcat()` like functions. This larger point being that unbounded input functions deserve limit testing. Users are fallible and nefarious - i.e. evil. Robust code detects oversize just like this code well handles the rare `if (!buf)`. – chux - Reinstate Monica Feb 02 '22 at 14:21
  • @chux-ReinstateMonica Nostalgy for the *good* ol' days... – Serge Ballesta Feb 02 '22 at 14:23