LETTERS

One Good Answer...

Dear DDJ,

In the October 1991 issue, Steve Summit's "C Q&A" had a question proposed on how to write a generic macro to swap two values. The answer given was that there was no good answer. Certainly this is true in general for pointers and floating-point values. However, for integer and character values, the following is a simple solution: #define SWAP(a,b) a^=b; b^=a; a^=b.

By using UNION statements, pointer, and floating-point values can also be swapped. But I admit that it surely would be a special case if the above could be used without a lot of setup.

Ron Harper

Houston, Texas

...Deserves Another

Dear DDJ,

In his October 1991 "Programmer's Bookshelf," Andrew Schulman reprinted one of his favorite functions, cardinality(), from Harbison and Steele, saying, "I still can't get how it works." Dammit, I couldn't sleep till I got it figured out, and it is cute.

The trick quantity is (x &...x); x is unsigned. Assuming a nonzero x, the result has one and only one high bit, in the position of the rightmost 1 in x. It is a neat trick of twos-complement arithmetic. See Example 1.

Example 1

  x =         yyy...y100...0
  !x =        YYY...Y011...1 ( Y = !y )
  -x = !x+1=  YYY...Y100...0 (unary - is twos complement inversion)
  x & -x=     000...0100...0 !

Example 2 is a commented version of cardinality() in C++. I suspect the technique has been reinvented many times for parity-bit check routines and interrupt handlers.

Example 2

  // cardinality() from Harbison&Steele, over easy (lightly cooked)

  typedef unsigned SET;
  const SET emptyset=0;
  inline SET rightmost1 (SET x) { return (x & -x); };
  // this returns a SET with one and only one bit set, at the rightmost
  // high bit of x.

  int cardinality (SET x) {
        int count = 0;
        while (x ! = emptyset) {
               x ^= rightmost1(x); // clears the rightmost 1 in x
               ++count; // count the number of times we clear a 1
        }
        return count; // this is the number of 1's that were in x
  }

Greg Goodknight

Reseda, California

What's a MODder to Do?

Dear DDJ,

In reference to Jeff Duntemann's "Structured Programming" column (November 1990) about the MOD function in Turbo Pascal, Jeff states that in Turbo Pascal, -17 MOD 7 returns -3, whereas -17 modulo 7 should be 4. If I remember from my mathematics classes correctly, in modulo 7 arithmetic, -3 is equivalent ("congruential to") 4. There are 7 congruencer classes in modulo 7 arithmetic. They are:

  ...-14-7071421...
  ...-13-61-81522...
  ...-12-5291623...
  ...-11-43101724...
  ...-10-34111825...
  ...-9-25121926...
  ...-8-16132027...

This can be seen through use of the graphical interpretation of the modulus operator Jeff gives in his article --count clockwise (for positive numbers) or counterclockwise (for negative numbers) and see which numbers leave you at each position on the "dial." It is conventional to name as a representative of a congruence class for modulo n the value between 0 and n-1. Turbo Pascal (and QuickBasic and other programming languages) return a different representative.

To return the desired representative, Jeff's method of using Turbo Pascal's TRUNC (truncation) operator and forcing it to round in the proper direction if a negative value is encountered appears to work. In QuickBasic, the FIX operator seems to return the same results as does Turbo Pascal's TRUNC. The MOD operator appears to give the same results in both languages. There are two options which are probably more straightforward than the method you use to return the desired representative:

INT x "returns the largest integer less than or equal to" x, whereas FIX x "returns the truncated integer part" of x. The difference between INT and FIX is that, for negative x, FIX returns the first negative integer greater than x, while INT returns the first negative integer less than x.

So, in conclusion, I wouldn't consider the MOD operator to be "wrong" in Turbo Pascal (or QuickBasic)--it just returns a representative of the correct congruence class which may not be the one that you (or the rest of your program) are expecting.

David Hall

Moscow, Idaho

Code B. Free

Dear DDJ,

I enjoy DDJ immensely. I thought the April 1991 "Editorial," "Mark's Modest Patent Proposal," was highly interesting. The last comment, however, really hit the spot.

If you study the patent situation carefully and in depth, you may come to the same conclusion that I have: Government has no business interfering with business. Dick Gabriel has the right idea. If you can't "out-engineer, out-market, and generally out-perform the competition," you should not expect the Government to pull your fat out of the fire. It's a good thing that Isaac Newton couldn't patent his law of gravity or we'd all still be paying royalties to his descendants. (Not really!)

The "patenting of software" issue points up the conceptual difficulties that lie, lurking in the underbrush of legalisms. If we are going to have a free market, let it be free!

Perhaps your readers can let their minds soar and think of the progress (and money) that could be made when companies (and individuals) don't waste their time trying to corner the market by inventing "claims." Of course, things would change even faster, but there is only one thing that is certain in the universe, change. (Who said that?)

David Michael Myers

Martinsburg, West Virginia

Don't Stop Now!

Dear DDJ,

In his August 1991 "Structured Programming" column, Jeff Duntemann stopped just short of explaining the thing I most needed to know. He stated:

When an interrupt comes in from one of the second set of IRQs, the second 8259 enters an interrupt to IRQ2 of the first 8259. Then some additional protocols must be followed to inform the CPU which of the second set of IRQs was the ultimate source of the interrupt. Yes, it does get hairy, but the second eight IRQs don't really involve serial communications in any way, and I won't be discussing them further.

I am developing a system where I need to have interrupt-driven routines to catch bytes arriving through COM3 and COM4, as well as COM1 and COM2. Could you please explain the "additional protocols" which must be followed for IRQ8 through IRQ15, or could you point me to a source of this information? It would be much appreciated.

Jonathan E. Kopke

Cincinnati, Ohio

String Class Opposition

Dear DDJ,

This is in regard to Steve Teale's October 1991 article, "Proposing a C++ String Class Standard." I strongly oppose the proposed ANSII string class. The proposed class is a class for character arrays and should be named accordingly. In C, there always was a difference between character arrays and strings, please let's keep it. Kernighan and Ritchie had very good reasons for allowing both options. Is this just an effort to confuse C programmers or is there a trend to make C++ incompatible with C?

The need to have the "\0" character in a string arises very seldom. Let's have a character array class for that purpose. Sometimes specialization is an advantage, but there is no need to change names as it just confuses the issue.

A string class should "waste" the memory needed to store the terminating "\0" character, because it saves the time and memory consuming conversion to a C string if you call a library function accepting a string argument. Note that the code in Example 3 would not work.

Example 3

  String name ="myfile";
  String ext=".dat";
  String fullname=name+ext;
  FILE *fp=fopen (fullname, "r"); // missing '\0' terminator
  FILE *fp=fopen (fullname+' \0',"r"); // calls malloc!!

The purpose of having a string class is to permit the code in Example 3 without the need of a time- and memory-consuming conversion.

The C standard library contains many functions accepting C string arguments. Many compilers supply these functions in tight assembler code. Why should we be discouraged from using these C standard library functions? What are the advantages of rewriting all that proved and widely used code?

An often used argument against the C string is that determining the end of the string can be time consuming. A String class can easily overcome that disadvantage by storing the current length of the string. To speed up concatenation, the class could also store the current size of the reserved memory and assign memory in chunks.

There is a reason why the proposed ANSII class, specifying that a string can contain the "\0" character and that it is not terminated by the "\0" character, will not be used very much:

Millions and millions of lines of code in libraries accept C strings as arguments. To use the proposed string class, a conversion routine, probably reserving heap memory for temporary storage, must be called before a call to a function accepting a string argument. A major feature of C++ is that it is easy to use existing C libraries. It would be a waste of programming power to rewrite all that code just so functions would accept ANSII C++ strings.

What programmers really need is a dynamic string class that makes string handling as easy as in Basic, and also allows the use of C libraries. Classes like that have been published. The ANSII committee should not invent new things or change accepted conventions, but should generate a useful standard that combines the advantages of the different versions already in use.

Helmuth Schmalzl

San Luis Potosi, Mexico

Less is More

Dear DDJ,

This is in regard to Jack Woehr's September 1991 article, "Forth: A Status Report." I have received copies of Forth BASIS proposals for a few years, only to see that the standard was a growing set of words. I felt the standard should be on the Core Word Set (original 83 word set?). All other words should be in a routine library that can be chosen and compiled by the end user if needed. The idea is to give the end user the tightest or smallest kernel to start with for the platform being developed on.

William B. Higinbotham

Brookhaven, New York.

Why Buy the Cow When the Milk is Free?

Dear DDJ,

This is in response to the "Editorial" in the October 1991 issue concerning "pay-per-use" software. There really is no reason to pay to use software, if software is as free as air (paraphrasing the GNU manifesto). You just have to know how to find it. Many talented people (including GNU) are replacing and enhancing ~NIX with free software. One problem ~ vendors tend to hide their source c~, making it difficult for users to fix bugs. It makes life much easier when you have readable source code.

Marty Leisner

Rochester, New York

C Fans and Otherwise

Dear DDJ,

I like the new "C Language Q&A" feature very much. I tend to agree with Jeff Duntemann's assessments of C (as a "sorry mess," for example -- March 1989). Still, C is what I use. I am always glad to see warnings about hazy aspects of the language. Gimpel's lint advertisements are likewise stimulating, though of course they don't print the answers.

Mr. Duntemann may be interested to learn that I switched from Pascal because it is so different on OS-9 and MS-DOS. I find C less confusing than attempting to remember which Pascal I'm using.

Charles Marsh

Monroeville, Pennsylvania

Dear DDJ,

I enjoyed your May 1991 issue, as I do each issue. However, in reading the "Programmer's Bookshelf" by Andrew Schulman I see that he could not be considered a C fan. Can't fault him too much for that, as it seems my favorite programming columnist (Jeff Duntemann) doesn't appear to be enamored with it either. It is, however, in the C++ form, my favorite. I therefore feel obligated to point out what appears to be a misunderstanding of operator overloading.

On page 138, column 2, paragraph 1, the statement is made: "Nor can you overload operator ^() to mean exponentiation and check if (n == 2^64 - 1) because in C--I mean in C++, the ^ operator is unary not binary." Example 4 demonstrates that this is not the case.

Example 4

  /*******************************************************
  CLASS    :  cMyInt
  PURPOSE  :  demonstrate overloaded xor operator
  DESCRIPTION  :  useless
  LAST UPDATE  :  4-15-1991 21:51:03
  PUBLIC  :
  MEMBER FUNCTION      CALL WITH     RETURNS
  cMyInt               void
  cMyInt               int i
  operator =           int i         cMyInt&
  operator ^           int i         int
  operator int         void          int
  ********************************************************/
  class cMyInt {
     long Val;
     public:
     cMyInt ( void ) { Val = 0; }
     cMyInt ( int i ) { Val = i; }
     cMyInt& operator =( int i ) { Val = (long) i; return *this; }
     int operator ^( int i );
     operator int( void ) { return (int) Val; }
  }; // class cMyInt
  int cMyInt::operator ^( int i ) {
     if ( i == 0 ) {
          if ( Val == 0 ) return 0;
          else return 1;
  }
     int exp = ( i < 0 ) ? ( i * -1): i;
     long l = Val;
     if ( exp > 1 )
        for(--exp; exp; exp--) 1 *= Val;
     return (int) 1;
  }
  main() {
     int i = 3;
     cMyInt m;
     m = i;
     if ( ( ( (cMyInt) 4^2 ) - 1 ) == 15 ) {
      printf("%d ^ 4 = %d\n", (int) m, m ^ 4 );
      printf("\tc's version is %d ^ 4 = %d\n", i, i^4);
     }
     else printf("\tIt didn't work!\n");
  }

If your example class BigInt has been properly defined to handle integers large enough and includes the definitions for:

  BigInt::Bigint( int ); BigInt& BigInt::operator ^( int );

your code would work in this form:

  if (n == ( (BigInt) 2^64 ) - 1 ) {...}

A cast is necessary in order for the compiler to "know" that we want the overloaded operator for a power function, not the xor function that would be used on the char 0x2. The extra parens are necessary because while we can overload the operators all we want, we cannot change the precedence which they receive, and xor has a lower precedence than minus.

xor is not unary in any language I have ever tried.

Compiled with Turbo C++, Version 1.01, the output from the above test program looks like this:

   C:\ > test 3 ^ 4 = 81
   c's version is 3 ^ 4 = 7

I admit, however, that I don't know any way to pass a 20-digit integer in C++ except as a string. Nobody's perfect. C++ gives me the low-level control that I want and the reusability and ability to build in error detection--correction of objects. It also has the notation system that makes the most sense to me, which is probably the only true reason for preferring it over Turbo Pascal. It just seems more natural to code:

  int a, b, c; a = b = c = 0; a = ++c; b = ++c; ++c += a;

than the Pascal way:

    var a, b, c: integer; a := 0; b := 0; c := 0;
    c := c + 1; a = c; inc( c ); b := c; inc (c, a + 1 );

I do wish however, that there were a way to say:

  function funcA( a, b, c: integer): integer;

(although this would take more space), rather than:

  
  int funcA( int a, int b, int c );
  // shouldn't have to repeat int three times.  K&R missed the boat there, or 
  rather the improvements to K&R did.  With K&R you could say:

  int funcA( a, b, c )
  int a, b, c; {... }

Prototypes are an improvement, but we lost something too.

As always I enjoyed DDJ. Thanks for a great mag, and keep it coming.

Jerry E. Howton

Madisonville, Kentucky

That's the Point

Dear DDJ,

I found the November 1991 interview with the creator of PenPoint ("A Conversation with Robert Carr,") fascinating. I think more attention should be given in your magazine to operating systems of the future. MS-DOS I think is the Fortran of operating systems, and years from now people will wonder how it took so long for a better operating system to arrive.

In particular, I believe an operating system should be integrated with and complement all applications. Bits of program should be able to move around freely; files, variables, and devices should all be objects; memory hassles should be hidden from the user and the programmer; maybe even spreadsheets and other applications could operate on general objects with a set of generic functions. It sounds like Robert Carr is doing everything I have always dreamed of doing.

On a completely different subject, has anyone explored the idea of using data compression for information encryption? It stands to reason that if you take all the redundancy out of a file, you render it impervious to cryptoanalytic attack. Arithmetic coding with a good adaptive model should create files in which there is no detectable difference from a random file, so how could anyone crack it?

Tim Cooper

Eastwood, Australia


Copyright © 1992, Dr. Dobb's Journal