Departments


We Have Mail


Dear Dr. Plauger,

The code in your reply to Dale Wharton's letter (CUJ October 1990, p. 130) did not work when I compiled it using Borland's C+ +. As am amateur programmer I hesitate to suggest errors, but there seem to be three problems (Listing 1) .

1. The expression (s-prt) in prt[4*(s-prt)] should be (s-esc) to give the offset of the desired character in esc[]. This is then multiplied by 4 to give the offset in prt[].

2. The multiplier 4 is only valid if each string in prt[] is 4 characters in length (including the terminating null). It is necessary to pad the characters BS, HT, LF, and CR with a space to achieve this.

3. The expression printf("[%s]", prt[4*(s-esc)]) caused an error, "abnormal program termination," possibly from memory overwriting. I think this could be because prt[4*(s-esc)] returns a single character and printf used with %s requires a pointer to a string. If prt+4*(s-esc) is substituted the program works.

I enjoy the C Users Journal, which I discovered by chance some years ago. The articles, particularly your "Standard C," deal with the finer points of the language not covered in any textbooks. The glimpse of the problems you deal with in the standards committee is most interesting.

Many thanks for an intersting journal.

Yours sincerely,

Leonard Carter
P.O.Box 7
Northbridge N.S.W. 2063
Australia

Thanks for the corrections. By the way, I see we're practically neighbors, at least for the year. I am in Bondi N.S.W. through the end of 1991. — pjp

Dear Rex,

I have a couple of comments regarding your and other columns in CUJ.

1. I assume that puzzles are aimed primarily toward the beginning C programmer and keep wondering why you don't use function prototypes. For example on the examples, you don't define main as returning void. Yet main ends without returning a value as it defaults to an int. Also, should you not declare main to have a void parameter list? I have noticed that this is common among articles in CUJ or DDJ, etc. I feel that increasing the warning level to the highest level and fixing such omissions with correct declarations and coerced values helps to decrease errors. It also forces the programmer to acknowledge those times when he/she assigns int values to a char, etc. I remember when I first began using prototypes that I made a lot of mistakes without really thinking about them. Responding to the compiler warnings made me realize those times when I declared an int function then didn't return a value upon exiting it. It only takes one "minor" error with the resultant days of debugging to realize the value of function prototypes.

To me, if the teacher doesn't do something then why should the student?

2. In example 3 would it have made more sense to first tell the student that C doesn't not allow you to equate two strings, a.k.a. if ( "fred" == "mary") as you did in the last paragraph rather than spend several paragraphs "blowing their minds" before dropping the simple answer on them. If this example was aimed at an accomplished programmer, then just pointing out the mistake would have solved it. But I doubt very seriously that novices would understand your explanation in the first place. If they don't understand that you can't equate two strings, they sure will not understand stuff like &"end"[0].

In my opinion, it's is better to just tell a beginner, "Don't do that," and let it go at that. As they learn and understand, it will be easier to teach exactly why.

The same basic thing happened in example 1, you never really came out and said, "Before you store a string of characters, then you must allocate storage space to hold the string with a char buff[4] statement. A pointer does not allocate storage space but simply contains the address of the string and not the string itself." This is the one common thread that seems to bind beginning programmers — they all try to declare a char pointer and then start storing the whole world in it.

I apologize if I appear to be raking you over the coals, I'm really not. I really enjoy your articles and look forward to them each month. I have been programming in C for five or six years in both the DOS and Xenix environment and still have problems with C, particularly with complex pointer assignments. That is another reason for using a compiler that does strong type checking, to be sure the compiler agrees that the declaration is the same as yours.

Sincerely,

P. Lyle Mariam, P.E.
Manager of Software Development
# 5 Oak Forest Ct.
St. Charles, MO 63303

Rex responds:

Re 1: While I'm sure novice C programmers could answer some of the puzzles, I consider many of them to be reasonably well advanced. In fact, I cover all five parts of this series in my advanced C seminars. If you knew the answers and the explanations to most puzzles, I suggest you are well above average based on my experience teaching introductory and advanced C for five years.

I agree whole-heartedly that function prototypes can and should be used. However, you are confusing a function prototype (which is an abbreviation for a new-style function declaration) with the new way of writing a function definition. Let me explain.

I don't define main as having a void return type since it does not. Standard C says that main has a return type of int. I rarely return an actual value from main, however. Standard C says that if you drop through the closing brace of main or have a return without a value, the value returned (and hence the program's exit status code) is undefined. Since I am not checking the exit status code outside the program that doesn't matter. If you want me to exit with a value of zero to make it cleaner, then my program does strange things on VAX/VMS where such a value does not represent success and the command-line processor produces a silly message. I could, instead, include <stdlib.h> and return EXIT_SUCCESS. However, this is unnecessary and merely clutters up the otherwise simple example. In short it serves as a distraction.

I don't like the fact that Standard C allows you to not return a value from a non-void function, but that's the way it's always been. I do, however, like compilers that produce a warning in such cases (as does Turbo C). Interestingly, such a compiler will still complain even if I use exit(EXIT_SUCCESS) from main because I still don't have a return statement with a specific value.

Yes I could write int main(void) {. . .}. However, Standard C does not require me to do so and it is not incorrect to omit the void. The simple fact that there are no formal argument names inside the parentheses tells the compiler this function expects no arguments. Explicitly saying there are none is redundant. If you extract prototypes from the source by hand, putting the void there will make your job easier. However, if you use an automatic prototype generator, such as is supplied with Microsoft and Watcom C, the compiler inserts the void for you.

In the case of a function prototype, I agree that int main(); and int main(void); are quite different. Argument list checking is lost in the former case. However, this is in a prototype, not a function definition.

So as a major supporter and teacher of Standard C, I defend my style as being quite reasonable and acceptable.

Re 2: You suggest I should have said, "C doesn't allow you to equate two strings." As far as I can see, one of the first things C programmers learn about is arrays and strings. They also learn that strings are nothing more than null-terminated arrays. Very soon after, they learn or discover that you cannot perform operations on arrays as a whole. As such, I saw no need to state the basics up-front.

Since the puzzle series was intended to be useful to readers having a broad range of experience, the solutions are quite detailed. If you don't know all the gory details, I provide them. If you do, you skip over them. — rj

Dear Mr. Ward:

Enclosed, please find my renewal. You have a good publication, and I look forward to receiving it each month.

I am prompted to write to you for two reasons.

First, I read your comment (January 1991 issue) to the subscriber who was not renewing due to lack of source code. I guess I too am perplexed as to his real complaint. Additionally, though, I have been frustrated by the inability to obtain "machinable" code. I would like to indicate my support for a BBS through which we could obtain both source code from the articles as well as the code from the library. I am currently in need of some code from the library which we order under separate cover. That will slow down the project at hand, as we will have to wait for the shipment. A BBS would be a true benefit. I truly hope that you can implement it in the very near future. I wish I could help with the logistics.

Finally, there is an issue of the journal I would like to obtain. You never seem to mention in your advertisements that back issues are available. Are they? If so, how can they be ordered and at what cost? I am after the issue with the first article by Tsvi Bar-David, "Building A Text Editor."

Thanks for a good publication and your dedication to the promotion of the C language.

Sincerely,

Gregory L. Filter
87 Lathrop
Battle Creek, MI 49017

I know a bulletin board would be helpful, but I also know from our experience and that of other specialized magazine publishers that bulletin boards require a relatively large investment in staff time. We aren't ready to make that committment just yet.

As for back issues, those that are still available are $7.50 each (domestic — see the C Users Order Form on page 80). Even if the issue is not available, we can photocopy the article in question. You should call first to check availability. We try to keep our unsold inventory at a minimum, so availability changes rapidly. — rlw

Dear Editor,

Dr. Timothy Prince laments, "Why do the gurus do this to us?" in his "Pennies in Long Double" article. He should have answered his own question. The floating-point gurus are trying to solve the problem of measurements. Money is not a measurement, it is a count, and integers are appropriate for counts. Measurements are always approximate with some margin of error. Counts are always exact.

If C had fixed point numbers, they would be an appropriate representation for U.S. money. In their absence, integer counts of pennies are a more appropriate representation. Dr. Prince uses the solution to a different problem on the basis of a superficial similarity of appearence in print, complains that it doesn't work right, and then hacks away until it looks good. He hasn't solved the problem, just buried it in extended precision where it hasn't bitten him recently. The inevitable errors have just been postponed.

What the gurus tell you is true. Your job is to find what it is true of.

Sincerely,

Jeffrey L. Taylor
P.O. Box 5000
Davis, CA 95616

That's certainly a valid viewpoint. I think Mr. Prince has a valid viewpoint too. — pjp

Dear Sirs:

We are working very extensively with QNX. The C Users Journal is very much MS-DOS/UNIX oriented. Could it be possible for you to introduce articles about QNX into your very fine journal?

Thanks for your consideration.

Yours truly,

Jorg Kempmann
IBK
Pirolstr. 12
D W-3150 Peine
Germany

Watch this space. — pjp

Dear Dr. Plauger,

The attached code (Listing 2) illustrates what I consider to be a rather undesirable new "feature" of versions 6.00 and 6.00A of the Microsoft C compiler. Namely, the comparison if(a+b == a) can give a different result from that of if((a+b) == a). There appears to be no such distinction in version 5.1 of Microsoft C or with any other C compiler I have tried.

Dwayne Phillips recently reviewed the book Numerical Recipes in C for your journal (December 1990, pp.103-105.) Numerical Recipes algorithms (e.g., their singular value decomposition) use if(a+b == a) to detect when b is a negligibly small addition to a. I believe these algorithms were intended to be highly portable across C implementations.

Is there some good reason why this feature is not a "bug"?

I would like you to either address this problem in your "Standard C" column or else refer my question to one of your regular columnists. Thank you.

Sincerely,

Robert L. Obenchain,
Research Scientist
Eli Lilly and Company
Lilly Corporate Center, MC730,
Drop 2233
Indianapolis, IN 46285
Tel: (317)276-3150 FAX: (317)276-2095

Looks like a bug to me. The expression must group the same with or without the parentheses, or behave "as if" it were grouped properly. — pjp

Dear Mr. Ward:

The review of Numerical Recipes in C by Dwayne Phillips (December 1990) damns an outstanding book with faint praise. While there is nothing specific in the review that I can take issue with, I was disappointed in the lack of enthusiasm for a book that is the best in its field of numerical textbooks.

The book's strength is that it gives you enough mathematical depth to understand the strengths and weaknesses of various numerical techniques, without losing you amidst the mathematical trees. The result is an eminently practial reference book for working programmers and scientists. This balance of depth and practicality is unique in the literature.

My group had bought a set of scientific libraries from IMSL for about $10,000 for a departmental license for routines that would run on both Sun SparcStations and Intel 368 machines. IMSL is the granddaddy of scientific libraries; it was developed on mainframes in FORTRAN and has been ported to hundreds of mainframe and minicomputer environments. I have been able to replace all the IMSL calls with routines developed from Numerical Recipes, at a cost of about $120 (book plus source disk). There are no licensing restrictions, and because I have the source, I don't worry about paying lots of money to move to a different environment in the future. Most important of all, I understand my routines much deeper than I understand IMSL's "black box" routines.

Now those who spend their lives studying numerical techniques may be able to point out the drawbacks of Numerical Recipes vs. IMSL. IMSL is undoubtedly superior in error detection and probably robustness and detection of stability problems. However I have used IMSL to benchmark Numerical Recipes, and on my set of problems, at least, the two methods have produced equivalent results to well within the accuracy of my data.

Finally, I would like to add that I was motivated to write by a letter from Mr. Farah (January 1991) asking for recommendations for a linear fitting routine. I have used the Numerical Recipes routines to produce a very flexible generalized least-squares fitting routine. Full use of Numerical Recipes requires a background approximately at the level of a bachelor's degree in mathematics. Hopefully anyone charged with producing scientific software will have such a background. If so, run, don't walk, to buy this book.

John Caron
1406 Sunshine Canyon Dr.
Boulder, CO 80302

See the next letter for an opposing view. — pjp

Dear Editor:

Regarding the book review of Numerical Recipes in C by Dwayne Phillips (CUJ, December 1990), I strongly disagree with the suggestion that including the source code rules out the use of the book as a college text. The reason is that the C code in the book is very poor and clumsy, being more representative of FORTRAN code than of well-written C. It would be very instructive to students if they were asked to improve the programming to see how much faster the routines can be when using the full power of C.

I agree that the book is very readable and the publisher did a marvelous job, and it is a useful collection. But the authors admit their distaste for C and go on to demonstrate a lack of understanding of C and of what makes computers work efficiently. They also demonstrate their inability to change or learn new tricks. Because C is different they balk at learning how to use it and, instead, demand it operate like FORTRAN. They suggest that it is not suitable for scientific programming and, to make that point, prove they just don't understand.

Well, I can demonstrate that, while C is different from FORTRAN, when properly used, it is much faster. The rich syntax permits programming either in a FORTRAN-like style or a more efficient style. By the way, I am not a computer scientist, but rather a mathematical physicist. My early programming, all in FORTRAN or BASIC, was not concerned with runtime efficiency. I maintain that C is just as suitable for scientific programming as is FORTRAN, but can be much faster. I have tested the basic FFT in the book against one properly coded in C to find that theirs is 40% slower. In the case of their sorting routines, the well-coded heapsort can be 35% faster and the quicksort more than double the speed of theirs. In addition, the quicksort can be written in a way such that a presorted array is processed faster than an unsorted array (rather than being a slowest case), and one need not designate a scratch stack with the chance that it may not be large enough. It is possible to use the application stack for scratch without using recursion (which would repeatedly and wastefully push the return address and frame pointer on the stack). Although it is not recursion, this latter innovation also cannot be done in FORTRAN at all.

The efficiency of their matrix routines can be greatly improved by proper use of the power of C. I am writing a book which I will probably call "Numerical Efficiency in C." In this book, I will not only demonstrate the items above, but will provide a new heretofor unpublished iterative matrix inverter. It converges much faster than previous iterative methods, and it is numerically more stable than any previously published inversion method. With good programming it should be faster than Gauss Jordan methods or SVD for large matrices. But for small matrices, is is slowed by some preliminary setup overhead. The crossover is probably about 6x6 or 8x8. But against poorly programmed methods, it may always be faster. Preliminary studies suggest a 12x12 complex double precision inversion that is as much as four times faster than other methods. Also, because of better stability, matrices that could not be inverted by other methods in single precision can be with this method.

Let me address some of the remarks about C in Recipes. They complain that one cannot just use x**2 to square x, and that even defining the macro SQR(x) ((x)*(x)) results is two computations of x=sin(y) to achieve (sin(y)) **2. They did not conceive of using

#define SQR(x) (((z=x))*(z))
where z is a "scratch" float variable. Here the extra set of parentheses is to ensure x is put in z first. Parentheses have the highest precedence. It should not be necessary, but one of the things C compilers have been negligent about is following precedence rules.

They complain that

subroutine(a,n,m)
float a[n][m];
is illegal. But one can achieve the same thing with

#define ma(i,j) (*(a + i*n + j))
subroutine (a,n,m)
float *a; int n,m ;
No, this is not clumsy. The amount of computation to get the matrix element is exactly the same as using FORTRAN a(j,i). I don't recommend its use inside of intensive loops because it's inefficient. Does that imply that FORTRAN array notation gives inefficient code? Yes!!! Let me terminate with such an example.

In Recipes they use a pointer to pointer device to avoid the multiplication in addressing two dimensional array elements. That's a useful device that can be done only in C, not in FORTRAN. Consider a simplistic matrix multiplier. Listing 3 is the C equivalent of what FORTRAN would do. An optimizer might remove i*coll to the outer loop. If it does, the inner loop would contain only one multiplication for index evaluation. To get the value from aray2, k*col2 must be added to j and the result added to pointer aray2. To get the value from arayl, two integers must be added to the pointer. To get the destination pointer, two integers must be added to aray3. But these last two integers are constant during the innermost loop, and therefore the pointer is constant. A super optimizer might notice that and take destination pointer out of the innermost loop. Then each product on the right would be accumulated into memory, at the location pointer. But that's still wasteful, as explained below.

Each product first resides in a floating point register of the processor. When it is moved to memory, that consumes time. That time is large for micro-computers and many minicomputers because the word size and format inside the FP processor is different from that in memory.

Listing 4 overcomes all the problems by straightforward good C programming. The individual products are accumulated in a FP register before being moved to memory.

Another small saving is obtained in the loop control. Comparing to zero is always more efficient than comparing two variables or comparing to nonzero.

Essentially the second part uses low level methods unavailable to other languages.

Sincerely,

Morton Rudin
P.O. Box 2715
Gardena, CA 90247

Thanks for the alternate view. — rlw

I agree with much of what Mr. Rudin says. Having cut my teeth on FORTRAN long before C was invented, I still carry a fondness for that language. But writing C as "FORTRAN with semicolons" can lead you to clunky and suboptimal code. It does no good to view C as an inadequate successor.

On the other hand, serious numerical programmers can point to areas where FORTRAN is truly superior to C. The C standards committee fixed the worst problems with floating-point arithmetic, but we failed to solve the problem of unrestricted pointer aliasing in C. Because FORTRAN is a less powerful language, it can be optimized more aggressively than C. Those are some of the areas being addressed by the Numerical C Extensions Group. — pjp

Dear Mr. Plauger,

Concerning your December 1990 edition, I am sad to tell you that M. Johnson's fine attempt to turn us to "Writing MS-DOS Device Drivers" in C happens to be non-fruitful because his Code has been shortened beyond understandability. I know that it is a very tricky thing to put such an elaborate treat into a newsletter, but by writing all his assembler code you just can't get the point of the device driver mechanics unless you have written one before, preferably in assembler language. Please remember that Mr. Duncan is not head to everybody's family and please provide the rest of the story to us plain ordinary folks out in the software prairie. I do confess that I got a glimpse at the meaning by parallel reading Dr. Dobbs farewell 1990 issue, but I am still not sure that I understood the truth in full. Anyway thanks for all your nice work so far and I wish you and all the R&D staff all the best for 1991.

Yours,

Klaus-Guenter Albrecht
Seerobenstrasse 27
D-6200 Wiesbaden 1
West-Germany

See the letter following. — pjp

Dear CUJ,

I'm unhappy. The article, "Writing MS-DOS Device Drivers," in the December 1990 CUJ was of great interest to me because I once tried to do just that. I didn't have much time, ran into lots of problems, and finally ended up doing the job another (much less elegant) way. Finding an article that showed how the job could be done, especially with MSC v4.0, promised to be an answer to my past prayers and a solution to future problems. But it wasn't.

The terrible problem of interfacing DOS to C at the device driver level has to be done in asm and the article mentions several .ASM and .INC files. But they weren't printed in the article. I ordered the code disk thinking that surely they would be on it. But they weren't.

Now then, where/how can I get the .ASM and .INC files? I really want to see them.

Best regards,

C. J. Robinson
5036 17th Ave. South
Minneapolis, MN 55417

I am the major driving force on this magazine toward keeping printed code to a minimum. If we occasionally cut too deep, I probably deserve the blame. You should always be able to get the full code text supplied by the author in machine-readable form, however. I don't know where the ball got dropped on this occasion.

I realize that asking for a code diskette may be less convenient to someone in Wiesbaden than having all the text shipped with the issue. Sometimes, it's the best compromise we can manage. — pjp

And I am the major driving force for making certain articles fit the needs of our readers and not some abstract "ideal." Never fear, on any magazine the publisher is a fairly potent advocate, and I advocate as much real-world code as necessary to make the information directly usable. — rlw

All interested parties can now obtain the full source code on the December code disk.— hth

Dear Mr. Plauger,

I appreciate the article "Pennies In Long Double" by Timothy Prince (CUJ, January 1991), which explores subtle points of rounding in floating point arithmetic. I would like to add a few notes.

Borland's latest compiler, Turbo C++, has a new option to eliminate the problem Dr. Prince noted with successive assignments, as shown by

double d = 0.99;
int i = (d *= 100.0);
Using the -ff- option causes the compiler to assign 99 to i (as desired) because it forces an intermediate cast to double before conversion to int, instead of direct conversion from the internal 80-bit temporary format. It should be noted that Standard C doesn't guarantee 99, however, because of possible differences in rounding and truncation during the evaluation of d.

Numbers can be rounded in Standard C by using floor(x + 0.5). This should be more robust than adding and subtracting 1.0/DBL_EPSILON, as suggested in the article.

Dr. Prince implies that there are compilers which would, during optimization, eliminate the cast resulting from an intermediate assignment to an unused variable, thus making

int i; double d; d = i = 3.14;
assign the value 3.14 to d when the unused assignment to i is removed. If any compiler were that bad, I would consider it seriously broken and want it fixed!

Thad Smith III
T3 Systems
2001 N. 75th St.
Boulder, CO 80301

The C Standard requires that a value be "stuffed through a knothole" any time it is assigned to a data object. That is true of the operator *= as well as =. It is true whether an optimizer merges separate expressions or eliminates "unused" ones. Sounds like Turbo C++ conforms only when you use the -ff- flag. — pjp

Dear Dr. Plauger,

I have just received my three subscriptions (September, December, and January). This is really strange! Normally, I receive the journal one month after its release. Now, I have two past subscriptions and a newly released one. Can you shed light to this? How long does it really take the journal to reach me?

Also, included in the January issue is the entry form for the Bad C Pun Contest. I'm surprised to read my mail "containing my Bad C Puns" to Robert in the September issue. What a coincidence! Well, I have decided to submit them formally. I hope I'm not yet late.

In an article "Paving the Migration Path" by Dan Saks, he discussed something about converting a program from C to C++. In my case, I still adhere to the rule, "If it ain't broke, don't fix it.".

Best Regards. By the way, what does "P.J." mean in your initials?

"C"incerely yours,

Victor Caballero
World Health Organization
UN Avenue. P.O. Box 293
Manila, Philippines

The initials stand for Phillip James. I don't dislike either name, I just don't use either one. My family relabeled me Bill practically at birth, for bizarre and almost inexplicable reasons. I decided in my teens to be P.J. Plauger professionally. My friends call me Bill or P.J. — pjp