1

I'm trying to write a simple C function for command line input that checks whether the user's input was too long. I've left the debug prints in to show what happens.

bool read_line(char str[])
{
    char c = '\0';
    int max_chars = sizeof(str) / sizeof(char);
    printf("%d\n", max_chars);
    int i = 0;
    while (true) {
        c = getchar();
        if (c == '\n' || c == EOF) {
            break;
        }
        printf("The value of c is %c\n", c);
        i++;
        printf("The value of i is %d\n", i);
        if (i > max_chars) {
            return false;
        }
        printf("Inserting character %c\n", c);
        str[i] = c;
        printf("str[i] is %c\n", str[i]);
    }

    return true;
}

The testing code is:

char str[] = {[0 ... SIZE - 1] = 0};
bool length_valid = read_line(str);
if (!length_valid) {
    printf("Input too long\n");
    return 1;
}
puts(str);
return 0;

Here is the output from running the program; I entered the line "forth".

8
forth
The value of c is f
The value of i is 1
Inserting character f
str[i] is f
The value of c is o
The value of i is 2
Inserting character o
str[i] is o
The value of c is r
The value of i is 3
Inserting character r
str[i] is r
The value of c is t
The value of i is 4
Inserting character t
str[i] is t
The value of c is h
The value of i is 5
Inserting character h
str[i] is h
8
forth
The value of c is f
The value of i is 1
Inserting character f
str[i] is f
The value of c is o
The value of i is 2
Inserting character o
str[i] is o
The value of c is r
The value of i is 3
Inserting character r
str[i] is r
The value of c is t
The value of i is 4
Inserting character t
str[i] is t
The value of c is h
The value of i is 5
Inserting character h
str[i] is h
<Empty line>

The string was clearly shown to be filled with characters moments before, yet somehow in the intervening time it has become null.

EMBLEM
  • 2,207
  • 4
  • 24
  • 32
  • Change it to `int c = '\0';` – Kerrek SB Mar 12 '14 at 00:12
  • @KerrekSB Really? I guess I assumed `getchar()` returns a character. – EMBLEM Mar 12 '14 at 00:13
  • Did you read the manual for it? – Kerrek SB Mar 12 '14 at 00:13
  • And move the `i++` elsewhere. Think about it. – Kerrek SB Mar 12 '14 at 00:14
  • @KerrekSB It works now, but why can it not take more than 8 characters, regardless of the size I give it? – EMBLEM Mar 12 '14 at 00:20
  • Naturally. That's not how arrays work.... – Kerrek SB Mar 12 '14 at 00:24
  • 1
    I'm guessing you're running on a 64-bit machine. The `str` parameter is of type `char *` (yes, even though you said `char[]`, it's still just really a pointer to `char`. On a 64-bit machine, the size of a pointer will be 8 bytes. – Mike Holt Mar 12 '14 at 00:27
  • You started writing on `str[1]`. The initial value of `str[0]` is `0` then the string `str` will always be an empty string. – alvits Mar 12 '14 at 02:07
  • @MikeHolt How can I get the maximum number of elements a given array can hold once I pass it to a function? Because once I do, it decays to a pointer. I can use `sizeof()` on arrays (so it seems) with correct results, but not pointers. – EMBLEM Mar 12 '14 at 03:30
  • `char c = '\0';` should be `int c = '\0';` – chux - Reinstate Monica Mar 12 '14 at 04:53
  • Using `sizeof()` on arrays that are declared in the same scope as where `sizeof()` is used will return the size of the array, because the compiler has complete type information for the array in that scope. When using `sizeof()` on a pointer type inside a function, the compiler only knows that it's a pointer, and it has zero information about what that pointer might actually point to at run-time. So it returns the size of the pointer itself. This is why functions like `fread`, `memcpy`, etc all require an extra size argument. So that's what you need to do: add a size argument to your function. – Mike Holt Mar 12 '14 at 07:42
  • Alternatively, if you dislike the idea of adding an extra argument, you could define a struct containing both the array and the array size, then modify the function to take a pointer to that struct type. Then inside the function, you could use `ptr->array` and `ptr->size`, for example. – Mike Holt Mar 12 '14 at 07:48

2 Answers2

1

EOF kind of had it correct, but the much easier fix is to make max_chars a parameter like:

bool read_line(char str[], int max_chars)

...and then call it by:

bool length_valid = read_line(str, sizeof(str));

...and that should get you most of the way there.

James Antill
  • 2,825
  • 18
  • 16
0

int max_chars = sizeof(str)/sizeof(char);

Should be

int max_chars = strlen(str);

Because:
sizeof(char) is defined to be 1
sizeof(char*) depends on your machine and is not the length of your string.

Either way, this program will not give you any useful information:
Once you correct the errors, you will basically be testing for strlen(str) <= strlen(str), which should obviously always be true.
You might find some use in checking something like strlen(len) <= BUFFSIZE after defining a suitable BUFFSIZE as a macro. However, be aware that strlen() can only be used on a null-terminated string.

EOF
  • 6,273
  • 2
  • 26
  • 50
  • `strlen()` is not what's needed here. The function is writing into the array, not reading from the array. – Mike Holt Mar 12 '14 at 07:50
  • Also, the reason that the `puts(str)` after the function call prints an empty string is that `str[0]` is never being populated and is set to 0 (null) before the function call. Thus `str[1]` through `str[8]` contain the characters read by `getchar()`, but since `str[0]` is null, `str` is effectively still a zero-length string. You need to put the `i++` *after* the `str[i]=c;`. – Mike Holt Mar 12 '14 at 07:55
  • And once you do that, you need to make sure to change `if (i > max_chars)` to `if (i >= (max_chars-1))` (or some equivalent expression) to leave room for the trailing null character. If `max_chars` is set to the size of the array, and you fill `str[max_chars-1]` with a character other than the null character (0), you will overrun the array whenever you print it out, and you'll likely end up with a bunch of non-printable garbage characters on the screen, because you'll be printing out whatever happens to reside in memory after the array. – Mike Holt Mar 12 '14 at 08:04