Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!coherent!dplatt From: dplatt@coherent.com (Dave Platt) Newsgroups: comp.sys.mac.programmer Subject: Re: Stale Pointers Keywords: Lightspeed C Message-ID: <27101@coherent.com> Date: 28 Jun 89 22:42:33 GMT References: <2394@ur-cc.UUCP> Reply-To: dplatt@coherent.com (Dave Platt) Distribution: usa Organization: Coherent Thought Inc., Palo Alto CA Lines: 73 In article <2394@ur-cc.UUCP> giaccone@uhura.cc.rochester.edu writes: > > In my case I was trying to allocate memory for a bitmap image, and > I stored that Pointer in a structure pointed to by a handle. The C > source code looks like this: > > (**myImage).rasI_image = (SIPtr) NewPtr(size); (....) > > Now, this code works fine the first time I execute it, I because the > heap doesn't get shuffled. However, the next time I try to execute it, a > serious problem occurs.... > > Now obviously the simple work around is to lock the block pointed to by > myImage before I make the call to NewPtr (and in fact that is what I do). > Still it seems odd that Lightspeed C should do part of the dereference > before the call, and the other part after the call. What do you think? This is a classic "problem"... or, if you wish, a wll-known ambiguity in C. The C language as defined by K&R does not specify the order in which intermediate results are to be computed in many cases. If, for example, a statement contains the expression: (foo(a) + bar) * (foo(c) + d) then the compiler may issue the two calls to "foo" in either order. Similarly, in the case of the NewPtr() call you've issued above, the compiler must evaluate two intermediate results before performing the final assignment. One intermediate result is the value of "NewPtr(size)"; the other is the value of "*myImage". This may be a bit clearer if we rewrite your assigment in the entirely-equivalent form: (*myImage)->rasI_image = (SIPtr) NewPtr(size); Because C does not specify the order in which intermediate values are to be calculated, one must be very cautious about "doing things" that can cause side-effects. In your case, the call to NewPtr() can have a side-effect... memory gets shuffed. If this happens, and if the newPtr() call occurs after the calculation of *myImage, then the side-effect of the memory shuffling invalidates the value of *myImage, and (as you've noticed) the whole world collapses messily. There are two solutions: one acceptable, and one better. The acceptable solution is the one you've proposed: lock the block to which myImage is a handle; this will prevent the objectionable side-effect from occurring. The better solution is to use two statements: tempPtr = (SIPtr) NewPtr(size); (**myImage).rasI_image = tempPtr; This approach is cleaner, because it explicitly specifies the order-of-evaluation and thus avoids the ambituity in C entirely. It's more CPU-efficient, because it's not necessary to spend cycles calling HLock and HUnlock (you weren't even _thinking_ of diddling the lock bit in the handle directly, now were you? ;-). It's also more likely to work if your application is running low on memory; the Memory Manager will be free to move the block referred to by myImage if it needs to make room for the new nonrelocatable block. I'd be somewhat chagrined to admit the number of times I've forgotten about this ambiguity in C and have written "allocate me a foo" code that broke when heap-compaction occurred. Using TMON's heap scrambling code is a sure cure for this sort of bug, though. -- Dave Platt FIDONET: Dave Platt on 1:204/444 VOICE: (415) 493-8805 UUCP: ...!{ames,sun,uunet}!coherent!dplatt DOMAIN: dplatt@coherent.com INTERNET: coherent!dplatt@ames.arpa, ...@uunet.uu.net USNAIL: Coherent Thought Inc. 3350 West Bayshore #205 Palo Alto CA 94303