Dr.Plauger:I am writing for two reasons, the first is a long overdue response to the letter from James R Lane (CUJ, February 1994). He wrote, "The code (sortargs) from the October 1993 issue would not work with Borland C++. (sortargs is from Chuck Allison's "Code Capsules: Pointers, Part 3.") The problem, as you pointed out, was that Mr. Allison was speaking UNIX, not DOS. This is because the UNIX shell expands wildcards and invokes the command/program with the complete argument list.
However, a better answer to his problem is to simply link (Borland's object file) wildargs.obj with the program. I believe that Borland C compilers have always included this object file for the very purpose of avoiding UNIX/DOS shell incompatibility. Also, on p.90 of CUJ, February 1992, there is an article by Toby Popenfoose which addresses similar problems for those who use Microsoft or Amiga C.
The second reason I am writing (and why I have been searching through old CUJs) has to do with the use of the void typecast. A college student recently asked me for help tracking down a bug in a C program. I found the bug, but was surprised when he showed me his C program. The program contained several lines of code with functions, all prefixed with (void)D. The following is typical of what I saw:
(void) printf("I can count\n"); (void) printf("1,2,3...");I asked why he had put the (void) before each printf and said that I did not think this was standard. (I have been programing in C for 8 years, and a CUJ subscriber for four years, yet I have never encountered this. I thought I remembered reading something about this in CUJ, but could not find anything which specifically addressed this, nor did I notice any published C code which used or promoted it.) He could not explain why it was used, other than to say it was "good form" and to hint at the possibility that it might help the compiler realize that the int returned by printf would not be used.I believe void is a type specifier which indicates the absence of any values. By type casting printf, it does not change the fact that printf returns a value. It only changes the return value to type void, which is then disregarded. C compilers could disregard a return value without any problems long before type casting was available. It is hard for me to imagine that type casts were intended to be used in the above nature.
I seems to me that the student might just as well use (float) printf(...). The compiler then could first convert the integer into a floating-point and then disregard it. I can see the importance of type casting when there is a left hand side, but I believe this practice is pointless. Moreover, keeping C code simple and clear is what I consider "good form" and this practice seems to promote only some sort of redundancy (for lack of a better word) Please comment on this, perhaps you can lend some insight into this practice.
Sincerely,
Gene Norris
Athens, OhioYou're correct that a void cast is never needed in Standard C. Adding such casts was encouraged by an early dialect of lint, as a way of promising that you knew the function returned a value and really meant to ignore it. So widespread was this convention for a time that even some people on the C standards committee thought that void casts were meaningful in C proper. pjp
Mr. Plauger,
Perhaps you could help me. I am familiar with the C/C++ Users Journal and have been a subscriber, as well as an advertiser for awhile. However, I find that most of the respondents to my products (C subroutine libraries in math, stat and graphics) are PC users. I am searching for the C programmer that works on a UNIX workstation (Sun, SGI, HP, IBM). Can you suggest from your experience, where I might reach these people? Do you know of any tech journals they read?
I sincerely appreciate your assistance.
Thanks,
laurie potratz
lpotratz@houston.vni.comAt the risk of being self serving, I have to observe that the C/C++ Users Journal has a significant readership (40%) among UNIX users who are C programmers, and I don't know of a popular magazine that does better in reaching this audience. The problem with UNIX-specific publications is that they deal with lots of other issues besides C programming. pjp
P.J. Plauger
I just read your Editor's Forum in the May 1994 CUJ, and had a question for you. I know that not being a lawyer, you can't profess to give me an authoritative answer; but you seem to have quite a bit of experience in the field.
If a programmer writes some simple functions for Company A, say to draw circles on output devices with differing aspect ratios, and then goes to work for Company B, and rewrites the same functions for them from memory, they're going to be very similar. I assume that this would not be a violation of copyright, as the programmer did not copy the actual code. I also assume that Company A could conceivably have patented the algorithm, and that this might be an infringement of that patent.
Can you speak to this? It seems to me that as we programmers develop our own writing styles and techniques, and work for different organizations, many of us will almost inevitably fall into this situation.
Keith Neufeld
Prairie View, Inc.
1901 E. 1st
Newton, KS 67117
(316) 284-6375
neufeld%ediger @ bethelks.edu
neufeld@bethelks.edu
neufeld@acm.orgCopyright protects the expression of an idea. If you replicate the earlier expression, even if only approximately and from memory, you risk infringement. On the other hand, copyright does not cover the idea itself. Your obligation as a professional is to learn to distinguish expression from idea. You can almost always find a new and better way to express an idea, if circumstances dictate that you reimplement code you once wrote (or even just read).
In practice, software infringement cases arise from rather direct piracy and rather direct competition. My experience is that even a minor amount of prudence goes a long way in this area. That was one of the points of my editorial.
Patents are another kettle of fish. They indeed protect the idea itself You can "reinvent" a clever technique and still infringe a patent. That's why I feel that patents have little or no place in programming it's too hard for even the most conscientious programmer to steer clear of trouble.
Good style can be your friend. If you can argue that, given your style of programming, you always code certain sequences a certain way, then you're less likely to be guilty of infringement. (If there's only one way to say something then there's less unique expression to protect.)
Mostly, follow your conscience. If you feel that you're acting honorably, you probably are. And other people will see you the same way. pjp
Dear Mr. Plauger,
Having followed railroad track diagrams til I'm dizzy, I'm looking for some help with a declaration. I figure you or Brian Kernighan would be the best people to ask, and you were kind enough to publish your e-mail address.
I am trying to declare a set of functions, each implementing a state of a state-machine, which return a pointer to the function implementing the next state. I thought it was a good idea, until I tried to write the declarations.
I want to say:
"x is a function (accepting two arguments, each having a type) returning a pointer to a function, which will accept two arguments, each having a type, and will itself return a pointer to a function..."
The best I've been able to come up with is:
"x is a function (accepting two arguments, each having a type) returning a pointer to void"
which does not express the recursive nature of what I'm trying to implement. This will run okay, but doesn't take good advantage of type checking done by my compiler or by lint, and generally feels like cheating. Is there a way to make the declaration I really want?
Thanks,
Tom Leith
4434 Dewey Ave.
St. Louis, Missouri 63116
Internet: trl@wuerl.WUstl.EDU
CompuServe: 70441,3536It ain't easy. The following should work, but I haven't stuffed it through a compiler to check for nits:
struct S { struct S *(*pf)(T1, T2); }; struct S *f1(T1, T2), *f2(T1, T2), .....; struct S xf1 = {&f1], xf2 = {&f2], .....; struct S *f1(T1 t1, T2 t2) { /* perform state function #1 */ ..... return (&xf2); /* successor state is #2 */ } ..... main() { struct S *state = &xf1; for (; ; ) { /* loop on states */ state = (*state-pf)(t1, t2); ..... } }Frankly, I tend to use your solution cauterize the recursion with a void pointer return and cast where necessary. Admittedly, it's not as interesting as this approach. pjpHello P.J.,
I ran across this puzzler. The for loop in the following code should execute up to and including x = 2.0, but it does 1.9 and the other shoe never drops! Please see below for the program and the results. Any light you can shed on this would be much appreciated.
//: FLOAT.CPP - Demonstrate the //'remainder' effect of floats #include <stdio.h> #include <stdlib.h> void main() { float start = atof("0.1"); float stop = atof("2.0"); float incr = atof("0.1"); float x = atof("0.0"); for ( x = start; x <= stop; x += incr ) printf(" %1.8f \n", x); } Results: 0.10000000 0.20000000 0.30000001 0.40000001 0.50000000 0.60000002 0.70000005 0.80000007 0.90000010 1.10000012 1.10000014 1.20000017 1.30000019 1.40000021 1.50000024 1.60000026 1.70000029 1.80000031 1.90000033fraley@usfca.eduIt's a classic problem in floating-point arithmetic. 0.1 has no exact binary representation it's a continued fraction much like 1/3 becomes 0.333333... forever in decimal. You can chop it or round it where you like, but you always end up with a slight error in its representation.
In this particular case, atof apparently rounds the value up. Multiplication, or repeated addition, amplifies the error. So 20 * 0.01 is a tad larger than 2.0. Your loop count is a classic off-by-one error. pjp
ferent dialects, as they interpret the draft
Dear Editor,
Unlike most magazines, you have one of the most active, interesting, and informative letters section. Even better, you seem to be publishing more and more mail. Unfortunately, even I am getting older or the type size in your "We Have Mail" section is getting smaller.
The truth, I fear, is both!
Keep up the good work, but please make the type at least the same size as that used in articles. Thanks.
Andy Levinson
Andy,
If we did that, we wouldn't have room to print this nice letter. And I hate to tell you this, but haven't changed the type size recently. Must be something else that's changing...
Thanks for writing.
mb
Mr Plauger:
I have always wondered why one gets a rotate instruction in the instruction set of almost any CPU one cares to mention, while almost no higher level languages seem to have such an operator. Can you clarify why C does not have a rotate operator ? Is there a very simple way to write C code in such a way that most compilers will detect that a rotate is required and use the rotate instruction ? Any comments you have on this will be greatly appreciated.
Yours Sincerely
Anton Erasmus
I received this e-mail while attending the Australian UNIX Users Group meeting in Melbourne. Since Dennis Ritchie was also attending, I decided to simply ask him your question, rather than guess an answer myself. He told me that neither B nor BCPL, ancestors to C, had a rotate operator, so C didn't get one either.
I personally have written a couple of "narrative assemblers" that included a rotate operator, so I agree that it can be useful. A smart compiler can conceivably recognize the combination of shifts and masks that simulate a rotate, but I don't know of any that do. pjp
Dear Mr. Plauger:
In a previous letter Mr. Mark Pumphrey showed a code fragment which attempts to determine the CPU ID of the PC by calling Int 15h Function C0h. He was disappointed to learn that this fragment always told him he had a PC/AT. The problem with that method is that it doesn't test the CPU ID, but only what the system BIOS knows about the system ID. (Interrupt 15h is one of the BIOS services.) As far as the BIOS is concerned, any CPU from 286 and up is a 286, because nothing in the BIOS would know the difference.
There are a large number of code packages floating around which do identify the CPU type. They do this by feeding the processor CPU-dependent machine instructions and testing the results. Here are two references to such programs:
cpuid the official Intel code to identify CPU type (doesn't include Pentium yet).
whichcpu identifies all 80x86 processors, including Pentium
Both of these can be found on any SimTel mirror, e.g. oak.oakland.edu in directory /pub/msdos/sysinfo, files icpuid3a.zip and wcpu050.zip. They come with source and might therefore be converted into functions callable by other programs.
Eli Zaretskii
eliz@is.elta.co.ilMark [Pumphrey],
There are a number of ways to find the processor type on a PC.
You can write a program to identify which rev (8086/80286/386/486/586) you have there are subtle differences to take advantage of.
Get these from oak.oakland.edu (or any SimTel 20 archive) in sysinfo:
cpuid593.zip 13640 8 910702
Identify system CPU
8086/8088/286/386/486sx/dx
wcpu050.zip 16163 8 930418CPU-identifier
(386/486/DX/SX/Pentium) & NPUHere's a technique that will work on some PS/2s:
INT 15 - newer PSI2; various BIOSes - GET CPU TYPE AND MASK REVISION AH = C9h AL = 10h (may be required on some non-PS BIOSes) Return: CF clear if successful AH = 00h CH = CPU type (see #0357) CL = mask revision (stepping level) (see #0358) CF set on error AH = status (80h,86h = function not supported)Notes: the BIOS must save DX at startup in order to be able to support this call; PS/2 Models 56, 57, 90, and 95 are known to support it. The PS/2 BIOS merely reads CMOS locations 190h (type) and 191h (rev).
Values for CPU type: 03h 80386DX or clone 04h 80486 23h 80386SX or clone 43h 80386SL or clone A3h IBM 386SLC A4h IBM 486SLC Values for stepping level: ---08386--- 03h B1 05h D0 08h D1/D2/E1 ---80386SX--- 04h A0 05h B 08h C/D1 ---80486--- 00h A0/A1 01h B2 03 B3 04h B4 05h B5 06h B6 07h C1 10h cAx 11h cBx 33h DX2/66 --486SX--- 20h A0Note: the steppings for 486 are alternately reported to be 01h=Bx, 02h=C0,04h=D0marty
Member of the League of Programming Freedom
leisner@sdsp.mc.xerox.com
leisner@eso.mc.xerox.com