Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83 (MC830713); site edai.UUCP Path: utzoo!watmath!clyde!floyd!vax135!ukc!edcaad!edee!edai!ok From: ok@edai.UUCP (Richard O'Keefe) Newsgroups: net.lang.c Subject: if sizeof (char*) == 4 then sizeof (int) MUST = 4 (K&R) Message-ID: <4076@edai.UUCP> Date: Mon, 12-Mar-84 18:51:52 EST Article-I.D.: edai.4076 Posted: Mon Mar 12 18:51:52 1984 Date-Received: Thu, 15-Mar-84 01:45:34 EST Organization: Art.Intelligence,Edin.Univ. Lines: 99 The question about passing NULL as a parameter having been settled, (though on p163 of the Kernighan & Ritchie book you will find two instances of NULL being passed where (char*) was expected), there remains another problem with the size of pointers versus the size of 'int'. The program I was trying to make portable has a function something like structp consVect(n, p) long n; structp p[]; /* build an object containing n things */ which is called in several places: long n = ...; p = consVect(n, table); which works fine, and also: q = table; while (...) *q++ = ...; p = consVect(q-table, table); WHICH LINT REJECTS. The reason is that I declared the argument "n" to be 'long' (which it can be on a 32-bit machine, and if the compiler treats int as 16 bits I don't want a structure with 100,000 elements mysteriously truncated). However, Lint is convinced that pointer-pointer='int'. This time I decided to read Kernighan and Ritchie with the utmost care myself before going off half-cocked, and I found that Lint has The Book on its side: page 189 (in the "C Reference Manual, 7.4") says If two pointers to objects of the same type are subtracted, the result is converted (by division by the length of the object) to an INT representing the number of objects separating the pointed to objects. This conversion will in general give unexpected results unless the pointers point to objects in the same array, since pointers, even to objects of the same type, do not necessarily differ by a multiple of the object-length. (As an aside: while K&R p98 explicitly permits p==0 and p!=0, I can find nothing in K&R which requires p>0 or p-(char*)0 to be defined, where char *p;) There would appear to be two courses of action open to someone who wants his C compiler for a 32-bit machine to conform to K&R. EITHER make sizeof (int) == 2 AND ALSO make sure that no array can contain more than 32,767 bytes (that was 'int' remember, not 'unsigned int'). This is clearly silly. OR make sizeof (int) big enough to hold the largest possible difference between two char* pointers into the user's address space. Thus if the user's address space is 16 Mbytes, sizeof (int) has to be 4 (3 won't do, the difference has to be POSITIVE). If a program is to be moved from a 16-bit machine to a 32-bit machine, obviously differences between pointers will have been 'int' rather than 'long', and by the book this is guaranteed safe on any machine! There really doesn't seem to be any way of escaping from the conclusion that sizeof (int) == sizeof (long*). On pp 182-183 we find the folllowing: Up to three sizes of integer, declared SHORT INT, INT, and LONG INT, are available. Longer integers provide no less storage than shorter ones, but the implementation may make either short integers, or long integers, or both, equivalent to plain integers. >>>"Plain" integers have the natural size suggested by the host machine architecture; the other sizes are provided to meet special needs.<<< (Emphasis mine.) No explicit definition of "suggested by ..." is given, so we turn to the table at the top of p182. Now that only describes the PDP-11, Honeywell 6000, IBM 370, and Interdata 8/32 implementations, and it doesn't say what size a pointer is. We can still draw the conclusion that in this "approved" list, "int" has at least as many bits as a machine address. I propose that the definition of "plain" integers should be read as "Plain" integers (declared by the type 'int' without modifiers) are the smallest size fully supported by the host architecture which is sufficient to represent the difference between any two addresses in the user's data space. If no integer size is fully supported by the architecture, the smallest size supporting addition, subtraction, arithmetic shifts, and logical operations which is sufficient to represent the difference between any two addresses in the user's data space. [The second sentence is meant for machines which don't fully support multiplication and division; given the operations listed you can program them.] This still leaves Lint entitled to complain about my predecessor's passing NULL instead of (foo*)0. While int must have enough bits to represent any pointer, pointers are not required to be compactly encoded. (The lamentable PR1ME shows this. A character pointer on the P400 is represented by 48 bits, but one 16-bit word just holds a byte number (i.e. 1 bit), and one bit in the remaining 32 just says whether or not the pointer is thought to be valid (for omitted arguments), so 32 bits is all you need on the PR1ME.) Lint is entitled to complain for another reason too. 'int' could be LONGER than a pointer! Oh well, I've put casts around all the NULLs anyway.