Path: utzoo!utgpu!news-server.csri.toronto.edu!mailrus!cs.utexas.edu!usc!zaphod.mps.ohio-state.edu!think!mintaka!ogicse!zephyr.ens.tek.com!tekfdi!videovax!bart From: bart@videovax.tv.tek.com (Bart Massey) Newsgroups: comp.lang.c++ Subject: Re: Re: references to dereferenced null pointers, etc... Message-ID: <5763@videovax.tv.tek.com> Date: 16 Mar 90 19:25:15 GMT References: <1520025@hpmwjaa.HP.COM> Reply-To: bart@videovax.tv.tek.com (Bart Massey) Organization: Tektronix TV Measurement Systems, Beaverton OR Lines: 108 In article <1520025@hpmwjaa.HP.COM> jeffa@hpmwtd.HP.COM (Jeff Aguilera) writes: > > The following pointer nightmares really bother me. > > I am a fairly defensive programmer. So when I could not determine > whether strlen() was portably defined for a null argument, I rolled > my own, just to thwart unwanted surprises: > > ptrdiff_t myStrlen(const char* p) > { > register const char* q = p; > if (q) while (*q) ++q; > return q-p; > } > > Alas! This is not portable. It's not terribly useful, either. There's really no reason to write your own implementation of strlen() for this, since the system strlen() may well be written in assembly and blindingly faster than yours. Instead, why don't you try /* warning: evaluates S twice */ #define myStrlen(S) ((S) ? strlen((S)) : 0) or if you really want to be able to write myStrlen(s++) ptrdiff_t myStrlen(const char *p) { if( !p ) return 0; return strlen( p ); } or even, if on your machine the function call to strlen() is ludicrously expensive, and you just can't stand it ptrdiff_t myStrlen(const char *p) { register const char *q; if( !p ) return 0; for(q = p; *q; q++) /*do nothing*/; return q-p; } which really doesn't generate any more instructions than your nonportable version in any case, and is *much more* efficient in the case that p == 0! > The other nightmare is implementing an efficient and portable > memmove, or, equivalently, whether a given pointer aliases a > member of a known array. > > Suppose char* p points to a malloc'ed arena of length N. Thus, > p[0] through p[N-1] are valid lvalues, and generating p[N] is > guaranteed not to dump core, as long as it is not accessed. > How can we portably determine whether there exists a k, 0<=k such that p+k==q? Mathematically, we are groping for the > following algorithm: > > int alias(const char* q, const char* p, int N) > { > int k=q-p; > return 0<=k && k } > ... > Right? No. The only portable solution that I can think of > is O(N) time complexity: > > int alias(const char* q, const char* p, int N) > { > for (register int k=0; k return 0; > } I don't think so. Even your O(N) solution is non-portable: if p and q are in different "segments" on a segmented machine, they are allowed to compare *equal* even if they point to different objects, since the result of the comparison is undefined [ANSI C Draft 12-7-88 3.3.8,3.3.9]! In short, ANSI C restricts pointer comparisons to (a) comparisons between pointers to a single "object", and (b) comparisons with the pointer constant 0, which indicates an invalid location. Thus, there is no way to write a legal portable "alias" function in ANSI C. But this is just fine -- there was no way to write an even vaguely portable general purpose alias function in K&R C, either! Note that the results of such comparisons are "undefined" by the spec -- the comparisons are not *illegal*. Thus, if you are writing memmove() for a traditional machine with a linear address space, you would probably write a variant of your first function anyway, "knowing" that your compiler will do the right thing in this case. All you have proved is that memmove() implementations are non-portable, which everybody already knew -- that's why it's in the standard ANSI C library. IMHO, the restrictions of the ANSI draft are a very nice compromise between implementability of C on machines with bizarre pointers, and standard C pointer semantics on traditional machines. > I want to vomit. [Insert some obvious wisecracks here... :-) :-) :-)] Bart Massey ..tektronix!videovax.tv.tek.com!bart ..tektronix!reed.bitnet!bart