dff@Morgan.COM (Daniel F. Fisher) (02/24/89)
In article <9382@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: > >No, I just use %p. > Consider a hosted C implementation in which different pointers have different representations, i.e. (char *) is different from (int *) is different from (int (*)()), etc. My question is To what type should a pointer argument be cast when passing it to fprintf() for printing using the %p conversion specifier? In view of the following two examples, I don't see that there is a single correct answer that will work in all architectures. And since %p is part of dANS C, I assume that the answer must be the same for every pointer in every conforming hosted implementation. First Example: In an IBM-PC implementation with small-data, large-code, the data pointers (int *), (char *), etc. are all 16 bits., but function pointers (int (*)()), etc. are all 32 bits. So if I wish to print a function pointer, I cannot cast it to a data pointer without losing information. One the other hand, since the type cast used for both function pointers and data pointers should be the same, I must cast all data pointers to something that is as wide as a function pointer. Second Example: In an implementation on an architecture that is NOT byte addressable, int pointers and function pointers will be word addresses and character pointers will be wider to contain both a word address and a byte offset. So if I wish to print a character pointer, I cannot cast it to an int pointer or function pointer without losing information. One the other hand, since the type cast used for all pointers must be the same, I must cast int pointers and function pointers to something as wide as a character pointer. So in the first example, I want to cast everything to be as wide as a function pointer and in the second, I want to cast everything to be as wide as a character pointer (which is wider than a function pointer). What's a programmer to do? Now if someone wants to tell me that I should cast to (void *) because it is guaranteed to be at least as wide as any pointer type in the implementation, then I will say, "Thank you very much", "Wonderful", "Hooray for void star" and "Gee, I didn't know that". Well, what gives? -- Daniel F. Fisher dff@morgan.com
chris@mimsy.UUCP (Chris Torek) (02/25/89)
In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: >To what type should a pointer argument be cast when passing it to >fprintf() for printing using the %p conversion specifier? Put it this way: `%p' prints one (void *). >First Example: In an IBM-PC implementation with small-data, >large-code, the data pointers (int *), (char *), etc. are all 16 >bits., but function pointers (int (*)()), etc. are all 32 bits. >So if I wish to print a function pointer, I cannot cast it to a >data pointer without losing information. Yes. Moreover, since sizeof(void *)==sizeof(char *)==2, when you print a pointer-to-function using %p and a (void *) cast (on that machine in that model), you will lose some information. I think this is what is known as an oversight (and goes to show why standards committees should avoid inventions).... -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/25/89)
In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: >To what type should a pointer argument be cast when passing it to >fprintf() for printing using the %p conversion specifier? (void *), or, since it has the same representation, (char *).
gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/25/89)
In article <16112@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >I think this is what is known as an oversight (and goes to show why >standards committees should avoid inventions).... No, it's not an oversight. Obviously we could not require generic object pointers to be large enough to act as generic function pointers also. This is clearly reflected in the specification for void *. A second kind of generic pointer would have had to been invented to hold function addresses. We didn't invent one, on the grounds of "insufficient utility". Personally I would rather the %p format not have been introduced, but given the existence of void *, it was a natural addition. It is pretty easy to snipe at X3J11's work and imply that you could have done better, but I note that you didn't help formulate the standard. If you had any constructive criticism to offer, you should have done so during the public review process.
scs@adam.pika.mit.edu (Steve Summit) (02/25/89)
In article <1089@vicorp.UUCP> charlie@vicorp.UUCP (Charlie Goldensher) writes: >In article <9382@bloom-beacon.MIT.EDU>, scs@adam.pika.mit.edu (Steve Summit) writes: >> No, I just use %p. >That's fine if you don't expect to port the code to another machine. >But be aware that not all versions of printf provide the %p option. Yes, but see below. In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: >To what type should a pointer argument be cast when passing it to >fprintf() for printing using the %p conversion specifier? An excellent question. The previously-posted answers (cast to void *) are correct, as far as the standard goes. The case of the 80x86 (IBM PC et al) is, as usual, er, "enlightening." (I never think of peering down cesspools as being particularly enlightening; "engloomening" or "endrearening," perhaps.) Under Microsoft's PC C compiler (I don't know about others), %p expects a 32-bit ("far") pointer, in all memory models. Therefore, the pointer passed must, in general, be cast to "void far *". Although this, I suppose, violates the standard, it's probably the right solution for that architecture, since not forcing a far pointer admits the possibility of losing information. (You're probably printing a pointer because you need to know its value; therefore having printf throw half of it away would not be terribly useful.) Note that I am not defending the 80x86, nor excusing Microsoft for violating the forthcoming standard (it's hardly the first time, anyway). I do think Microsoft's compromise was a good one, under the circumstances. (A better one might have been to make %p strictly standard-conforming albeit potentially useless, and use %P as a nonstandard extension to force 32 bit pointers. This idea has other problems, however.) Although I've been using %p regularly on the PC, I hadn't thought about its implications until Daniel asked his question. I'm afraid that one of two conclusions is inescapable: 1. The ANSI standard is flawed, as far as %p is concerned, or 2. an 80x86 that supports mixed-model programming cannot support C. I've been impressed by the number of architectural abominations that turn out to be compatible with C, given correctly-written, portable code. The extent to which this is true is a tribute to Dennis Ritchie's wisdom and foresight. (A case in point is the recent frequent discussion of the inadvisability of simulating non-zero-based arrays with "funny" pointers to where [0] would be. Such pointers have been illegal since at least 1978, yet most of us have only recently come face-to-face with machines which actually disallow them.) That %p may not enjoy this kind of widespread, forward-thinking portability is not a disparagement of X3J11. (Before anyone gets offended, this means I'd go with conclusion 2 above, at the risk of alienating all of the 80x86 aficionadoes in the world, both of them.) %p has proved to be quite useful; it fills a need and I'm glad it was added. If nothing else, this difficulty merely lends further credence to a truth many have long known: segmented architectures are a crime against the industry. (Intel and IBM are guilty of mass programmer genocide for foisting it upon the world as the people's architecture of choice.) In practice, printing pointers is pretty unportable, but it isn't much of a problem. (Where's the peck of pickled peppers Peter Piper picked?) I can think of few uses of %p outside of debugging code, and it's less important that debugging code be portable. (I usually get most of the bugs out before I start porting.) The few exceptions I can think of are programs like linkers that want to print addresses, but those programs aren't terribly portable, either. To summarize, don't lose much sleep over %p difficulties. Steve Summit scs@adam.pika.mit.edu
mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)
In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: >Consider a hosted C implementation in which different pointers >have different representations, i.e. (char *) is different from >(int *) is different from (int (*)()), etc. My question is > >To what type should a pointer argument be cast when passing it to >fprintf() for printing using the %p conversion specifier? > >In view of the following two examples, I don't see that there is >a single correct answer that will work in all architectures. And >since %p is part of dANS C, I assume that the answer must be the >same for every pointer in every conforming hosted implementation. Casting the pointer to (void *) is supposed to do what you want. The pANS says that a void pointer is large enough to handle any pointer type (ss 3.2.2.3), much like "char *" was supposed to handle any pointer type in old K&R. The documentation for scanf (ss 4.9.6.2) and printf (ss 4.9.6.1), also say that the %p is expected to be a void pointer. >First Example: In an IBM-PC implementation with small-data, >large-code, the data pointers (int *), (char *), etc. are all 16 >bits., but function pointers (int (*)()), etc. are all 32 bits. >So if I wish to print a function pointer, I cannot cast it to a >data pointer without losing information. One the other hand, >since the type cast used for both function pointers and data >pointers should be the same, I must cast all data pointers to >something that is as wide as a function pointer. Casting the pointer to "void *" is not supposed to lose information for any conforming implementation. On the IBM-PC, using Turbo C 2.0, printf("main %p\n", main) will print out a segment:offset address like so: main 252B:000C It does this for all memory models. (I just checked). The address which is printed corresponds to the link map which I generated as well. I checked this for functions, pointers to functions, static and global data in all memory models. The results were always correct. A side note here: the %p parameter prints the result out in an implementation defined format, so the above example is what you get on the IBM-PC, while on a SUN you may get: main 0x00678A The reason, of course, is to allow the implementation to print the address out in whatever format is most usefull for the particular architecture or implementation. >Second Example: In an implementation on an architecture that is >NOT byte addressable, int pointers and function pointers will be >word addresses and character pointers will be wider to contain >both a word address and a byte offset. So if I wish to print a >character pointer, I cannot cast it to an int pointer or function >pointer without losing information. One the other hand, since the >type cast used for all pointers must be the same, I must cast int >pointers and function pointers to something as wide as a character >pointer. The void pointer is gauranteed to be wide enough by the pANS. If it is not able to handle this example, then you should have a serious chat with your compiler vendor. >So in the first example, I want to cast everything to be as wide >as a function pointer and in the second, I want to cast everything >to be as wide as a character pointer (which is wider than a function >pointer). What's a programmer to do? Now if someone wants to tell >me that I should cast to (void *) because it is guaranteed to be at >least as wide as any pointer type in the implementation, then I will >say, "Thank you very much", "Wonderful", "Hooray for void star" and >"Gee, I didn't know that". Fair enough, now you do. :-) -- Mark H. Colburn "Look into a child's eye; Minnetech Consulting, Inc. there's no hate and there's no lie; mark@jhereg.mn.org there's no black and there's no white."
mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)
In article <16112@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >Yes. Moreover, since sizeof(void *)==sizeof(char *)==2, when you >print a pointer-to-function using %p and a (void *) cast (on that >machine in that model), you will lose some information. Wait a minute, Chris. The pANS says (ss 3.2.2.3, pp 37 Oct 31, 88): "A pointer to void may be converted to or from a pointer to any incomplete or other type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer." What you are saying would seem to contradict this. Also, I don't beleive that there is any requirement that (void *) has to be 2 bytes long. In my opinion void should be defined to be at least as large as the largest pointer available in the current compiler model. Therefore, on the IBM-PC, sizeof (void *) should be 4 in every model except for small or tiny. This can be checked using the following program: #include <stdio.h> int main() { printf("sizeof(char *) = %d\n", sizeof(char *)); printf("sizeof(void *) = %d\n", sizeof(void *)); printf("sizeof(int (*)()) = %d\n", sizeof(int (*)())); return (0); } Using Turbo C, I got the following results: +-------------------------------------------------------------------+ | Model | sizeof(char *) | sizeof(void *) | sizeof(int (*)()) | +=============+================+================+===================| | Huge | 4 | 4 | 4 | +-------------+----------------+----------------+-------------------| | Large | 4 | 4 | 4 | +-------------+----------------+----------------+-------------------| >> | Medium | 2 | 2 | 4 | << +-------------+----------------+----------------+-------------------| | Small | 2 | 2 | 2 | +-------------+----------------+----------------+-------------------| | Tiny | 2 | 2 | 2 | +-------------+----------------+----------------+-------------------+ If you take a look at the medium model, you will notice the annomally. However, on the IBM-PC, the compiler may be able to tell the difference between a code and data pointer by checking which segment it is in. I am not sure exactly how Turbo C does this, although I will check. This assumption would break down if the application was attempting to dynamically load functions and execute them (which would tend to put them into the data segment). Taking a function pointer in this case, may, indeed, case problems. There are several applications that I know of that do this. I will point out, however, the the %p notation does appear to work in all of the memory models correctly. I did not try dynamic function loading, but I suppose that I could. -- Mark H. Colburn "Look into a child's eye; Minnetech Consulting, Inc. there's no hate and there's no lie; mark@jhereg.mn.org there's no black and there's no white."
gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/26/89)
In article <9453@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >Under Microsoft's PC C compiler (I don't know about others), %p >expects a 32-bit ("far") pointer, in all memory models. Therefore, >the pointer passed must, in general, be cast to "void far *". This is the essential botch in the MicroSoft implementation(s). void * is REQUIRED to be big enough to hold ANY pointer to object or incomplete type without loss of information, and %p is REQUIRED to correspond to a void * (or char *, same representation). If a void * is not big enough, you already have problems quite apart from %p. > 2. an 80x86 that supports mixed-model programming cannot support C. A standard-conforming implementation of C must make a SINGLE definite choice of how big each object type is. This includes pointers. >segmented architectures are a crime against the industry. There is nothing inherently wrong with segmented architectures; some of the nicest computers ever built have been segment-oriented. Some programming languages (such as Fortran) do not fit well the segment model, so implementations of such languages on segmented machines aren't generally satisfactory, although they can be made to work. Other languages (such as Algol) are an almost perfect match to the segemented model. C is not a perfect fit (its main problem on a segmented machine is implementing malloc() reasonably) but it's pretty close. Unfortunately, many programmers weaned on the VAX (or similar architectures) have developed bad habits that don't port to other architectures. The real crime, in my opinion, was the introduction of several "memory models" for C programming on the 80*86 architecture and the subsequent attempt to link together things compiled under different models. I'm familiar with the rationale behind that, but I don't agree with it.
gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/26/89)
In article <591@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes: >The pANS says that a void pointer is large enough to handle any >pointer type ... No, only any pointer to an incomplete or object type. A function pointer may well not fit into a void*.
chris@mimsy.UUCP (Chris Torek) (02/26/89)
In article <592@jhereg.Jhereg.MN.ORG> mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) writes: >Wait a minute, Chris. The pANS says (ss 3.2.2.3, pp 37 Oct 31, 88): > >"A pointer to void may be converted to or from a pointer to any > incomplete or other type. A pointer to any incomplete or object > type may be converted to a pointer to void and back again; the > result shall compare equal to the original pointer." I believe this paragraph refers only to `data' pointers---at least, the last time I checked carefully, `void *'s were not supposed to be used to hold function pointers. Function pointers are sometimes bigger than data pointers, as with medium model IBM PC compilers and (to a greater extent) with Sperry systems (I hear one compiler for the Univac 1100 has 18-bit pointers to data objects and 162-bit pointers to functions). Again, the last time I checked---I do not now have access to the current proposed standard---the standard had essentially two major sub-groups of pointers, and said that, for all data types T1, T2: if sizeof(T1) < sizeof(T2), sizeof(T1 *) >= sizeof(T2 *) sizeof(T1 *) <= sizeof(char *) == sizeof(void *) sizeof(T1 (*)()) == sizeof(T2 (*)()) In English: a pointer to one data type can only have more bits than a pointer to another if the first type (T1) has more bits than the second; all data pointers (but not necessarily function pointers) fit in (char *)/(void *); and all function pointers fit in each other. >What you are saying would seem to contradict this. I do remember that, at one point, I thought (void *) had to hold function pointers as well. Knowing of medium model and of that Sperry compiler, I checked the then-current draft, and (after much squinting and re-reading of fine print) discovering otherwise. >Also, I don't beleive that there is any requirement that (void *) has to be >2 bytes long. In my opinion void should be defined to be at least as >large as the largest pointer available in the current compiler model. I would agree. Alas, the standard says that (void *) and (char *) must have the same representation, so to comply with our opinions (as opposed to the letter of the proposed standard), an IBM PC compiler or Sperry compiler or . . . would have to make (char *) be twice, or nine times, or whatever, as big as necessary. (If the proposed standard says only that (void *) must hold function pointers, one could avoid this penalty for (char *) pointers.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
mark@jhereg.Jhereg.MN.ORG (Mark H. Colburn) (02/26/89)
Damn. I hate it when the article propagate faster than the cancel message that follows them... In article <16120@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >I do remember that, at one point, I thought (void *) had to hold >function pointers as well. Knowing of medium model and of that Sperry >compiler, I checked the then-current draft, and (after much squinting >and re-reading of fine print) discovering otherwise. This is the delusion that I was under as well. It turns out to be wrong. >>Also, I don't beleive that there is any requirement that (void *) has to be >>2 bytes long. In my opinion void should be defined to be at least as >>large as the largest pointer available in the current compiler model. Unfortunately, there is. I spent last night re-reading the pANS, and there is a paragraph which states that (void *) must be the same size and have the same alignment as (char *). This seems really silly to me. Why introduce a new type if it has no new functionality? I understand that the commitee did not want to break existing code, but if add a new feature... As soon as I found this out, I canceled the article, since it was definitely wrong. At the same time I prayed that it would not get out, but I knew better... By the way, before Doug slams me for not participating in the comment cycle of the standard, I did, however, I piggybacked on another persons ballot since I got involved to late to become a balloting member myself. Somehow I missed this when I was reviewing the standard. >I would agree. Alas, the standard says that (void *) and (char *) must >have the same representation, so to comply with our opinions (as >opposed to the letter of the proposed standard), an IBM PC compiler or >Sperry compiler or . . . would have to make (char *) be twice, or nine >times, or whatever, as big as necessary. (If the proposed standard >says only that (void *) must hold function pointers, one could avoid >this penalty for (char *) pointers.) True. My original concept of a void pointer was a pointer which was maximally aligned and large enough to hold any pointer. It would seem that this would be more useful that the (char *) paradigm that is being proposed. What it means, is that there is no portable way to pass an arbitrary pointer to a function or store one in a data structure. That is really too bad. -- Mark H. Colburn "Look into a child's eye; Minnetech Consulting, Inc. there's no hate and there's no lie; mark@jhereg.mn.org there's no black and there's no white."
tjr@cbnewsc.ATT.COM (thomas.j.roberts) (02/27/89)
In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: >To what type should a pointer argument be cast when passing it to >fprintf() for printing using the %p conversion specifier? >First Example: In an IBM-PC implementation with small-data, >large-code, the data pointers (int *), (char *), etc. are all 16 >bits., but function pointers (int (*)()), etc. are all 32 bits. >So if I wish to print a function pointer, I cannot cast it to a >data pointer without losing information. In TURBO C, you can do: int *p; printf("%Fp",(void far *)p); in ANY MEMORY MODEL, for any size of data pointer p, and get printed "XXXX:YYYY", where XXXX is the segment, and YYYY is the offset for pointer p. If p is a small data pointer, DS will be supplied by the compiler. Note that using the "near" keyword is dangerous in a large-data memory model, because DS != SS, and DS is supplied when converting near pointers to far pointers. I believe that the keywords "_ds", "_es", and "_ss" are intended to permit proper use of small pointers in a large-pointer memory model. For a function pointer, do: typedef int far (*FAR_FUN_PTR)(); /* NOT TESTED */ printf("%Fp",(FAR_FUN_PTR)p); Again, the "near" keyword is dangerous - it is not clear whether it can sensibly be used with function pointers in a large-code memory model. In a large-code model, simply do: int (*fun_ptr)(); printf("%Fp",fun_ptr); Tom Roberts att!ihnet!tjr
henry@utzoo.uucp (Henry Spencer) (02/28/89)
In article <597@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes: >... I spent last night re-reading the pANS, and there >is a paragraph which states that (void *) must be the same size and have >the same alignment as (char *). This seems really silly to me. Why >introduce a new type if it has no new functionality? ... The functionality of "void *" is to be a replacement for all the places where "char *" was used as a generic pointer because there wasn't any generic pointer. For example, the first argument to fwrite(). If "char *" was not generic enough for those places in your implementation, your implementation was broken to begin with. The requirement for representation compatibility with "char *" is vital for backward compatibility with all that code that *does* use "char *" as a generic pointer. If you study things like the conversion rules, you'll see that "void *" does have new functionality. But expecting it to fix your broken compiler (or the broken machine that is the cause of the compiler breakage) is a bit too much to hope for. -- The Earth is our mother; | Henry Spencer at U of Toronto Zoology our nine months are up. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu
bright@Data-IO.COM (Walter Bright) (02/28/89)
In article <9453@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >Under Microsoft's PC C compiler (I don't know about others), %p >expects a 32-bit ("far") pointer, in all memory models. Therefore, >the pointer passed must, in general, be cast to "void far *". >Although this, I suppose, violates the standard, it's probably >the right solution for that architecture, since not forcing a far >pointer admits the possibility of losing information. >I do think Microsoft's compromise was a good one, >under the circumstances. (A better one might have been to make >%p strictly standard-conforming albeit potentially useless, and >use %P as a nonstandard extension to force 32 bit pointers.) Under Zortech's printf(), %p expects a pointer of the default data pointer size. %lp always expects a far pointer. Thus, to print each type of pointer in each memory model: void * func * void near * void far * S %p %p %x %lp M %p %lp %x %lp C %p %x %x %lp L %p %p %x %lp Things to note: 1. Using default data pointers, %p always works. 2. Using far pointers, %lp always works, it's easy to remember because of the similarity with %lx. 3. Using near pointers, %x always works. It seemed redundant to add a special flag for %p for near pointers, since %x did the job nicely. 4. Be careful using function pointers! A compromise is to cast them to (void far *), and use %lp on them. P.S. I wrote it.
cjc@ulysses.homer.nj.att.com (Chris Calabrese[mav]) (02/28/89)
> In article <234@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes: > >To what type should a pointer argument be cast when passing it to > >fprintf() for printing using the %p conversion specifier? > >First Example: In an IBM-PC implementation with small-data, > >large-code, the data pointers (int *), (char *), etc. are all 16 > >bits., but function pointers (int (*)()), etc. are all 32 bits. > >So if I wish to print a function pointer, I cannot cast it to a > >data pointer without losing information. > > In TURBO C, you can do: > int *p; > printf("%Fp",(void far *)p); > in ANY MEMORY MODEL, for any size of data pointer p, and get printed > "XXXX:YYYY", where XXXX is the segment, and YYYY is the offset for pointer p. > If p is a small data pointer, DS will be supplied by the compiler. > Note that using the "near" keyword is dangerous in a large-data memory model, > because DS != SS, and DS is supplied when converting near pointers to far > pointers. I believe that the keywords "_ds", "_es", and "_ss" are intended > to permit proper use of small pointers in a large-pointer memory model. [Other examples and arguments deleted] #define Flame Yes, but the whole point of %p and void is to make such things _more_ portable, where as this example _only_ works on 80*86 architectures, and relies on keywords that aren't even part of C, but are a non-conforming polution of keyword name space invented for IBM PC compilers. #undef Flame The point is, %ld works on 99% of the machines out there now, why should I use %p if it makes things even less portable? The representation of the pointer is non-portable, but accessing it should not be. I can see that you don't really need to pass around function pointers as void*, but there should be a way of printing their values! However, please don't give me %h - prints a pointer to a function (h is the first free letter after f, which is taken) -- Name: Christopher J. Calabrese Brain loaned to: AT&T Bell Laboratories, Murray Hill, NJ att!ulysses!cjc cjc@ulysses.att.com Obligatory Quote: ``Now, where DID I put that bagel?''
mtr@ukc.ac.uk (M.T.Russell) (02/28/89)
In article <9730@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes: >A function pointer may well not fit into a void*. This presumably implies that comparison and assignment between function pointers and void* pointers is illegal, and thus that f = NULL; and if (f == NULL) are both illegal if f is a function pointer and NULL is defined as (void *)0. If this is so, then I don't see that (void *)0 is a reasonable definition of NULL. Is (void *)0 a special case? Gcc 1.32 -ansi -pedantic doesn't think so - it objects to the above constructs. Mark Russell mtr@ukc.ac.uk
karl@haddock.ima.isc.com (Karl Heuer) (03/02/89)
In article <597@jhereg.Jhereg.MN.ORG> mark@jhereg.MN.ORG (Mark H. Colburn) writes: >What it means, is that there is no portable way to pass an arbitrary pointer >to a function or store one in a data structure. Any function-pointer object may be cast to any other function-pointer type and back without change; hence any such type (e.g. `void (*)(void)') may be used as a generic function-pointer type. Therefore, a completely generic pointer type (to function or data) is `union {void (*)(void); void *}', though I can't think of any practical use for such a thing. (The (nonportable) uses I can come up with are better served by a cast rather than a union.) Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
c08_d103@jhunix.HCF.JHU.EDU (Ex-God) (03/02/89)
I need a few things clarified; I don't have a copy of the standard, and a few people have said mutually contradictory things. So tell me if the following is right: Any object or incomplete pointer fits in (void *). Any object or inconplete pointer fits in (char *). Any function pointer fits in any other function pointer. Function pointers do not have to fit into (void *) (or vice-versa). Also, how's this for a solution: #if sizeof (void *) > sizeof (void (*)()) #define generic_ptr (void *) #else #define generic_ptr (void (*)()) #endif Then use generic_ptr instead of void *, and any pointer type (including pointers to functions) will fit (as long as you remember to cast everything you assign). I can see one problem with this solution -- if, for example, an implementation stored functions in one area and objects in another and there was no pointer type that could point to either. I suppose you could use a union or something.... I guess the best solution would have been to continue using char * as a generic object pointer, to make all function pointer the same size, and to make void * big enough to hold either. Someone should have thought of that earlier, I guess.... -- Andrew Barnert (Andy Social/Andy Christ/Andy Matter/ex-God) ins_balb@jhunix/ins_balb@jhuvms/c08_d103@jhunix The opinions expressed in this message are yours. "If you can't stand the Big Chill, burn down the freezer." -- Jello Biafra
gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/02/89)
In article <248@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes: >In article <9730@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes: >>A function pointer may well not fit into a void*. >This presumably implies that comparison and assignment between function >pointers and void* pointers is illegal, and thus that > f = NULL; >and > if (f == NULL) >are both illegal if f is a function pointer and NULL is defined as (void *)0. >If this is so, then I don't see that (void *)0 is a reasonable definition >of NULL. >Is (void *)0 a special case? Gcc 1.32 -ansi -pedantic doesn't think so - >it objects to the above constructs. Yes, null pointer constants are special cases. (void*)0 is not really a pointer of type void*, it's just one way of writing a null pointer constant and thus has special properties. In particular it can be assigned to ANY non-const pointer variable and compared against ANY pointer type. It compares unequal to a pointer to any object or function. A valid pointer expression of any type has a value that fits exactly one of the following categories: (1) valid pointer to object or function (2) null pointer An uninitialized static-duration pointer variable starts out with a value that is a null pointer; other pointer variables acquire null-pointer values only by having one assigned to them, either directly as a null pointer constant or indirectly via some other null-valued variable. GCC 1.32 sounds broken here.
gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/02/89)
In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes: >I need a few things clarified; I don't have a copy of the standard, >and a few people have said mutually contradictory things. So tell me >if the following is right: >Any object or incomplete pointer fits in (void *). Yes, any POINTER to an object or to an incomplete type after conversion to void* and back and will still compare equal to the original. >Any object or inconplete pointer fits in (char *). Yes, but you have to exert some care to get it into or out of one. In particular, you have to use casts. >Any function pointer fits in any other function pointer. Yes, but you have to exert some care. In particular, you have to use casts. By the way, to USE a pointer, it has to be of the right type at the place it is used. It is best and simplest to maintain pointers as their correct types all along, rather than unnecessarily mapping them back and forth into other types. For one thing, if you use proper types everywhere, the compiler can help detect usage errors. >Function pointers do not have to fit into (void *) (or vice-versa). Yes. >Also, how's this for a solution: >#if sizeof (void *) > sizeof (void (*)()) >#define generic_ptr (void *) >#else >#define generic_ptr (void (*)()) >#endif >Then use generic_ptr instead of void *, and any pointer type >(including pointers to functions) will fit (as long as you remember to >cast everything you assign). Won't work. There is no guarantee that an object pointer can be converted to a function pointer or vice-versa. >I guess the best solution would have been to continue using char * as >a generic object pointer, to make all function pointer the same size, >and to make void * big enough to hold either. Someone should have >thought of that earlier, I guess.... "Someone" did think of it, and realized that it wasn't acceptable. I don't understand why so many people seem to be concerned about packing both object AND function pointers into the same type of variable. (By the way, it CAN be done; use a union.) What sort of program calls for such weirdness, anyway? The only genuine "problem" I've seen mentioned is the inability to portably print out a function pointer, something I don't think is all that useful.
henry@utzoo.uucp (Henry Spencer) (03/03/89)
In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes: >#if sizeof (void *) > sizeof (void (*)()) >#define generic_ptr (void *) >#else >#define generic_ptr (void (*)()) >#endif >Then use generic_ptr instead of void *... Unfortunately, if I recall correctly (my Oct draft is out on loan), in strict ANSI C it is illegal to convert function pointers into object pointers and vice-versa. Before you scream, consider that there are architectures on which this is not a meaningful operation, and it is almost always a highly machine-specific operation. The ability to do such conversions is mentioned under "common extensions" in the appendix. -- The Earth is our mother; | Henry Spencer at U of Toronto Zoology our nine months are up. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu
ked@garnet.berkeley.edu (Earl H. Kinmonth) (03/03/89)
In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >In article <928@jhunix.HCF.JHU.EDU> ins_balb@jhunix.UUCP (Andy Matter) writes: >I don't understand why so many people seem to be concerned about >packing both object AND function pointers into the same type of >variable. (By the way, it CAN be done; use a union.) What sort >of program calls for such weirdness, anyway? The only genuine It doesn't take a particularly weird program to require this. Almost any program that interprets a script containing both function calls and variables (sym table references) will require this. The hoc calculator program in Kernighan/Pike, UNIX PROGRAMMING ENVIRONMENT, requires this. Any awk like program will require it. Any script for a spread sheet that uses functions and variable names will probably require it. So too will an editor that allows complex scripts. Generally such applications will generate a vector that contains a mix of calls to functions and objects (pointers to elements in a symbol table).
scs@adam.pika.mit.edu (Steve Summit) (03/03/89)
In article <11915@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >Therefore, a completely generic pointer >type (to function or data) is `union {void (*)(void); void *}', though I can't >think of any practical use for such a thing. In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >I don't understand why so many people seem to be concerned about >packing both object AND function pointers into the same type of >variable. (By the way, it CAN be done; use a union.) What sort >of program calls for such weirdness, anyway? The only genuine >"problem" I've seen mentioned is the inability to portably print >out a function pointer, something I don't think is all that useful. I will have to use a variation on this union in the symbol table and the rvalue structure of a C interpreter I wrote once, if I ever want it to pass ANSI muster. (I'm not sure I'll bother.) So far, the interpreter's symbol table has assumed that there is one type which can contain any pointer, to code or data. There is also an "nm" ("namelist") command, which prints the symbol table; this command has been using %p but will require revision if %p will not portably print a function pointer. The same difficulties would hold for any assemblers, linkers, or other object file manipulation programs (nm, etc.) written in C on a system with incompatible code and data pointers. Those programs don't have to be portable, and can use whatever system- dependent kludges are required. It's more troublesome for the interpreter, most of which (outside of its dynamic linker) is quite portable. Admittedly, a C interpreter is a rather extreme, one-of-a-kind program, that pushes the edges of the envelope in several ways. (The dynamic linker reads object code into malloc'ed memory and then jumps to it, a maneuver which some architectures reject outright. Others require a special call to create new text segments, which the interpreter would have to use.) Steve Summit scs@adam.pika.mit.edu P.S. The astute reader may wonder why an interpreter needs to handle code pointers at all, since the "code" being interpreted by an interpreter is really data. However, an interpreter that allows intermixing of interpreted and compiled code must keep "code" pointers, whether to interpreble or compiled code, as true code pointers, partly to keep function calls made from interpreted code consistent, and more importantly to allow calls from compiled code back to interpreted code to be made at all. (When you link and relocate object code with undefined external references, you've got to have a real function pointer to fill in. Arranging that calls to interpreted routines from compiled code work is a challenging problem, and is left as an exercise for the reader :-). A correct solution allows installing interpreted routines as signal handlers, a fascinating concept.)
gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/04/89)
In article <9571@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >I will have to use a variation on this union in the symbol table >and the rvalue structure of a C interpreter I wrote once, if I >ever want it to pass ANSI muster. (I'm not sure I'll bother.) It's not a question of ANSI C; you have to do it right if your code is going to be maximally portable, quite apart from ANSI C. X3J11 did not invent the distinction between code and data addresses; they simply acknowledged that there often is a distinction and that it would be prohibitively expensive to have to disguise this distinction in some common environments.
karl@haddock.ima.isc.com (Karl Heuer) (03/04/89)
In article <9571@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >In article <11915@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >>Therefore, a completely generic pointer type (to function or data) is >>`union {void (*)(void); void *}', though I can't think of any practical >>use for such a thing. > >[Symbol table, etc.] I stand corrected. I was thinking in terms of using the union as a type pun; a tagged union (or any other protocol which guarantees that the member fetched is the one most recently stored) is indeed useful. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
bill@twwells.uucp (T. William Wells) (03/04/89)
In article <9765@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
: >Also, how's this for a solution:
: >#if sizeof (void *) > sizeof (void (*)())
: >#define generic_ptr (void *)
: >#else
: >#define generic_ptr (void (*)())
: >#endif
: >Then use generic_ptr instead of void *, and any pointer type
: >(including pointers to functions) will fit (as long as you remember to
: >cast everything you assign).
:
: Won't work. There is no guarantee that an object pointer can be
: converted to a function pointer or vice-versa.
Also, the sizeofs don't work in #ifs. Not according to the dpANS, nor
in many existing compilers.
---
Bill
{ uunet | novavax } !twwells!bill
(BTW, I'm going to be looking for a new job sometime in the next
few months. If you know of a good one, do send me e-mail.)