Newsgroups: comp.lang.c Path: utzoo!utgpu!jarvis.csri.toronto.edu!dgp.toronto.edu!hugh From: hugh@dgp.toronto.edu ("D. Hugh Redelmeier") Subject: Re: Bug in new K&R? Message-ID: <8805120741.AA05095@explorer.dgp.toronto.edu> Organization: University of Toronto, CSRI References: <7861@alice.UUCP> Date: Thu, 12 May 88 02:21:58 EDT In article <7861@alice.UUCP> dmr@alice.UUCP writes: >In spite of the fact that the type void* is now guaranteed to have >the same representation as char*, I don't think that the type >rules of the dpANS guarantee this will work. > >In our defense, given that the representation of char* is the same >as that of void*, it is reasonable to expect that you would be safe >in reproducing the book example provided your compiler accepted it. >However, at least as I read the last-issued version of the standard, >the compiler might well reject it. > >A lot of people are going to be surprised when ANSI compilers become >common. As I have argued before, type qualifiers are not an unmixed >blessing. Here is an extract from my public comment to X3J11. References to the draft (X3J11/88-001) look like page/line chapter.section.subsection I don't think that any action resulted from this. 24/5 3.1.2.5 overlapping values of signed and unsigned have same rep 25/5 3.1.2.5 void * has same rep as char * 39/18 3.3 accessing an object with type different from declaration 131/25 4.9.6.1 fprintf There seems to be some partial compatibility presumed between unsigned, signed, and plain types of the same width. There also seems to be some unspecified compatibility between char * and void *: why else decree that their representations match? In fprintf and its variants, d, i, o, u, x, and X conversion specifiers work only with signed int arguments (or signed long int, if preceded by l) (see 132/44 4.9.6.1). But many existing programs pass unsigned int (or unsigned long) arguments. Surely these must not be deemed wrong. And I suspect that the effort of casting all unsigned arguments will seem so silly that it won't even be done for new programs (prototypes don't change this: they are matched by elipsis). Especially for the u conversion -- it would seem downright wrong on a one's complement machine (unsigned max would probably have to print as zero). okOld programs, written under the "unsignedness sticks" rules might well pass an unsigned short argument to an unsigned int parameter. Under the current default argument promotions, the argument will be promoted to signed int if sizeof(int) is greater than sizeof(short). Thus, the program will silently fail (i.e. violate a rule in the standard). Of course, on most implementations, these programs will continue to work IN SPITE OF the standard. The similar thing can happen to unsigned char arguments. Issue 1: extended parameter/argument compatibility A new kind of type compatibility is required, say "congruence". Two types are congruent if they are compatible. In addition, two integer types are congruent if their sizes are the same. Furthermore, two pointer types are congruent if one is compatible with pointer to char and the other is compatible with pointer to void. The agreement of parameters with arguments must be changed. Argument and parameter types, if either was produced by the default argument promotions, must at least be congruent. If they are congruent, but not compatible, the parameter passing will work when the value is representable in both types, and both representations are identical (note: in one's complement, -0 has a distinct representation from +0). Even though passing parameters to congruent types will sometimes work, it should be considered an error so that it may be diagnosed. This is wishy-washy, but existing programs have not carefully distinguished signed versus unsigned, and certainly have not passed void * parameters to library functions that now require them. It is to be admitted that the distinction between "working" and "correct" is questionable. Making this code work is important to the mandate of X3J11: existing code is important. I infer that this is what some of the referenced passages of the draft standard are trying to get at, but fail to accomplish. Issue 2: fprintf parameter should be unsigned for o, u, x, and X Draft 132/44: "The int argument is converted to signed decimal (d or i), unsigned octal (o), unsigned decimal (u), or unsigned hexadecimal notation (x or X); ..." Since o, u, x, and X conversion specifiers do not generate signs, it would seem most natural that they should take unsigned numbers to format. In fact, they take a signed number. This should be changed. Note that fscanf gets these conversion specifiers right: for them it expects the argument to be a pointer to unsigned int. Proposed replacement: "The int (d or i) or unsigned int (o, u, x, or X) argument is converted to signed decimal (d or i), unsigned octal (o), unsigned decimal (u), or unsigned hexadecimal notation (x or X); ..." This still leaves a problem: old programs have often printed signed ints with these conversion specifiers. Unless the first issue is addressed, some programs are bound to be broken. Hugh Redelmeier {utcsri, utzoo, yunexus, hcr}!redvax!hugh In desperation: hugh@csri.toronto.edu +1 416 482 8253