Dear C Users Journal:I have two comments to make concerning Bill Plauger's column in the September 1990 issue.
The implementation of assert shown on page 16 is:
void _Assert (<parameters>); #define assert(test) \ ((test) | | \ _Assert (<arguments>))A complete example is next to it in Listing 1.The idea is to use | | to take the place of an if statement. However, I did not understand the use of logical-OR with a void type as the second parameter. I looked it up in Standard C (by Plauger and Brodie), and it states that both arguments must be scalar. On page 29 of the same book, it clearly shows that void is not a scaler.
I'm wondering what | | could do in that situation. It returns an int anyway, not a void which is the correct type that the macro is supposed to expand into. If | | is supposed to evaluate the first argument, and then evaluate the second only if the first was non-zero but not return a result if the second argument is void, that is something I had never heard before.
It sounds useful though, so I checked the Standard. Alas, section 3.3.14 states "The result has type int."
I think what he meant was something more like:
#define assert(test) \ (void) ((test) | | \ _Assert (<arguments>), 0)The other point I want to make was that defining one's own assert should be more common than it is. For example, I use a text-based windowing system in my programs. Using an assert that wrote to standard output would not work. Instead, I wrote my own that knew how to output to the screen under that library.A programmer can also enhance assert to his own tastes and practices. For example, the built-in assert will call abort(), which had the undesirable side effect of not freeing up any EMS the program was using. So I would have to periodically reboot after a few abnormal terminations when I ran out of EMS. I wrote my own assert to call exit() instead, which I knew to be safe under the circumstances I was debugging under, and served me much better.
Likewise, my windowing system does not take well to an abort (). My own assert has a popup window which lets me select Exit, Abort, or Resume. I normally exit, but can still abort under dire conditions. And letting me resume allows me to assert suspicious conditions as well as outright errors, and to assert things that would be far too difficult to compose a complete test for but are OK if they have a false hit every once in a while. Or perhaps I know that this bug is just cosmetic, and I can go on with the next test instead of starting over.
To summarize, you would be well advised to use assert statements or something like it during testing and development. Crafting your own debugging aids in the same light can pay big dividends.
John M. Dlugosz
2629 Glenhaven
Plano, TX 75023Mr. Dlugosz is right, as is so often the case. John's magazine articles frequently clarify the darker aspects of computer programming. See my apology and corrections at the end of the "Standard C" column. pjp
Dear Mr. Ward,
Re: CUG disk #236 ("start-up disk") CFLOW.C
I have taken the trouble and analyzed the CFLOW.C program. I will share my findings with you:
1) The program fails to identify any function declarations which have whitespace (tabs or blanks) between function name and the left parenthesis.
2) The program fails to process (input) programs correctly, if their last line has no linefeed, i.e. if an EOF is reached at the end of this last program line.
3) The nesting of tests should strictly be:
If not within comment, are we perhaps within a double quote?
If not within a double quote, are we perhaps within a single quote?
The current logic is wrong.
4) The programs fails to handle cases which have three double quotes in a printf argument, one destined to be printed (preceded by a backslash).
5) The program fails to handle cases which have single quotes directly imbedded in printf arguments (or in string literals for that matter).
6) The program arbitrarily assumes that single quotes are three chars wide (the normal case, agreed). It therefore identifies two double quotes if it hits any four-character quotes (those with backslashes, backslash followed by two single quotes e.g.).
You will agree that in such a case it is better to reinvent the wheel rather than rely on poor code. I strongly suggest you withdraw CFLOW from the library.
Best regards,
L. Engbert, Engbert UB
Taunusstrasse 8, D-6384
Schmitten 1
West GermanyOuch. - pjp
CUG 329 UNIX Tools for PC includes a better cflow. -Kenji
Dear Howard,
I believe that R&D Publications needs to create an online presence in the programming world. There are several good reasons to go online, and with careful planning, costs can be limited. The reasons for going online include maintaining competitiveness, electronic mail for authors and readers, reader services, and product sales.
The computer magazine business is notoriously competitive. Readers vote with their subscriptions to the magazines which best meet their needs. Some of the largest technical computer magazines have online services. These online services, such as PC Magazine's PC Magnet and Byte's BIX, have helped establish them as being on the leading edge of technology. Nowadays, computer users expect more than just printed pages from a magazine and want to interact with it and through it.
Electronic mail for authors and readers provides the interactive dimension to online services. Readers can ask simple questions or pose difficult problems and together share solutions. For authors, an online connection means an easier method to submit stories, receive proofs, and correspond with readers. Computer Language routinely receives manuscripts through their online system. This can be faster and cheaper than overnight mail.
In addition to electronic mail, you can provide other reader services with an online system. First, many readers are anxious to try out source code printed in the magazine but are unwilling to spend the hours necessary to type it in correctly. Many of these people won't go through the trouble of mailing an order for a diskette and waiting around for it to be mailed back either. Online availability of source code from the magazines is what these readers want. I have been providing this service for readers of The C Gazette for the last 18 months. Last, an online service can provide another channel for sales. Interactive ordering forms can provide the reader with the means to purchase books, diskettes, and even subscription renewals. Of course, online services can also generate revenue themselves if a fee is charged for registration, extended access time, or file transfer privileges.
Thus far I have avoided mentioning the specific vehicle for "online services." There are many avenues available including starting your own forum on existing networks like CompuServe, Genie, Delphi, and Prodigy. The alternative route is bulletin board system (BBS). Advantages of the BBS include control of costs, ability to scale up the level of service, and no limits on file storage.
These have been my ideas on this subject and I thought I should share them with you.
Respectfully,
Victor R. Volkman
5145 Pontiac Trail
Ann Arbor, MI 48105Mr. Volkman, a regular contributor to CUJ, has a valid point. We are increasing the use of electronic mail at a rapid rate. I intend to depend heavily on it if I am to edit this magazine from Down Under for a year.
R&D Publications has a fledgling BBS as well. To what extent it will be used as Victor describes, or when, has yet to be determined. That, in the end, is for Robert Ward to decide. - pjp
Attention: Microsoft Product Support
Technician Mr. Dan Kirby
Dear Mr. Kirby,Thank you for your prompt (4 September) response to my problem concerning the IDE for Quick C 2.51 always opening in the /h (maximum lines) mode whether CGA, EGA, or VGA.
Based on your suggestions, I quickly tracked the problem to the file REMIND.COM which was called as the last item of my AUTOEXEC.BAT. This program was distributed by the late "Micro Cornucopia" in their General Utilities disk MS-36 and possibly has received wider distribution. The 13K COM program contains the words, "Copyright (C) 1984 Borland Inc ... display 80x25eP," perhaps indicating that it was written/compiled in an early Turbo Pascal. The DOC file gives the author as R. Schnapp.
Your "mode co80" also worked. I'll retain REMIND.COM, insure that MODE.COM is available, and add the appropriate mode co80 command as the last call to AUTOEXEC.BAT on the computer in which QC 2.51 is installed.
Microsoft should still be interested in why the problem occurs in Quick C 2.51 but does not occur in the MS BASIC 7.1 QBX environment and take corrective actions. Thank you again.
Very truly yours,
David W. Fischer
4609 Braeburn Drive
Fairfax, VA 22032-1830Dear Sirs:
I have a couple of comments on Rex Jaeschke's useful article, "Using the Quicksort Function" (CUJ, September 1990).
We must use if tests as in Listing 1 of that article not only for unsigned values but for any types whose difference cannot always be expressed in an int return value, such as long or float. (This is probably clear to most users, but it was not made explicit.)
For multi-key sorts, Mr. Jaeschke states that qsort sorts elements according to one key, and outlines a method for sorting on multiple keys, which he admits may not be the most efficient. Actually, qsort knows nothing about keys; what it does know about is only one compare function, but that function may incorporate comparisons for as many keys as desired. The usual method for handling multiple keys in the compare function is first to compare according to the primary key and, if unequal, return with the appropriate positive or negative value. If equal, however, then compare according to the secondary key, etc. That is, proceed on down the line of keys until the highest of them produces an unequal comparison. The function can then return a positive or negative value without examining lesser keys. Only if the last key also compares equal is there a zero return value for the compare function. With such a compare function, qsort sorts on multiple keys in one pass. Hope this helps.
Sincerely,
Stuart T. Baird
927 Parkview Drive F-301
King of Prussia, PA 19406
Mr. Baird is correct. - pjpDear Robert,
I have seen a bunch of articles about sorting lately, so the attached table might be of interest. What inspired me to send it is the oft-repeated comment "qsort is the fastest..." I got into this testing because I have an application that may do 100,000+ calls to a sort. The final word is you must test with actual data Shell's may be the best for some things, but if all the real data are almost sorted, an insertion sort is faster.
Note one bit of unfairness in the times: qsort() has the overhead of a function call for comparison where the rest had the comparison in-line. The test is realistic for me, since I don't have the qsort() source and thus was stuck with the call.
Table 1 shows the times for various sorting algorithms. qsort() is the standard SYS/V library function; Shell's sort is the implementation in K&R; Shell's sort II is the implementation in Algorithms, 2nd Ed., Sedgewick; insertion sort is per Sedgewick; heapsort is from Numerical Recipes in C. Count is the size of the array. Sorting performed on an array of integers:
same all elements in the array = 1
sorted elements are 0 to count 1 in numerical order
reversed elements are count 1 to 0 in reverse numerical order
random elements are generated by drand48()*count calls
Times are in seconds obtained from clock(3C); all testing on 3B2-700 running Sys V.3.2.1; programmed in C and compiled with:
cc -0 shell.c -lmallocSimply yours,David X. Callaway
1B49 Independence Rd.
Fort Collins, CO 80526Dear Editor:
Your September 1990 issue featured an article on CRC-16 calculation. The algorithm presented was a straight software emulation of the hardware solution to the problem of calculating CCITT compatible 16-bit Cyclic Redundancy Checks. Since software solutions are usually more efficient if they are byte-oriented rather than bit-oriented, as a hardware solution would be, I was asked by my employer to develop such a byte-wise algorithm for the same problem. By running a symbolic model of the bit manipulations involved in calculating the new CRC after one byte has been processed, I was able to develop a solution that does not involve a loop to handle each bit separately, but processes all eight bits in parallel. As expected, the algorithm performed much faster than the standard bit-oriented version. In my own informal timing tests, I replaced the bit-oriented routine in your example program by the byte-oriented routine I developed for Argilla Corporation and compiled both versions on Microsoft C 6.0 to 80286 code for MS-DOS. The result was a performance increase by 500 percent over the version you published. Listing 1 is the code required to calculate the inverted and byte swapped (for transmission) CRC of a message. For the test cases and examples, please see Bob Felice's article in the September 1990 issue of The C Users Journal.
Ammo Goettsch
Argilla Corporation
(and a student at USA)
Mobile, AlabamaDear Robert,
SUBJECT: qsort, September issue.
An easier way to do multi-key sorts given, e.g., a struct like this:
struct stuff { char str [20] int cnt; int subcnt; }To sort struct stuff * array[], use a comparison function, as in Listing 2. It seems a lot more straightforward than sorting on one key and then sorting sub-blocks of data by another key (and even better, it works).Also, to sort reversed, as on p. 30, use:
return(strcmp(p1, p2)) /* forward, or * / return(strcmp(p2, p1)) /* backward */In other words, just reverse the pointers.Simply yours,
David X Callaway
IB49 Independence Rd.
Fort Collins, CO 80526Dear Sirs,
I'd like to point out a problem with Arthur Held's article "Function Returns: How to Use Them" in your October issue. I wouldn't bother, except the article was obviously intended for beginners in C and contains a glaring syntactical error which might cause some confusion and frustration to new users.
Held writes the ternary operator to convert ch to upper case with the following syntax:
(if ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch);As many readers have informed you, I'm sure, the if is incorrect. The ternary operator does not require an if. The correct syntax is:
(ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch);Note the outer parentheses are unnecessary but harmless, and do serve to group the ternary operator together. Unfortunately the article contains the same error three times. Also, in Held's first example, the programmer will need to assign the result of this ternary operator to something in order to actually save the converted ch. The corrected syntax I gave above still doesn't do this. A beginner might be confused about that as the ternary operator is probably something quite unique from anything they've used before.On another more positive note, I'd like to provide a little input for beginners that might have read this article. I noticed Held used the printf function to display string constants when giving examples of prompts and error messages. Certainly this is a common practice and I'm not picking on Held. A hint: if you are going to output a string constant, it can be very beneficial to use the puts or fputs function. They deal explicitly with string constants and thus don't suffer the overhead of linking in all the baggage to print every kind of integer, character, and floating point like printf will. If printf is not needed anywhere in your code, avoiding it and using puts or fputs instead can greatly reduce the size of the executable.
I think your mix of beginner and advanced material is wonderful, and serves a great purpose in the C community. I consider myself an advanced C programmer, having worked with C exclusively in many major projects over the past eight years. Having received your publication over the last two or three years, I can honestly say I get something out of every issue. The C Users Journal is consistently the best publication I receive.
Thanks for the good work,
John T. Pearson
3405 Elmwood Ave.
Lubbock, TX 79407-4027Mr. Pearson is quite correct. The errors he cites are egregious. We can only hope that a less overworked editorial staff will catch more such gaffes before they see the light of day. - pjp
Dear Sir:
I am a current subscriber in your magazine and I am interested in finding a font editor for stroked fonts of Borland graphics library. Your help will be highly appreciated.
Sincerely,
John Kolias
74 Lelas Karagiani Str.
Athens 11361, GreeceAnybody? - pjp
Mr. Ward:
If some of my emotion leaks in your direction please forgive me because it is not my intent. I believe that you, as well as I, are simply the victims in this matter. The perp, as the cops like to say, is SoftC in Vol. 326. They have used you to market for them, at your expense, a very uncompetitive product. It is unknown at this time if the product even works or how many bugs it has, and it is economically unfeasible to find out. In brief, it is because their product costs $100 for the source code and that is for one user on one computer at a time per their license agreement in license.doc and manual.doc on Vol. 326. I have plans to produce and market a dBase access utility for $49.95. This means that I would be $50.05 in the hole for every copy I sold, using SoftC's library, per their license agreement. You're probably thinking, "Why don't I just use the runtime library, it's cheaper." The problem is that I don't want the whole load, just a couple of read functions and once I buy the source at $100, I'm restricted per the license from using any portion of it except on one computer at a time, thus $100/copy royalty on a $49.95 package. If SoftC was the only player it would be take it or leave it. But they're not. CodeBase 4 from Sequiter Software is available at full retail for $295 with source code and with no royalties and they have windows and menus on top of the dBase access routines.
So the bottom line is I got skunked for $8 and you're doing the dirty work for SoftC for free and our group (CUG) is being abused by a greedy and cheap marketing operation.
Tom Curren
Dear Mr. Ward,
Regarding your request for books covering the information engineering area:
There are literally hundreds of texts with titles like Information Systems In Business, or Management Information Systems. They cover some of the topics described in your material. I am not familiar with titles that would emphasize engineering. Three titles which have been used in our program (which doesn't mean they are any good) are:
Management Information Systems, K. Laudon and J. Laudon, Macmillan Publishing Company.
Management of Business Systems, R. McLeod, Macmillan Publishing Company.
Foundation of Business Systems, P. Flaatten, et. al., Dryden Publishing.
In addition, I am sure that any text salesperson would happily talk your ear off about offerings that fit your needs.
Tim Shaftel
Dear Sir or Madam:
Congratulations on an excellent magazine. I look forward to receiving each issue, which I read cover to cover. I have not been able to read the Volume 8, Number 6 edition because I did not receive it. I would greatly appreciate it if you would send it to me. Thank you very much and keep up the good work.
Sincerely,
Calvin Trainor
63 Pincarrow Rd.
Winnipeg, MB R3Y 1E6Dear Sir:
In testing Rex Jaeschke's assertion that there is no such thing as a negative constant in C ("Operators And The Precedence Table," The C Users Journal, August 1990) I find that the code generated by one compiler yields the size of an int when asked for sizeof(-32768) and that the code coughed out by a competing compiler returns the size of a long. With either compiler, constants more negative than -32768 are tagged as longs and those less negative but still less positive than +32768 are sized as ints.
This seems to mean that the compiler that is capable of storing -32768 in an int-sized object is indeed using a signed format to store the constant. Rex's comment therefore leads me to ask whether this behavior is just an unusual practice in C or whether it constitutes a violation of the ANSI Standard.
Although this side issue proved to be interesting, the exposition of the precedence table in the article was quite valuable to me, and I look forward to future columns by Doctor C.
Sincerely,
Maynard A. Wright
6930 Enright Drive
Citrus Heights, CA 95621Rex Responds:
Thanks for your kind words.
Assuming the compiler is running on a twos-complement 16-bit machine (like the Intel 80x86 series and not using a DOS extender), if it stores -32768 in an int it is defficient. Here's why. The grammar for an integer constant token in ANSI C does not include a sign. Therefore, the expression -32768 consists of two tokens, the unary minus operator and the constant 32768.
According to the Standard, (page 28, lines 37-41), "The type of an integer constant is the first of the corresponding list in which its value can be represented. Unsuffixed decimal: int, long int, unsigned long int; unsuffixed octal or hexadecimal; int, unsigned int, long int, unsigned long int; suffixed by the letter u or U: unsigned int, unsigned long int; suffixed by the letter l or L: long int, unsigned long int; suffixed by both the letters u or U and l or L: unsigned long int."
32768 is an unsuffixed decimal and it won't fit into an int so long is tried and that works; long is its type. The negation is applied to the result of that long int. If you look carefully, you will see that the rules are different for decimal and octal/hex. The following program demonstrates this.
#include <stdio.h> main() { printf("sizeof(-32768) = %lu\n", (unsigned long)sizeof(-32768)); printf("sizeof(0x8000) = %lu\n", (unsigned long)sizeof(0x8000)); printf("sizeof(0100000) = %lu\n", (unsigned long)sizeof(0100000)); }The values -32768, 0x8000, and 0100000 have exactly the same bit pattern when stored in 16 bits. However, the type of the first expression is long while that of the second and third is unsigned int. The correct output produced is:
sizeoff(-32768) = 4 sizeof(0x8000) = 2 sizeof(0100000) = 2I tried this program with six DOS compilers and one did perform as you reported. It was the Power C v1.2.1 compiler from MIX. I wonder if that's the one you found too.Incidently, the original K&R (page 180 section 2..4.1) allows you to come to the same conclusions provided you recognize the minus is not part of the constant token.
And in case anyone is wondering why I used the casts; the type of the result of sizeof, size_t, is implementation-defined and it can either be unsigned or unsigned long. Since I need to choose a print edit mask I chose the larger of the two and cast the expression just in case. If it was already unsigned long the cast is vacuous. If it wasn't, the correct code will be generated to make it work. It's a very subtle porting issue.