0

How do I use scanf to limit the user from entering a string no larger than the array?

What i've tried so far is:

#include<stdio.h>

int main()
{
const MAXSTRING = 5;

char arr[MAXSTRING] = {};

printf("Enter a string: \n");

scanf("%*s", MAXSTRING-1, arr);

return 0;
}

But this doesn't seem to work.

  • I think this is answered well here: http://stackoverflow.com/questions/1621394/how-to-prevent-scanf-causing-a-buffer-overflow-in-c – shooper Mar 20 '15 at 23:43
  • 1
    `*` means "suppress the assignment" when used with `scanf`, unlike `printf` where `*` means "use the next arg as the width/precision". So the best you can do is `"%4s"` and deal with the maintenance nightmare of keeping the `scanf` in sync with the buffer size. – user3386109 Mar 20 '15 at 23:49
  • `const MAXSTRING = 5;` is incorrect in modern C language. You should specify a type. If you want C89 compatibility, you must use a macro. – chqrlie Mar 21 '15 at 03:14

2 Answers2

2

Use one of:

1 == scanf("%4s", arr)
1 == scanf("%4[^\n]", arr)
1 == scanf("%ms", &ptr) // POSIX 2008, remember to call `free`

The second is useful to because scanf's default behavior of separating "word"s by any whitespace is often not useful.

However, it's often better to avoid scanf entirely and get input a line at a time (using something like fgets (but beware truncation and EOF-without-EOL) or getline (POSIX 2008, but pretty easy to implement in terms of fgets)) and then use sscanf or simple array logic on that.

POSIX refs:

o11c
  • 15,265
  • 4
  • 50
  • 75
  • 1
    `%ms` is already in POSIX! Should be portable. `getline` is part of POSIX, too, but support is spotty. – fuz Mar 20 '15 at 23:46
  • @FUZxxl hm, I guess the "posix man pages" package is still POSIX 2003, not POSIX 2008. – o11c Mar 20 '15 at 23:47
  • Sources: [fscanf](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html), [getline](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html). POSIX is available for free on the internet. – fuz Mar 20 '15 at 23:48
  • @FUZxxl was aware, but the man pages are more convenient and I thought they were up-to-date. – o11c Mar 20 '15 at 23:53
  • I'm not sure why there are no man pages for POSIX.1 2008. Perhaps it's too new? As far as I know, POSIX is written roff, so it shouldn't be hard to make these man pages if the sources are available. – fuz Mar 20 '15 at 23:55
  • @FUZxxl Found an (old) bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=622918 – o11c Mar 21 '15 at 00:01
  • `char arr[5]; scanf("%5s", arr);` is a problem. Try `scanf("%4s", arr);` – chux - Reinstate Monica Mar 21 '15 at 00:58
  • 1
    `scanf("%5[^\n]", arr);` puts nothing in `arr` and `arr` remains uninitialized if first `char` is `'\n'` and leaves `'\n'` in `stdin`. – chux - Reinstate Monica Mar 21 '15 at 01:00
  • 1
    @chux Well, that's what checking the return value is for. You're right about the off-by-one though. – o11c Mar 21 '15 at 19:35
1

If you want to have the user enter a string terminated with a newline, it's a good idea to use fgets instead of scanf. The function fgets receives as a parameter the length of the buffer and won't overrun the buffer. You can check its result to figure out if truncation occured. Consult the documentation of your C language implementation for more details.

fgets(arr, MAXSTRING, stdin); 
fuz
  • 88,405
  • 25
  • 200
  • 352
  • `fgets` is not semantically equivalent to the `scanf` you are replacing, but it is much easier to understand and much safer. You should check the return value as the contents of `arr` is indeterminate upon `EOF`. – chqrlie Mar 21 '15 at 03:12
  • @chqlie Indeed. That's why I state “If you want to have the user enter a string terminated with a newline, …” I really hope OP is going to read the manual before using the function (or any other function). – fuz Mar 21 '15 at 05:21