gwyn@smoke.BRL.MIL (Doug Gwyn) (04/14/90)
In article <1841@ambush.dk> andrew@ambush.dk (Leif Andrew Rump) writes: >it is a bit puzzling at first to see that void * is legal but I sure >rather want that than char * as it was in malloc and other in the past The trick is to realize that there are no void objects; void denotes an incomplete type that cannot be completed! Thus when a pointer of type void * points to some object, the type of the pointed-to object cannot be void but must be determined in some other way. The standard library routines have void * parameters when they are expected to be used to operate with a variety of different actual object types. Since objects are made up of bytes, we COULD have continued to use char * to point at arbitrary objects, but void * permits stronger type checking, and that was considered to be a Good Thing.
sah@mips.COM (Steve Hanson) (08/07/90)
Using void* in function declarations I would think will be, if not already, a very common abstraction mechanism; however it seems to have an awkwardness about it. Here's a straight forward implementation of memchr, assuming char's are unsigned: void *memchr(const void *s, int c, unsigned int n) { register unsigned char C = c; while (--n >= 0) if (*s++ == C) { return --s; } return (0); } This fails of course because of the dereference and the increment applied to void*. The dereference problem is easy to remove: void *memchr(const void *s, int c, unsigned int n) { register unsigned char C = c; while (--n >= 0) if (*(unsigned char *)s++ == C) { return --s; } return (0); } however the inc/dec cause a temporary variable to be introduced: void *memchr(const void *s, int c, unsigned int n) { register unsigned char* S = s; register unsigned char C = (unsigned char) c; while (--n >= 0) if (*S++ == C ){ return (void* )--S; } return (0); } An alternative would have been to allow void* to be completed (or inherit its type) by casts. The temporary S wouldn't be necessary that causes a bit of coding awkwardness and inefficiency due to extra store that doesn't lend itself to copy propagation removal. The code then becomes: void *memchr(const void *s, int c, unsigned int n) { register unsigned char C = c; while (--n >= 0) if (*(unsigned char*)s++ == C ){ return (void *) --(unsigned char *)s; } return (0); } Reasonable defaults would be that the size of what void* points is the same as the size of char since void* pointers have the same representation and alignment requirements as a pointer to a character type. Since this is all not true today I would expect to see the idiom: type func(void* p) { type T = p; ... ref T ... } for the most trivial of functions that march through data structures via void*. -- UUCP: {ames,decwrl,prls,pyramid}!mips!sah USPS: MIPS Computer Systems, 930 Arques Ave, Sunnyvale CA, 94086
gwyn@smoke.BRL.MIL (Doug Gwyn) (08/07/90)
In article <40624@mips.mips.COM> sah@mips.COM (Steve Hanson) writes: >however the inc/dec cause a temporary variable to be introduced: That could be avoided by defining the function as void *memchr(const char *s, int c, unsigned int n) which works because of the "same representation" requirement.
jfh@rpp386.cactus.org (John F. Haugh II) (08/10/90)
In article <13499@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >In article <40624@mips.mips.COM> sah@mips.COM (Steve Hanson) writes: >>however the inc/dec cause a temporary variable to be introduced: > >That could be avoided by defining the function as > void *memchr(const char *s, int c, unsigned int n) >which works because of the "same representation" requirement. Doesn't this defeat the purpose of (void *) as an abstraction mechanism? -- John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 832-8832 Domain: jfh@rpp386.cactus.org
gwyn@smoke.BRL.MIL (Doug Gwyn) (08/11/90)
In article <18498@rpp386.cactus.org> jfh@rpp386.cactus.org (John F. Haugh II) writes: >In article <13499@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >>In article <40624@mips.mips.COM> sah@mips.COM (Steve Hanson) writes: >>>however the inc/dec cause a temporary variable to be introduced: >>That could be avoided by defining the function as >> void *memchr(const char *s, int c, unsigned int n) >>which works because of the "same representation" requirement. >Doesn't this defeat the purpose of (void *) as an abstraction >mechanism? Well, we were talking about a piece of the implementation, not a normal application (for which one could define the interface differently). In actuality, I expect most implementations of functions like this would be written in assembly language, not because C doesn't obtain a good result, but because customers are perceived as clamoring for the highest possible speed (in benchmarks, etc.), so it is deemed desirable from a marketing viewpoint. If you don't demand that the C version be utterly optimal, there is no need for the kludge I suggested.
rex@aussie.UUCP (Rex Jaeschke) (08/14/90)
Re the handling of void * in memchr, etc. Doug Gwyn writes: >That could be avoided by defining the function as > void *memchr(const char *s, int c, unsigned int n) >which works because of the "same representation" requirement. I like Doug's suggestion but it did raise another question. It's obviously useful to put a prototype in a header and include it at the site of every call. However, it's also a good idea to include it where the function is actually defined. If one uses Doug's approach, will the compiler cough or not? For example: void *memchr(const void *s, int c, unsigned int n); void *memchr(const char *s, int c, unsigned int n) { /*...*/ } I tried this on 6 DOS-based compilers all of which CLAIM to be either ANSI-conformant or VERY close and they came up 3/3 on whether the two function declarations were "compatible" (I'm not sure that's the correct term to use here though). Certainly void * and char * are required to have identical representation. However, the two types are different types. Should a REAL ANSI C compiler complain or not? Rex ---------------------------------------------------------------------------- Rex Jaeschke | Journal of C Language Translation | C Users Journal (703) 860-0091 | 2051 Swans Neck Way | DEC PROFESSIONAL uunet!aussie!rex | Reston, Virginia 22091, USA | Programmers Journal ---------------------------------------------------------------------------- Convener of the Numerical C Extensions Group (NCEG) ----------------------------------------------------------------------------
gwyn@smoke.BRL.MIL (Doug Gwyn) (08/14/90)
In article <43.UUL1.3#5077@aussie.UUCP> rex@aussie.UUCP (Rex Jaeschke) writes: >Certainly void * and char * are required to have identical >representation. However, the two types are different types. Should a >REAL ANSI C compiler complain or not? Yes, I think a diagnostic is required. That's why the implementation of memchr() along the lines I suggested should not include the standard header. Indeed, I wouldn't include the relevant header in most implementations of standard library functions, since it usually isn't either necessary or useful to do so.
karl@haddock.ima.isc.com (Karl Heuer) (08/16/90)
In article <13548@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >That's why the implementation of memchr() along the lines I suggested should >not include the standard header. Indeed, I wouldn't include the relevant >header in most implementations of standard library functions, since it >usually isn't either necessary or useful to do so. I would consider it bad style to take advantage of the same-representation clause like that; I believe it was added to grandfather in the pre-ANSI code that contained explicit declarations for functions like malloc(). I would simply implement memchr() using `void *', with casts as appropriate. It's a minor wart. For memchr(), you want the header anyway in order to get the definition of size_t. (You could get it from some other header, but why go out of your way to be difficult?) Even if this were not the case, I'd include it just to let the compiler confirm that the prototype in the header agrees with that in the source. Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
seanf@sco.COM (Sean Fagan) (08/16/90)
In article <17416@haddock.ima.isc.com> karl@kelp.ima.isc.com (Karl Heuer) writes: >In article <13548@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >>Indeed, I wouldn't include the relevant >>header in most implementations of standard library functions, since it >>usually isn't either necessary or useful to do so. And, of course, don't forget that the implementor can use whatever non-standard extensions in the library source that she or he wants to. Such as using gcc's extension that has sizeof(*(void *)) == 1. That would eliminate the need for casts for things like malloc, memcpy, etc. Karl *did* make a good point, that I deleted, about using the "standard" include files to make sure the prototypes agree. But remember that the libraries might not be writtin in C at all, and the prototypes either directly copies from the standard (with, hopefully, the parameter names removed 8-)), or generated by some utility. Just my $0.02 (Canadian). -- Sean Eric Fagan | "let's face it, finding yourself dead is one seanf@sco.COM | of life's more difficult moments." uunet!sco!seanf | -- Mark Leeper, reviewing _Ghost_ (408) 458-1422 | Any opinions expressed are my own, not my employers'.
gwyn@smoke.BRL.MIL (Doug Gwyn) (08/16/90)
In article <17416@haddock.ima.isc.com> karl@kelp.ima.isc.com (Karl Heuer) writes: >I would simply implement memchr() using `void *', with casts as appropriate. If you recall the original complaint, you should see that casts do not solve the problem under discussion.