Letters to the editor may be sent via email to cujed@rdpub.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.
Dear Mr. Plauger:
I read with great interest the two articles on the goto statement of the language [CUJ, December 1995]. I agree whole heartedly that the goto is bad and can cause trouble for programmers. As a college professor who teaches beginning programming, let me put forth several observations.
First, the goto is used by those who really do not know the structured programming methods. As a former supervisor of a programming team for a large hospital in Dallas, I met with great resistance from the old timers when I insisted that the goto not be used on any new development. I did agree to allow the use of the goto in existing programs, since the cost of rewriting was not in the budget.
Second, the languages of the past, especially BASIC, have been a great hindrance to structured programming. Many of the students that come to college, who have a little knowledge of programming, have already started the bad habit of gotoing. In order to correct this problem, we need to have some summer institutes for the middle and high-school teachers so that they can learn the structured concepts. Industry can help by providing some grant money to create such classes for our teachers. Relearning is always more difficult than learning the first time, so lets get our next generation started off in the right direction.
Thirdly, getting rid of gotos can be done in many ways, and I was particularly glad that Mr. Becker pointed that out. One needs to really understand the process that is to be coded before trying to code it. One also needs to understand the inner workings of the language and how the various commands can be used to code different results. Without both of these understandings one creates bad code that frequently has bugs in it; and this type of code is a nightmare to maintain.
Sincerely,
William W. Ryan, Jr., Phd, PE Associate Professor of Physics and Computer Science
Harding University
Searcy, AR
wryan@harding.edu
Dear Mr. Plauger,
I was reading another slap at the lowly goto and considering the code used for the example on loop unrolling:
i = 0; while(i < 1000) { a += f(x); i++; }I would think the compiler should check the function f to determine if the value produced by f is influenced only by x, in other words f is a function in the mathmatical sense. If the compiler determined the above was true, then the loop could be optimized using induction variable analysis to the following:
i = 1000; a += i*f(x);I think the point of loop unrolling would be better made using the following code
i = 0; while (i < 1000) a += f(i++);Now loop must be executed 1,000 times regardless.
Chris Webster
Christopher_Webster@Baylor.edu
Dear Dr. Plauger:
Both kudos and brickbats for your articles on gotos in the December CUJ. Kudos to Pete Beckers article. Given the religious nature of the subject, his discussion and analysis was insightful and even-handed.
The article by Aaron Garth Enright and Linda M. Wilkens was quite another matter. Using pathological example code that no C programmer with more than a few days experience would ever write, their article proves nothing. Another indication of the sloppiness of the article was thefact that, despite the authors credentials, the example code used void main(), a definition which violates the ANSI/ISO standard and is itself the subject of much religious debate.
Even as a discussion of optimization techniques, it is horribly flawed. I first began running optimization benchmarks back in 1988 and the authors results prove nothing to me other than what I already knew about the optimizers of various vendors whether compiling goto loops or real C code. PC compiler vendors (with the one exception noted in the article) have always been in the forefront of optimizer technology. In my original 1988 tests, a PC-AT running code compiled with Datalight Optimum-C (the precursor to Symantec C++) produced code that was only one order of magnitude slower than the same code running as the only job on a Cray X-MP/48. Like the authors, I also got miserably slow runtimes out of the code running as the only job on a VAX 780.
As a final note, allow me to quote part of a message that appeared in the FidoNet C_Echo some years back:
As for satisfying Dijkstra, Im put to mind of the exchange mentioned by Donald Knuth in his paper Structured Programming with goto Statements. Knuth had been reworking the partitioning logic for quicksort, and reached a point where he had a version which was significantly faster, but used gotos. Not just any gotos, but ones that jumped into the body of a loop. He showed this to Dijkstra, warning him that this might be quite a shock. Dijkstras reply will likely be a shock to those who are dogmatic about gotos:
Your technique of storing the value of UP in the order counter is, of course, absolutely safe. I did not faint! I am in no sense afraid of a program constructed that way, but I cannot consider it beautiful; ...Of course, it needs to be understood that the dirty version was derived carefully from a more structured version by applying well defined transformations, rather than just being cobbled together in a haphazard fashion. A critical distinction.
A critical distinction indeed! Your magazine would be better served instructing your readers in such exceptions and transformations rather than simply printing articles which continue to promote mindless dogmatism using bogus examples.
Bob Stout
bobstout@neosoft.comDijkstras famous 1967 letter to CACM, goto Considered Harmful, is often misrepresented. He noted a strong correlation between buggy programs and those that make undisciplined use of gotos to express flow of control. Simply banning the use of goto nevertheless has a salutary effect it forces programmers to think more carefully about how to structure flow of control. One persons dogma can be anothers good hygiene. pjp
Mr. Plauger,
I look foward to each issue of your magazine, especially the Letters to the Editor column. Your knowledge of the C++ programming language exceeds that of anyone I am likely to meet in my lifetime. I have a problem that I cannot figure out, and I do not know where else to turn for help.
I have developed an I/O manipulator that has the following prototype:
omanip<strstream&> printx(const char* format, ...);This manipulator allows one to write statements such as the following:
cout << "Testing---" << printx ("c = %c", i = %X, s = %s", 'A', 255, "Hello World") << endl;The output is as expected:
Testing --- c = A, i = FF, s = Hello WorldEverything seems to work as expected, except when I try to combine more than one printx manipulator into the same output statement. For example:
cout << printx ("cout = %p", &cout) << "---" << printx("The value is %d", 100) ><< endl;My compiler (Borland C++) seems to generate the calls to printx in the incorrect order. That is, the output looks something like the following:
The value is 100 --- cout = 0xfe32c04I know that the << operator is just syntactic sugar for a call to ostream::operator<<(). I also know that the C++ compiler is free to evaluate the order of arguments to a function.
However, I was under the impression that the arguments to ostream::operator<<() were evaluated from left to right. That is why one can write statements such as follows :
cout << " cout = " << &cout << " --- " << "The value is" << 100 << endl;and expect the output to be in the correct order.
Could you please explain to me what is happening and can you suggest a workaround?
If not, could you please add the following to the Standard C++ library:
ostream& ostream::operator() (const char* format, ...); istream& istream::operator() (const char* format, ...);Then we could write really cool statements like:
cout << "Testing --- " << ("c = %c, i = %X, s = %s", 'A', 255, "Hello World") << endl; int value; cin >> ws >> ("%d", value);Diehard C programmers will be happy and still have all the benifits that the stream classes offer. Thanks for a great magazine, keep up the good work.
D. Visage
dvisage@interserv.comThe rules for evaluating expressions are inherited from C. The way operands group with operators is always unambiguous. The order of evaluation of the terms, between two sequence points, is unspecified. The order of evaluation of the operators is also unspecified, but it is often limited by compiler ignorance. A typical compiler doesnt know that operator<< will always return a reference to cout, so it evaluates the inserters left to right to determine the result of each operator<<() call in the expected order.
Everything works fine, as a rule, if the order of the generated output is determined by the order of operator<<() calls. You havent shown me enough code to prove that is true, so I worry about that ampersand in omanip<strstream&>. It suggests that you might be using a common scratch buffer for both of the manipulator temporaries. If the actual inserter dumps the strstream buffer and empties it, you can get the surprising behavior. It merely tells you that the compiler has chosen to generate both omanip<strstream&> objects before making any inserter calls, thus filling the buffer with the two contributions in an unexpected order. The compiler has every right to do so.
If its any consolation, I make this error about three times a year.
By the way, I dont think your really cool inserters would work as you would hope. But thats another essay. pjp
Editor,
Id like to see more articles/columns discussing resuable software, particularly that software which will really be reused by virtue of its standardization and imminent acceptance: the template library. I feel that having standard, portable templates addresses some fundamental issues concerning the reinvention of the proverbial wheel and software engineering in general. Having templates, in my opinion, will provide solid and dependable tools for the purpose of engineering software in the conventional sense.
The problem, most profound in software development, is simply that too few globally accepted standard tools and heuristics (rules) are available to address a wide range of problem domains; the science is there, of course, but the application of it (i.e., engineering) is still in its infancy. Commonly accepted standards are a true measure of any successful engineering discipline (have a go at the history books). Without globally accepted standards, for example, the computer in front of you would simply and literally be a pile of integrated circuits boards and wires, each component of which works just fine but collectively is a pile of useless junk.
For example, contrast the reuse enjoyed by operational amplifiers versus C++ stack packages. Both are workhorses in their respective disciplines, Electrical Engineering and Computer Science. No computer company worth a dime would build a custom op amp. They have a wide range of standard op amps to choose from, with varying operational characteristics. This off-the-shelf-ness is so taken for granted by most of us that to even think of building one is not only absurd but impractical in most cases. On the other hand, even though you can find dozens of implementations of stacks, Ill challenge anyone to trust one to work as advertised and even at that use it without any modification based solely on its specification. Thats the problem, the op amp invariably has a commonly accepted formal specification to accompany it whereas the stack has the potential to have a common formal specification but there is none usually.
So, more formal template specifications discussions, please!
Bryan McKinley
BMC Software, Inc.Im all for reusable libraries too, and I see that templates make many library facilities more widely useful. I am nevertheless leery of the commonest analogies between electronics and programming. The dynamics of the two disciplines have many differences. pjp
Dear Editor,
Generally, I find your magazine to be an excellent resource. However, I was disappointed by several problems in the November 1995 edition which I felt warranted this letter.
In Pete Beckers column (page 96) he discusses using rand() to generate random integers in a range. The main problem I have with his solution is that most implementations of rand() are decidedly non-random in the least significant bits. This includes the example implementation given in 7.10.2 of the C Standard on which many existing implementations are based.
His algorithm uses the least-significant bits whereas it would be greatly preferable to use the most-significant bits. This deficiency will not be revealed by his superficial analysis of the numbers generated. In fact even the sequence of numbers [1,2,3,4,5,6, 1,2,3,4,5,6, 1,...] will pass his distribution test perfectly!
Another problem is that if RAND_MAX >= MAX_LONG for a particular implementation the code has undefined behaviour.
Mr. Becker seems to have trouble with rand() since in a previous column he recommends (from memory) using srand(time(NULL)) which could cause undefined behaviour since the type of a time_t could be a long or even a double.
At the time I thought it a huge waste of space to spend the whole column answering a very simple question that could have been answered in a few lines. For example:
You will always get the same sequence of random numbers if you dont seed the random number using srand(), or if you always pass the same value to srand(). To obtain different random numbers each time the program is run you can use the current time to seed the random number generator at the start of your program. For example:
srand((unsigned int)fmod( fabs((double)time(NULL)) (double)UINT_MAX));In Bobby Schmidts column (page 101) he says that the code f(n++, n+1); has unspecified behaviour. In fact it is undefined since the object n is modified and accessed between two sequence points (i.e., the one occurring before the statement is executed and the one occurring when the function is called).
Earlier, on the same page, he points out that overflow of a (signed) int is undefined, which is correct. But it may have also been useful to point out that when overflow should not cause undefined behaviour then unsigned integers can be used.
I hope this has been of use, and thanks again for a fine magazine.
Andrew Phillips
P.S. Re a name for Dan Saks column: what about something along the line of Saks Full of Info?
It was well known during the standardization of C that the stated algorithm had its limitations. Nevertheless, we suggested it because it is good enough for a broad range of applications. Jerry Dwyer, an earlier contributor to these pages, has made a pretty thorough study of random-number generators. (See Quick and Portable Random Numbers, CUJ, June 1995). I suspect he works more at the level of sophistication you expect. Still, I dont see the need for all the extra protections you impose on the srand call. My reading of the C Standard says that, at least in the presence of all the necessary function prototypes, the simpler call should be adequately safe.
Youre right about the categorization of f(n++, n+1). Its my job to catch such errors, and I missed that one. As for the pun on Saks, I leave it to the owner of the surname to pass judgement on its suitability. pjp