24
$\begingroup$

Most of the books I read give examples using printf and scanf. At some point the students know perfectly how to use these two functions but they don't know about stdin, stdout and argv.

To me and according to many programming principles (e.g. KISS) a program should not interrupt the execution for prompting the user. Instead, and this is a much clever approach, the developer should learn to use the power of pipes and use the options and the arguments.

I think this:

$ whatdaywas 1982 02 16
Monday

Is much better than:

$ whatdaywas
Enter a year: 1982
Enter a month: 2
Enter a day: 16
It was a Monday. 

Is there any rationale behind this pedagogical approach?

$\endgroup$
5
  • 16
    $\begingroup$ This has nothing to do with KISS and is simply part of the Unix philosophy. Sane modern command line environments make it very easy to support both - use the argument if provided and ask the user for it if a mandatory parameter isn't given. $\endgroup$
    – Voo
    Commented Jul 19, 2019 at 9:11
  • 2
    $\begingroup$ (not a pedagogical answer) The 2nd program is easier to use by a naive user. $\endgroup$ Commented Jul 20, 2019 at 15:24
  • $\begingroup$ Are you teaching C as their first programming language? $\endgroup$
    – Bergi
    Commented Jul 21, 2019 at 16:54
  • 2
    $\begingroup$ Writing interactive programs (where user input depends on the output) doesn't work with command line arguments. $\endgroup$
    – Bergi
    Commented Jul 21, 2019 at 16:55
  • $\begingroup$ @Bergi Yes, first year at an engineer school $\endgroup$
    – nowox
    Commented Jul 21, 2019 at 16:56

7 Answers 7

25
$\begingroup$

I think that the rationale is so you don't have to introduce arrays and array notation on day 1. Typically, arrays enter into the picture at roughly the same time as loops, which would take place a few weeks or months later, depending on the pacing of your course.

That said, if you want to start with command line programs, it's not a huge problem to do so. At the very, very beginning of programming, there are always a few magical incantations that students must type, but won't fully explore until later. args[0] could easily be such an incantation.

As a side benefit to this approach, your students may have an easier time when they come to arrays, since they've already used them a bit.

$\endgroup$
7
  • 7
    $\begingroup$ I like the incantation term you use. $\endgroup$
    – nowox
    Commented Jul 18, 2019 at 17:21
  • $\begingroup$ Can't you still talk about stdin/stdout/stderr without using the argv array? I believe that scanf still just reads from stdin, so you can easily store stdin to a variable. $\endgroup$
    – thesecretmaster
    Commented Jul 18, 2019 at 20:24
  • 1
    $\begingroup$ @thesecretmaster: That's what Ben is saying. The OP is asking why you don't teach about argv and Ben's answer is argv is an array so it's better to focus on stdin first $\endgroup$
    – slebetman
    Commented Jul 19, 2019 at 4:45
  • $\begingroup$ I'ts because we didn't have it in Basic. $\endgroup$ Commented Jul 19, 2019 at 13:49
  • 2
    $\begingroup$ You'd also have to explain the failure modes with missing arguments. $\endgroup$ Commented Jul 19, 2019 at 14:47
17
$\begingroup$

Because everything around C language education is just utterly awful. That's really all there is to it. scanf should not be taught, but it is. It's rarely useful, and you can discover it on your own if you need it. gets should not be taught, but it is. It's not even part of the language any more for the past eight years. I say this as one of the folks (through my work on musl libc) seriously working on the future of the C language. Education around C is utterly backwards and needs drastic rethinking.

No actual software people use operates in terms of prompts, much less ones that don't have any sort of line editing or navigation forward/back in the process. (OK, some masochists use make config still, but...) Input to real-world programs arrives via command lines (possibly files specified on command lines or provided as stdin), GUIs, sensors, input over some sort of network protocol, etc. The easiest first case to teach is the command line, and your students are going to have to learn a little bit about it anyway to invoke the compiler if they don't already know it. By starting to teach input from the command line, you show students how programs they're already familiar with are doing something they do, rather than giving them an example that seems forced/unnatural/foreign.

Others have commented that using the command line requires getting into arrays early, but you can paper over that until students are ready. I find processing input from stdin to be a good place to start too, with the caveat that you use input redirection. Behaviors like terminal line buffering/canonical mode are not at all intuitive to students, and detract from teaching the language. If you instead have them process input redirected from text (or binary) files, you can avoid that. Just don't use scanf. It's awkward, unintuitive, and often unsafe. Start with getc/putc, then fgets when you're ready to do some parsing.

$\endgroup$
4
  • $\begingroup$ Comments are not for extended discussion; this conversation has been moved to chat. $\endgroup$
    – thesecretmaster
    Commented Jul 20, 2019 at 12:31
  • 2
    $\begingroup$ Now that this question has pointed it out, the same thing is obviously true in assembly language. So many Q&As about reading string inputs into buffers and so much code clutter from printing prompts first. (Although we'd still get lots of asm questions about converting strings to integers, especially multi-digit strings.) $\endgroup$ Commented Jul 21, 2019 at 5:00
  • 1
    $\begingroup$ @PeterCordes: Absolutely. And it's even worse if you don't have subroutines yet, and rather misleading if you use bios/syscalls in place of subroutines. I'd hesitate to each asm as a standalone language at all, preferring to teach it first with "call from C" being the way it's invoked. $\endgroup$ Commented Jul 21, 2019 at 6:06
  • $\begingroup$ The MARS MIPS simulator keeps things somewhat clean by providing "syscall" implementations of read integer from user-input, and of print decimal, print hex, etc. It's very much a toy system. I think it makes command-line args accessible to modify between runs somehow, though. For x86 Linux running natively, not in a toy simulator, probably teaching calling conventions as part of the boilerplate for running asm by calling from C would work. But it does introduce more moving parts. I can certainly see why courses don't do that, at least if they try to do asm before student's really grasp C. $\endgroup$ Commented Jul 21, 2019 at 6:46
14
$\begingroup$

Because, traditionally, programs iterate over data. See JSP.

Admittedly, prompting the user for data is weird, however we're talking about beginners here, having them try to remember what the different fields of data they have to enter isn't the point of the exercise, and is likely to make them view interacting with computers as needlessly complicated. As a bonus, prompting for the values helps make the beginner programs self documenting - the text of the prompt is an explanation of the variable that is to be read - so this will hopefully help our novice programmers understand the simple program without introducing the complex topic of comments.

(I was taught it wasn't legitimate in early versions of pascal to output before input, so you couldn't prompt for input.)

$\endgroup$
4
  • 8
    $\begingroup$ Do you have a citation for your Pascal factoid? I don't remember that one. $\endgroup$
    – Buffy
    Commented Jul 19, 2019 at 0:17
  • $\begingroup$ good question @Buffy. It was something I was taught in '86, as having been the case but no longer was, but I took that as truth without asking for proof. $\endgroup$
    – Grump
    Commented Jul 19, 2019 at 23:07
  • $\begingroup$ Wasn't it something with some implementations, like: the first reads on input return the command line, unless a write is issued on output ? $\endgroup$ Commented Jul 20, 2019 at 17:26
  • 1
    $\begingroup$ Source of a Standard Pascal compiler here : sourceforge.net/projects/pascalp5 $\endgroup$ Commented Jul 20, 2019 at 17:48
12
$\begingroup$

From my own experience tutoring young CS students, user input is actually a tricky concept. If it were not for user input, the results of any program could be pre-compiled into nothing but its output.

You get one lesson from getting all the input right at the beginning, with argc/argv, but from that point on, its just the computer churning. You get a different lesson from getting input during the execution of the program.

I have found that the idea of getting input from the user is surprisingly non-intuitive, given that nearly every program we interact with on a daily basis does it. This suggests that introducing it early helps prune misconceptions about how computers work before they become a real problem.

Obviously not every student is the same, but a particular pattern I found occurs more than I'd like is that they can understand what a program should do when it executes but not how that actually happens. They can look at

BOOL x = TRUE;
if (x)
    printf("Hello\n");
else
    printf("World\n");

And they can intuit that the program should print "Hello", but they can't intuit how a computer can possibly do so. As best as I can tell they figure the compiler does the "magic," and figures out which printf to use (we'll ignore optimizers for a moment).

However, when the values come from input, you can no longer think about the computer as a magic device which reaches the final result immediately. You have to see that it is following steps. We know this, but it seems many students don't grasp this at first.

If you have inputs coming from argv, its easier to keep this illusion going. If you have input during the program, after the program has already done something, you have to realize what's going on quicker. In particular, it gets you closer to the point of realizing that the input the computer asks for may vary based on previous inputs (press 1 to calculate the area of a circle, press 2 to calculate the area of a rectangle. Depending on the next scanf/gets, the computer will ask for one additional number or two)

$\endgroup$
4
  • 1
    $\begingroup$ Could you elaborate on the unintuitive bit? I'm always on the lookout for new cognitive traps that I haven't noticed before. $\endgroup$
    – Ben I.
    Commented Jul 19, 2019 at 19:01
  • 3
    $\begingroup$ @BenI. I've found that some students learn the concept of functions before they grasp the idea of execution. They can wade their way through the code and make sense of it in their head, but they can't see that the computer wades through it also, in real time. I find the same people tend to assume that if they compile if(x) f(); else g();, that the compiler figures out which of those two branches will be taken, and only captures the instructions for that branch. They get bothered if I construct examples where it's hard to know which branch will be taken (such as when there is input) $\endgroup$
    – Cort Ammon
    Commented Jul 19, 2019 at 20:01
  • 4
    $\begingroup$ Fascinating! Could you add that into your answer? It would make the answer more valuable. $\endgroup$
    – Ben I.
    Commented Jul 19, 2019 at 21:19
  • $\begingroup$ As a teacher: mind blown. If your anecdote is correct, there must be some subset of students who don't grasp the concept that the code for both branches are in the executable, but one of them gets skipped over at runtime. Very interesting! $\endgroup$ Commented Sep 17, 2019 at 20:07
6
$\begingroup$

Ultimately they need to understand both ways. Which you do first seems to be a matter of preference - or maybe just following the textbook.

They need to understand at some point that 'main' in C is an interface with the OS. But, since you can't teach everything at once, you have do pick the order of instruction. But teaching command line params later can give you a way to talk about "elegance" in program design.

Of course, the program written to accept args needs to behave properly if none (or too few) are presented. Often this defaults to prompting for data in simple programs.

$\endgroup$
3
  • $\begingroup$ You make an interesting point about validating input. But I don't think falling back to prompting makes any sense for a simple toy program. if(argc < 4){ puts(USAGE); return 1;} near the top of main. Maybe with a specific message like "too few args" as well as the usage string. $\endgroup$ Commented Jul 21, 2019 at 5:07
  • $\begingroup$ When it comes to parsing each arg in turn, handling errors is maybe more problematic (like a string where there should be an integer). Prompting 1 at a time with scanf gives a different debugging experience. One bad input can block the rest of the inputs if you don't do error checking and remove the bad token from stdin. But a failed atoi() or strtoul just produces zero. There's something interesting to be said there about the difference in how you validate input and report errors, and the debugging experience for total beginners. $\endgroup$ Commented Jul 21, 2019 at 5:12
  • $\begingroup$ atoi is probably easier: a debugger can find that it's returning zero, and/or that you're calling it on the wrong string. But a debugger generally can't observe the stdin I/O buffer to find out why some combo of input + format string is making all the later scanf calls fail. (Same applies to debug-printf statements for people that haven't yet learned to use a debugger.) $\endgroup$ Commented Jul 21, 2019 at 5:15
3
$\begingroup$

This answer comes from experience teaching C to students that have about 2 years of programming experience in Python and no exposure to low-level programming. I teach an Operating Systems-like course and students take introductory C lessons before the main course.


For C begginers strings are hard. Although the concept seems rather easy, students struggle with the differences between '0' and the number 0 and with the whole "we represent characters as numbers" thing. So, for many students coding atoi is a challenge at first. Expressions such as such as str[i] - '0' are not obvious at all and things like comparing characters make no sense (str[i] >= '0' && str[i] <= '9'). Even 'a' and 'A' being different sometimes confuse students.

A second point is that argv is a pointer to char arrays, resulting in the type char *argv[]. This is not ok until students are comfortable with pointers and even then a pointer to an array takes some thinking for many students. Drawing the pointers helps a lot here.

Taking these two points into account, we use scanf/printf because it allows us to have students accepting user input and showing program results right from day one. We also teach fgets and input/output redirection as early as possible (so at least students understand that interacting with programs in the terminal is not the norm). The main point is that C has strange syntax and inner workings for students that have only used Python, so taking it slow helps. Also, they do not know the use of argc/argv yet, so we would have to teach it as well.

Later in the semester, when they are more prepared, we present argc/argv as a way of receiving arguments from another program. Students by then are able to quickly understand how to use it and can write well-behaved programs.

So, yeah.... teaching scanf/printf without ever talking about proper command line programs is not a very good approach. I would even say more: teaching C without a connection to how C is actually used is not a very good approach. The connection might be weak at first, but it must be made strong at some point. Otherwise, why would we be teaching such strange language if the programs students code are much more easily done with other languages?

$\endgroup$
0
$\begingroup$

Don't teach scanf. It is dangerous and it is a huge security leak. Use getline instead and limit input size. Then you can use functions in stdlib.h to parse your string into the desired type.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.