0

Can someone please advise regarding the scanf? "message" is an array of 4 rows by 16 columns. After the user enters the numbers (all 1 digit integers), when hitting "Enter" there is an error message. As said - probably something with the scanf.

for (int i = 0; i < M; i++) {
        for (int j = 0; j < columns; j++) {
            scanf("%d", message[i][j]);
        }
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ERN
  • 1
  • 1
  • Use scanf("%d", &message[i][j]); – Vlad from Moscow May 09 '22 at 21:47
  • This is the first rule of `scanf` — you virtually always need that `&`. – Steve Summit May 09 '22 at 21:55
  • `scanf` is simultaneously the most superficially useful, and the most difficult to actually use, function in all of C. If you're trying to learn how to use `scanf` by trial and error, based on what seems reasonable, you are going to fail, and badly. Please read some documentation on it first! – Steve Summit May 09 '22 at 21:56
  • 1
    First off, **never** use `scanf()` without checking the return code.... – DevSolar May 09 '22 at 21:57

2 Answers2

7

scanf seems like a nice, simple, easy, convenient way to read input. And in some ways it is, but it turns out that using it correctly can be surprisingly difficult, and it's chock-full of pitfalls.

You're probably not going to want to learn about all of scanf's intricacies at first. So for now, here are some guidelines for using a subset of scanf's functionality, safely. (For reasons I fail to understand, everybody tells beginners to use scanf, but nobody ever teaches rules like these along with it.)

  1. Remember to always use & in front of the variables you're trying to read. (This is a special rule, for scanf only. Don't try to use that & on the variables you pass to printf, for example.)
  2. Exception to rule 1: Do not use the & when you are reading strings with %s.
  3. If you are reading strings using %s, make sure the variable you read into is either a character array that's big enough for what the user is likely to type, or a pointer to malloc'ed memory that's big enough for what the user is likely to type. (See also note below about avoiding overflow.)
  4. Be aware that %s reads strings that don't contain space characters. You can't use %s to read strings (like full names) that might contain spaces. (For now, please don't worry about how you might read a string that might contain spaces. See also rule 14.)
  5. Don't try to use scanf and fgets (or the obsolete gets) together in the same program.
  6. Don't try to use scanf and getchar together in the same program.
  7. Try to limit yourself to only the format specifiers %d, %s, %f, and %lf, to read into variables of type int, string (see rule 3), float, and double, respectively.
  8. If you want to read a character into a variable of type char, you can use " %c", but the mysterious extra explicit space character there is vital.
  9. Use only one % sign in the format string, to read one variable. Don't try to read two or more variables in one scanf call.
  10. Always check scanf's return value. If it returns 0, or the negative value EOF, that means it didn't successfully read anything. If it returns 1, that means it successfully read one value. (And if you break rule 9, and try to read multiple values, it'll return the number of values it did successfully read, anywhere between 0 and the number you asked for.)
  11. If scanf returns 0 or EOF, indicating that the user did not type a valid value, just print an error message and exit. Don't try to write code that asks the user to try again, because the user's wrong input is still sitting on the input stream, and there's no good, simple way to get rid of it. (If you really want to write user-friendly code that re-prompts in case of error, scanf is not the right tool for the job.)
  12. Never put whitespace after the format string. That includes the newline character \n. (That is, use "%d", not "%d " or "%d\n".)
  13. Don't try to use the %[…] specifier.
  14. If you break rule 13 (perhaps because someone told you that %[…] might be a way to read a string containing spaces, or a whole line), do not put an s after it. The format is %[…], not %[…]s.

These rules may seem restrictive, but if you follow these rules, you should be able to simply and easily and reliably get simple inputs into your simple programs, which is the goal here. scanf is otherwise remarkably hard to use, and experience has shown that there are something like 17 different horribly frustrating problems that tend to come up, and trying to solve them is a completely unnecessary distraction from your goal of learning C by writing simple C programs.

When you're reading strings with %s or %[…], there's a danger: no matter how big the array or malloc'ed buffer you're reading into is, the user might type more than that. To avoid overflow, you can use a format like %19s to explicitly tell scanf how many characters it's allowed to read. (And remember to subtract 1, to leave room for the terminating '\0'. That is, you'd use %19s to read into char str[20]; or char *p = malloc(20);.)

These rules are somewhat numerous. For a simpler set of rules, see this answer. Putting rules 7, 8, 9, 12, and 13 together, there are really only five complete format strings you want to use with scanf: "%d", "%s", "%f", "%lf", or " %c". (But no commas, no fixed strings, no whitespace other than the explicit space in " %c", nothing else.) Exception: as mentioned above, it's fine to use something like %19s to limit the length of a string being read.

If you need to do something more complicated, that you can't do while staying within these rules, it's time to either:

  1. Learn enough about how scanf really works (and doesn't work) so that you can try to do the thing you want, but without getting stuck on one of those 17 problems, or
  2. Learn how to do input using better and more powerful methods than scanf, perhaps by reading whole lines using fgets and then parsing them.
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • #4 applies to `%[...` also. – chux - Reinstate Monica May 24 '22 at 11:29
  • 1. and 2. are over-simplifying to the point of being wrong. They are good guidelines, until you learn what these 3 functions do. After that, mixing them is no problem. – hyde Jun 26 '22 at 17:30
  • 2
    @hyde They are good guidelines because by the time you learn enough about these functions to be able to mix them, you've learned that `scanf` is more trouble than it's worth, and there'll be no issue with mixing because you won't be using `scanf` at all. – Steve Summit Jun 26 '22 at 18:15
  • 1
    @SteveSummit It'd be better to tell things how they are: `scanf` will always leave the remainder of the line unread, so if you use `fgets` after `scanf`, read and discard the remainder of the current input line first. – hyde Jun 26 '22 at 19:57
  • @hyde That's why option 1 at the end says you can "learn enough about how scanf works so that you can try to do the thing you want". But to my way of thinking, having to write code to read and discard the remainder of each line, every time you want to use `fgets` after `scanf`, completely demolishes the alleged ease and simplicity of calling `scanf` in the first place. – Steve Summit Jun 26 '22 at 22:50
  • The rules do seem quite restrictive, especially #7. But upon consideration, I agree that they serve their expressed purpose well for people who have not internalized all the gory details of how `scanf` works. Which includes substantially all the C newbies trying to use `scanf` to write simple programs. – John Bollinger Oct 05 '22 at 19:21
  • A very good summary, which I have upvoted and benchmarked so I can reference the whole shebang instead of repeating my "favorite" mantra (#8...) for basically every question that includes `scanf()`. Perhaps consider adding chux's advice about limiting the string size with `%s` to avoid overflows. May you reap the upvotes that you deserve! – DevSolar Oct 05 '22 at 20:54
  • @JohnBollinger: More than one conversion specifier gets ugly because then you're in the realm of "one whitespace matches *any kind and number* of whitespaces", "input string must match format string", and the fact that you will be unable to tell, even when checking the return value of `scanf()`, *where exactly the match failed*. I absolutely agree with Steve here, `scanf()` is a can of worms best left unopened. It's only in the standard to mirror `printf()`; IMHO you should only ever use `fscanf()` (sp!) on *known good* input, and eschew `scanf()` entirely. – DevSolar Oct 05 '22 at 21:06
  • Note for future readers: I have rewritten this answer, and renumbered some of the guidelines. chux's comment refers to what is now rule 2. hyde's comment about "over-simplifying to the point of being wrong" refers to what are now rules 5 and 6. John Bollinger's comment about "quite restrictive, especially" refers to what's now rule 9. – Steve Summit Oct 12 '22 at 16:32
  • 1
    @chux Notes about field widths added. – Steve Summit Oct 12 '22 at 16:33
  • @Steve Summit: is the space before "%c" really mysterious? Doesn't it simply skip any sequence of spaces? What if I want to read spaces too? – Giuseppe Guerrini Nov 03 '22 at 08:49
3

You're missing a "&" before "message[i][j]".

for (int i = 0; i < M; i++) {

    for (int j = 0; j < columns; j++) {

        scanf("%d", &message[i][j]);
    }
}