Letters to the editor may be sent via email to cujed@mfi.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.
Dear Editor,
Just wanted to "call in" and say: Keep up the good work.
Tom Nelson's article just happened to be especially timely for me I've got the responsibility for overlaying persistence on another developer caching scheme.
I like knowing that Dr. Plauger maintains his association with CUJ.
I like the fact that you publish "code reviews" that provide responsible criticisms or alternative viewpoints to previously published articles.
Sincerely,
Richard Muller
Dear Marc,
I found the Code Review on the CUJ website to be very interesting. ("A Quick and Simple Memory Allocator, by Vladimir Batov, CUJ, January 1988.) As I do not get the magazine until about three months after publication I sometimes miss out on what is happening. I have tried similar memory allocation schemes in the past. The third point is of particular interest. However when I try to look at Listing 3 and 4 I am told my browser is unable to do so. "The link you followed is either outdated, inaccurate, or the server has been instructed not to let you have it." I would appreciate a copy of the listings. It may also be benefical to make the code review source avaliable as a zip file for download.
John Grabner
Auckland, New ZealandThanks to both of you for visiting our Code Review web page (http://www.cuj.com/forum/). We haven't blabbered much about it up to now because it's still in the experimental phase. The purpose of the Code Review is to let our readers "review" code and techniques presented in the magazine, to discuss possible shortcomings, offer improvements, or provide related solutions. We intend to develop the page further I would appreciate any suggestions from readers as to what features they'd like to see there or how it ought to be structured (mbriand@mfi.com).
With respect to Listings 3 and 4: According to Ivan Jones, our webmaster, the access problem has been fixed. I like your idea, though, of providing the code via ftp. It is now available at ftp.mfi.com/pub/cuj/webzip/codrev1.zip. mb
Dear CUJ,
I write to applaud the article by V. Batov ("A Quick and Simple Memory Allocator," CUJ, January 1998). I appreciate how the author demythicizes memory allocation, easily achieving a performance boost.
The Limitations and Extensions section does not stress that building a general purpose class would cost in terms of performance, and that the cost is that order of magnitude presented in the preceding section (Testing). IMHO, such a kind of note would add incisiveness to the meaning of the article.
Nowadays many talk about garbage collection and other features that will have to be paid even by programs that don't use them. The allocation scheme presented in the article is probably the best one can get for a number of programs. And it only requires the effort of declaring the program's intentions, in the true spirit of C.
Keep up the good work!
Alessandro Vesely
Thanks for the comments. If you are writing in response to Simon Simeonov's letter in the last month's "We Have Mail" section, you'll know that while Vladimir Batov came close to meeting his objectives in his first implementation, there were a few problems with it. See our Code Review page, described immediately above. mb
Mr. Plauger,
I'm hoping to accomplish two objectives with this letter. First I would like to thank you for your continued excellent work as Senior Editor of the C/C++ Users Journal. It is an extremely rare month that I do not find the magazine to be a source of accurate, timely information that can be used in my work the very next day. My subscription has been one of the best educational investments I have ever made. Thank you.
Secondly, I was hoping you could clear up a small interpretation issue with the C Standard; or point me to someone who can.
The area in which I work develops embedded C applications for use in automotive audio systems. Recently we have begun working with a new microprocessor and compiler vendor. For obvious reasons, I would rather not identify them. The microprocessor that they are supplying has a limited number of operations in its instruction set that allow manipulation of the system stack pointer. As a result of this, the only compiler available for the micro uses an "overlap" segment of global RAM to store local variables and to pass parameters. The system stack is not used for either of these operations.
At link time, the linker generates a call tree for the entire application This information is then used to allocate addresses in the overlap segment to local variables and parameters. The call tree information is used in such a way as to insure that the local variables of a function never destroy the locals of its calling function. Storage for locals will be shared between functions, but not between two functions in the same branch of the call tree.
This has some obvious implications. Functions are not reentrant. The compiler does not support recursion. And, since interrupts are asynchronous, their service routine call trees must be completely disjoint from the foreground logic call tree. Otherwise, a function called from a service routine could destroy the local variables of a previous instance of itself, if it is called from the foreground logic.
I am maintaining that this behavior violates the reentrancy requirement of section 5.2.3. The compiler vendor disagrees, claiming that a hardware interrupt is not the same as a signal. And that a signal handler is not the equivalent of a interrupt service routine. I'm pretty certain that I am correct on this issue, but I would like a more "official" interpretation of section 5.2.3 than my own to help support my case.
Thank you for your time and any answer you may be able to give.
David P. Wright
Visteon Automotive Systems
dwrigh11@ford.comReentrancy is indeed required by the C Standard. A vendor can argue that interrupt handling is beyond the scope of the C Standard it certainly doesn't have to behave like a signal handler but any implementation that gets reentrancy right should also handle interrupts properly as well. I think you have a right to be disappointed. pjp
Dear PJP,
I am currently studying C++ at Wesleyan college and recently we did a lab project demonstrating the use of pointers, constructors, destructors, memory allocation, and deallocation using new and delete. The code for the project is shown in Listing 1.
My question concerns the deallocation of memory using MicroSoft Visual C++. When the above code is compiled using MS C++, there is no error; However at run time a debug assertion error is generated which points to the deallocation:
delete [] messageThe same code compiled on Borland C++ version 4.02 did not generate any errors at compile time or run time.
Can you determine what is causing the error using MS VC++.
Thanks for a great magazine,
Rick Cummings
The problem arises when you assign the string literals to message. All such an assignment does is replace one stored pointer with another. No characters get copied. Thus, the assignment in the constructor destroys the pointer to allocated storage, rather than copy the literal into the allocated storage as you obviously intend. The assignment in the destructor is equally ineffectual. The delete statement is then asked to free storage for a string literal. This is undefined behavior, so Borland C++ is permitted to stumble on in silence. But VC++ is doing you a favor by diagnosing the improper deletion. Your best bet is to use string objects instead of string literals, since they handle storage management for you automatically. pjp
Dear CUJ,
I liked the non-zero based Array class in "Implementing Pascal Data Types in C++" (CUJ, February 1998). Non-zero based arrays are often very useful in numerical algorithms. Here's a trick I learned from Numerical Recipes in C that makes using non-zero based arrays just as efficient in C (and C++) as normal zero based array. I'd implement the first Array class as shown in Listing 2.
Note that the constructor subtracts the low bound when the memory is allocated and the pointer to it is initialized. Just be sure you add the low bound back before attempting to delete the memory (like Array's destructor does). Now, operator [] can simply return value[i] (after checking the bounds).
The second Array class you presented, which templatizes the subrange, is inherently more efficient (memory for the array is allocated automatically rather than from the heap), and a good C++ compiler should be able to reduce the return of value[i-S::low] to code which is just (or at least nearly) as efficient as a simple zero-based index. Changing the return value to (value-S::low)[i] would probably guarantee the more efficient code even if the compiler wasn't very smart.
Randy Weems
Electronic Product Information Corp.
randy@epicsoft.comBrian Campbell replies:
More novice programmers may not understand this, but experienced C/C++ programmers will see that in the statement labeled //1 below:
template <class T> class Array { int low, high; T *value; public: Array(int l, int h) : low(l), high(h) { value = new T[high-low+1] - low; //1you are making the pointer value point to an imaginary start of the array at low number of elements below the physical array (or above if low is negative). This is a neat trick which moves the subtraction of the lower bound to a one-time occurrence within the constructor, so that it doesn't have to happen at every array access:
T & operator [](int i) { if (i >= low && i <= high) return value[i]; // no subtraction requiredWithin your code you also pose the question:
else { // why not just throw an exception here?You are not the first to ask about this. The simple answer is that I did not want to complicate my examples with an additional subject. I plan to elaborate on this on my web page soon. See http://home.att.net/~brian.campbell/
cuj.htm. I have a few other user queries and answers there already.I also think you made some good points about the second Array class being more efficient. The latter pointer subtraction is more straightforward IMHO, and subtraction that the compiler can do (vs. run-time subtraction) because S::low is a compile time constant. Whether it actually does the subtraction, may depend on the compiler architecture. For example, each object in a block is probably addressed as an offset within a function's stack frame. So it depends on whether the compiler is willing to combine the stack offset and byte subtraction into a new modified offset. However an optimizer may not see the stack offset as anything special, and may merely see two constants as being subtracted, do the subtraction, and give the result you suggested.
Brian Campbell
brian.campbell@worldnet.att.net
http://home.att.net/~brian.campbell