I'm an older high school teacher who used to work as a systems programmer on large mainframes, primarily in COBOL on banking systems. I do have a BS Computer Science, but my "native" language isn't C -- back when I started, BASIC was where you started and the banking industry used COBOL. So, I have a few years of experience with C, but not much real practice, if you catch my drift. I don't do work with it.
I teach Algebra, Geometry and the like. Occasionally, when we can fill a class, I get to teach a little programming. I can't do anything about it, no matter how much I swear--my kids will work in C/C++ so that's what I use to teach. I teach programming, not languages.
It's been a couple of years since I last had a class for programming. This year something has happened that has me questioning my sanity.
I suddenly seem to have an issue with scanf. It doesn't seem to be working as I recall. So, I check the documentation, browse this net, read some articles and, by golly, I'm wrong. scanf won't read from the keyboard buffer until it finds the \n. It stops at the first whitespace. Darn. <sigh>
Ok. It's not the first time I've had to tell a class that I was wrong and it won't be the last. I'll get them around it and we'll move on.
But, like the wounded bank robber pleading to Dirty Harry, "I just gots to know..."
I clearly recall using scanf to read string input from the keyboard as a simple expedient for testing string handling routines. (They're great for providing something to DO with iteration loops, if you'll forgive the pun.)
I know it's wasteful of space and hazardous to the program's eventual health, but the actual solution isn't something I want to get into with them just yet. Remember, they're just getting used to iteration loops and data definitions. At this point, I don't want to introduce complicated input routines or, heaven forfend! sub-routine calls. And I don't want them to get used to immediately hunting for the magic routine someone else wrote.
I clearly recall that scanf("%s",a) where a is from char a[300]; or the like would swallow anything from the keyboard until it hit the 13 and then lay the 13 in your buffer just to be annoying. I even have (at work, naturally, so I can't produce it here at the moment) a web-site reference that claims the same thing. I distinctly recall writing FOR loops to fill that array with zeros to make life a little easier.
So, with all that horridly boring preface, what happened? Have I lost my mind? Or has something changed?
"Was it six? Or only five?"
Thanks, Dave
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
First, allow me to thank you for your lengthy and well-thought-out response. Frankly, this was/is rather embarrassing. No teacher enjoys being wrong quite so publicly. It happens, and the only thing to do is admit it, explain why it was wrong and try to make a lesson from that. I still don’t like it. On the other hand, some of the most important lessons I’ve taught have been about mistakes and why they happen.
All I really expected was:
a) Yes, you’re losing your mind or
b) (I rather hoped) Oh, yes, there was a standard change back in…
You’ve exceeded that modest expectation greatly and I’m grateful.
Would you be willing to engage in a conversation about teaching programming in general? I understand if you don’t have the time.
Thanks again. Perhaps I can return the favor sometime.
Dave
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
1) On most systems, console input (normally the default for stdin) is line oriented. This means that the underlying device driver does not inform the caller of the availability of data until a complete line has been entered. This (as you will probably know) is primarily an historical artefact from teh days of dumb terminals attached to mainframes or multi-user systems.
2) The upshot of point (1) is that any call that reads from stdin will not return until a <newline> character appears in the input buffer.
3) scanf() uses whitespace (space, tab, or newline) as a field delimiter. It has always been thus, it is documented as such, and it has never behaved as a 'line-input' function as you suggest. Read the documentation again.
4) So while due to point (2) scanf() will never return until the user presses <newline>, it does not necessarily consume the whole line. It will consume as many whitespace delimiited fields as it has format specifiers unless encounters a conversion error first (the return value tells how many fields were sucessfully converted).
5) When a scanf() call has not consumed all available input, that input remais buffered, and may be used by subsequent input routine calls. If the <newline> remains buffered, a subsequent input call will return without blocking on firther input.
However the problem may be better solved by avoiding it all together. You note that you are aware that the function is problematic, but you are teaching this stuff! It would be far better not to teach bad habits to start with when the 'solution' as you refer to it is no more (and probably less) complex than using scanf().
You could (but shouldn't) use gets() for line input:
char a[300] ;
gets( a ) ;
The reason you should not teach that is because it is not protected against buffer overrun from too much input. It has no idea of the length of teh buffer. Fixing that problem adds only marginal extra complexity, and I suggest you tell your students to simply accept it as the method of line input and simply gloss over the details.
char a[300] ;
fgets( a, sizeof(a), stdin ) ;
or where you have a pointer to a buffer and cannot therefore determine teh size directly:
define BUFFER_LEN 300
char a[BUFFER_LEN] ;
...
fgets( a, BUFFER_LEN, stdin ) ;
Now since you are presenting this course, it is common where you wish to 'hide' language or platform issues to provide a 'course library' of functions or classes that the students are to use to implement solutions to exercises. The advantage of this from your point of viwew is that it restricts the number of possible implementations to something that will be far easier to asses and award marks to. So for this I would suggest that you provide a course library function such as:
Then all your students have to do is write code thus:
include <courselib.h>
define BUFFER_LEN 300
char a[BUFFER_LEN] ;
...
getline( a, BUFFER_LEN, stdin ) ;
Now if your compiler supports it in C (or you use C++ which does in any case) you could for simplicity put the course functions in <courselib.h> and declare them inline. Otherwise you will have the added complexity of getting students to use multiple source files or link external libraries. This could be easily achieved without having to teach it as such by having predefined 'starter projects' with the relevant code already in place. In Dev-C++ you could even create a custom project template that links the course library by default.
>> [...]I don't want to introduce complicated input routines or, heaven forfend! sub-routine calls
Yikes! Subroutine calls (or functions in C) are a fundamental or programming. If you start as one normally does teaching this stuff, the forst thing you do is get them to print some text on teh screen using - guess what? A subroutune call! So they are already aware of such things very early on - there is no hiding it.
I would suggest that actually writing your own subroutine calls would come very early on in a course in any case. I would suggest for C the following sequence:
1) "Hello, world!". For no other reason other than to present teh concepts of a program, the process of compilation, and the main() entry point. The actual printf() call is entirely subsideray to all of that and should be glossed over until later, because printf() is a very complex and powerful function.
2) Variables, data types, initialisation, asignment.
3) Arithmetic operators.
4) How to use the debugger IMPORTANT!!!. Please teach this, it will make your life easier and theirs.
5) Strings
6) Introduction to the standard library (just that you need to complete course exercises: test output, user input, string manipulation). Make them aware that there is more available in the library - they can look it up if they are keen - but be prescriptive on what they should use for assignments - remember you have to mark that stuff!
7) Implementing functions.
8) Structures
9) File I/O
10) Separate compilation and linking, and writing larger systems.
11) Large(ish) assignment, involving functions, file I/O, user interaction, and multiple compilation units. This could be done as a class exercise, with individual students or teams implementing different parts to defined specifications. Then integrating the parts as a class exercise. This simulates how real development teams work, and involves design, planning, communication and teamwork, skills that can be far more important that knowing which function gets a line of input! ;-). As a professional developer and development team leadership, I would suggest that this is the most important lesson and dearly wish more courses were taught that way.
I reckon 1 to 4 could be covered in the first lesson. Because they are using the debugger they don't yet need to know about output functions - they can see the data directly in the program, and the debugger will make them more self reliant (and mor interested) during the rest of the course. Of course if you are planning on using Dev-C++, forget it - the debugger sucks (even when it works), and thy'll hate you. Real debuggers are fun and satisfying to use.
Strings aren't much fun without output, so 5 and 6 on the second lesson. With the third lesson doing string manipulation with the standard library.
So by lesson four you are teaching function implementation and able to relate it to what they have learned from the standard library stuff. This also helps you with breaking up assignments into managable tasks: write a function to do A, write a function to do B, write a main() routine that ises A and B to do C.
Structures usefully preceed file I/O. You can spend a few lessons of this stuff consolidating the earlier work at the same time.
You might choose to ommit 10, but doing so won't make great programmers, but that may not be your goal. If you don't do 10, doing 11 makes less sense, since 10 is the enabler of 11. Again this will cover sevaral lessons, but the group exercise is likely to keep them interested. Be watchful however for 'passengers' - people that put themselves in a team of people who work hard so they don't have to. Pick on that person to explain what the team are doing to make sure thay are involved. Makesure each team member has some responsibility. Some sort of final assessment will separate the 'men from the boys' and concentarte minds somewhat.
Clifford
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Windows XP, SP2
DevC++ ver 4.9.9.2
I'm an older high school teacher who used to work as a systems programmer on large mainframes, primarily in COBOL on banking systems. I do have a BS Computer Science, but my "native" language isn't C -- back when I started, BASIC was where you started and the banking industry used COBOL. So, I have a few years of experience with C, but not much real practice, if you catch my drift. I don't do work with it.
I teach Algebra, Geometry and the like. Occasionally, when we can fill a class, I get to teach a little programming. I can't do anything about it, no matter how much I swear--my kids will work in C/C++ so that's what I use to teach. I teach programming, not languages.
It's been a couple of years since I last had a class for programming. This year something has happened that has me questioning my sanity.
I suddenly seem to have an issue with scanf. It doesn't seem to be working as I recall. So, I check the documentation, browse this net, read some articles and, by golly, I'm wrong. scanf won't read from the keyboard buffer until it finds the \n. It stops at the first whitespace. Darn. <sigh>
Ok. It's not the first time I've had to tell a class that I was wrong and it won't be the last. I'll get them around it and we'll move on.
But, like the wounded bank robber pleading to Dirty Harry, "I just gots to know..."
I clearly recall using scanf to read string input from the keyboard as a simple expedient for testing string handling routines. (They're great for providing something to DO with iteration loops, if you'll forgive the pun.)
I know it's wasteful of space and hazardous to the program's eventual health, but the actual solution isn't something I want to get into with them just yet. Remember, they're just getting used to iteration loops and data definitions. At this point, I don't want to introduce complicated input routines or, heaven forfend! sub-routine calls. And I don't want them to get used to immediately hunting for the magic routine someone else wrote.
I clearly recall that scanf("%s",a) where a is from char a[300]; or the like would swallow anything from the keyboard until it hit the 13 and then lay the 13 in your buffer just to be annoying. I even have (at work, naturally, so I can't produce it here at the moment) a web-site reference that claims the same thing. I distinctly recall writing FOR loops to fill that array with zeros to make life a little easier.
So, with all that horridly boring preface, what happened? Have I lost my mind? Or has something changed?
"Was it six? Or only five?"
Thanks, Dave
First, allow me to thank you for your lengthy and well-thought-out response. Frankly, this was/is rather embarrassing. No teacher enjoys being wrong quite so publicly. It happens, and the only thing to do is admit it, explain why it was wrong and try to make a lesson from that. I still don’t like it. On the other hand, some of the most important lessons I’ve taught have been about mistakes and why they happen.
All I really expected was:
a) Yes, you’re losing your mind or
b) (I rather hoped) Oh, yes, there was a standard change back in…
You’ve exceeded that modest expectation greatly and I’m grateful.
Would you be willing to engage in a conversation about teaching programming in general? I understand if you don’t have the time.
Thanks again. Perhaps I can return the favor sometime.
Dave
A lot of prose, not much fact.
Things you should know about scanf() and stdin.
1) On most systems, console input (normally the default for stdin) is line oriented. This means that the underlying device driver does not inform the caller of the availability of data until a complete line has been entered. This (as you will probably know) is primarily an historical artefact from teh days of dumb terminals attached to mainframes or multi-user systems.
2) The upshot of point (1) is that any call that reads from stdin will not return until a <newline> character appears in the input buffer.
3) scanf() uses whitespace (space, tab, or newline) as a field delimiter. It has always been thus, it is documented as such, and it has never behaved as a 'line-input' function as you suggest. Read the documentation again.
4) So while due to point (2) scanf() will never return until the user presses <newline>, it does not necessarily consume the whole line. It will consume as many whitespace delimiited fields as it has format specifiers unless encounters a conversion error first (the return value tells how many fields were sucessfully converted).
5) When a scanf() call has not consumed all available input, that input remais buffered, and may be used by subsequent input routine calls. If the <newline> remains buffered, a subsequent input call will return without blocking on firther input.
However the problem may be better solved by avoiding it all together. You note that you are aware that the function is problematic, but you are teaching this stuff! It would be far better not to teach bad habits to start with when the 'solution' as you refer to it is no more (and probably less) complex than using scanf().
You could (but shouldn't) use gets() for line input:
char a[300] ;
gets( a ) ;
The reason you should not teach that is because it is not protected against buffer overrun from too much input. It has no idea of the length of teh buffer. Fixing that problem adds only marginal extra complexity, and I suggest you tell your students to simply accept it as the method of line input and simply gloss over the details.
char a[300] ;
fgets( a, sizeof(a), stdin ) ;
or where you have a pointer to a buffer and cannot therefore determine teh size directly:
define BUFFER_LEN 300
char a[BUFFER_LEN] ;
...
fgets( a, BUFFER_LEN, stdin ) ;
Now since you are presenting this course, it is common where you wish to 'hide' language or platform issues to provide a 'course library' of functions or classes that the students are to use to implement solutions to exercises. The advantage of this from your point of viwew is that it restricts the number of possible implementations to something that will be far easier to asses and award marks to. So for this I would suggest that you provide a course library function such as:
include <stdio.h>
char getline( char buffer, int max_length )
{
return fgets( buffer, length, stdin ) ;
}
Then all your students have to do is write code thus:
include <courselib.h>
define BUFFER_LEN 300
char a[BUFFER_LEN] ;
...
getline( a, BUFFER_LEN, stdin ) ;
Now if your compiler supports it in C (or you use C++ which does in any case) you could for simplicity put the course functions in <courselib.h> and declare them inline. Otherwise you will have the added complexity of getting students to use multiple source files or link external libraries. This could be easily achieved without having to teach it as such by having predefined 'starter projects' with the relevant code already in place. In Dev-C++ you could even create a custom project template that links the course library by default.
>> [...]I don't want to introduce complicated input routines or, heaven forfend! sub-routine calls
Yikes! Subroutine calls (or functions in C) are a fundamental or programming. If you start as one normally does teaching this stuff, the forst thing you do is get them to print some text on teh screen using - guess what? A subroutune call! So they are already aware of such things very early on - there is no hiding it.
I would suggest that actually writing your own subroutine calls would come very early on in a course in any case. I would suggest for C the following sequence:
1) "Hello, world!". For no other reason other than to present teh concepts of a program, the process of compilation, and the main() entry point. The actual printf() call is entirely subsideray to all of that and should be glossed over until later, because printf() is a very complex and powerful function.
2) Variables, data types, initialisation, asignment.
3) Arithmetic operators.
4) How to use the debugger IMPORTANT!!!. Please teach this, it will make your life easier and theirs.
5) Strings
6) Introduction to the standard library (just that you need to complete course exercises: test output, user input, string manipulation). Make them aware that there is more available in the library - they can look it up if they are keen - but be prescriptive on what they should use for assignments - remember you have to mark that stuff!
7) Implementing functions.
8) Structures
9) File I/O
10) Separate compilation and linking, and writing larger systems.
11) Large(ish) assignment, involving functions, file I/O, user interaction, and multiple compilation units. This could be done as a class exercise, with individual students or teams implementing different parts to defined specifications. Then integrating the parts as a class exercise. This simulates how real development teams work, and involves design, planning, communication and teamwork, skills that can be far more important that knowing which function gets a line of input! ;-). As a professional developer and development team leadership, I would suggest that this is the most important lesson and dearly wish more courses were taught that way.
I reckon 1 to 4 could be covered in the first lesson. Because they are using the debugger they don't yet need to know about output functions - they can see the data directly in the program, and the debugger will make them more self reliant (and mor interested) during the rest of the course. Of course if you are planning on using Dev-C++, forget it - the debugger sucks (even when it works), and thy'll hate you. Real debuggers are fun and satisfying to use.
Strings aren't much fun without output, so 5 and 6 on the second lesson. With the third lesson doing string manipulation with the standard library.
So by lesson four you are teaching function implementation and able to relate it to what they have learned from the standard library stuff. This also helps you with breaking up assignments into managable tasks: write a function to do A, write a function to do B, write a main() routine that ises A and B to do C.
Structures usefully preceed file I/O. You can spend a few lessons of this stuff consolidating the earlier work at the same time.
You might choose to ommit 10, but doing so won't make great programmers, but that may not be your goal. If you don't do 10, doing 11 makes less sense, since 10 is the enabler of 11. Again this will cover sevaral lessons, but the group exercise is likely to keep them interested. Be watchful however for 'passengers' - people that put themselves in a team of people who work hard so they don't have to. Pick on that person to explain what the team are doing to make sure thay are involved. Makesure each team member has some responsibility. Some sort of final assessment will separate the 'men from the boys' and concentarte minds somewhat.
Clifford
... I missed out:
4.5) Conditional and iterative constructs. (if,else,do,while,switch).