Advertisement

Computing properly aligned pointer values

November 01, 2009

Dan_Saks-November 01, 2009

A couple of months ago, I explained a technique that C++ run-time systems may use to dynamically allocate and deallocate arrays whose elements are class objects with constructors and destructors.1 I also showed how you can implement essentially the same behavior in C by writing your own array allocation and deallocation functions.

I ended that column by explaining that my implementation of those C functions will work just fine on any processor in which no type has an alignment stricter than that of size_t. However, it could produce undefined behavior on any machine with more strictly aligned types. This month, I'll explain why, and what you can do about it.

Rewinding just a little
As in my previous column, I'll illustrate the problem by using arrays of widgets. In C++, widget is a class defined with a default constructor, a destructor, and an assortment of other members (both functions and data). In C, a widget is a struct:

typedef struct widget widget;
struct widget
    {
    // ...
    };

along with functions that serve as the constructor and destructor:

void widget_construct(widget *pw);
void widget_destroy(widget *pw);

as well as other "member" functions.

In C++, you use an array new-expressions such as:

pw = new widget [n];

to allocate an array with n properly initialized widgets. In C, you can emulate the behavior of this array new-expression by implementing a function named new_widget_array and calling it in an expression such as:

pw = new_widget_array(n);

In C++, you use an array delete-expression such as:

delete [] pw;

to deallocate an array previously allocated by an array new-expression. In C, you can mimic the behavior of this array delete-expression by implementing a function named delete_widget_array and calling it in an expression such as:

delete_widget_array(pw);

Whereas the array dimension appears explicitly in each array new-expression, it never appears in an array delete-expression. Each array delete-expression has to obtain the array dimension another way. A common technique for passing the array dimension to the delete-expression is for the array new-expression to stash the array dimension in a location just before the array itself. Last month, I explained this technique by showing implementations of new_widget_array and delete_widget_ array that use it.

Inasmuch as the additional allocated storage holds an array dimension, it should be declared as type size_t, the same as the parameter to new_widget_array. Thus, new_widget_array should allocate storage for the array dimension along with the array itself using:

size_t size = sizeof(size_t) + n * sizeof(widget);
size_t *ps = (size_t *)malloc(size);

If ps is non-null (because malloc has returned a pointer to the requested storage), then new_widget_array can proceed to place the array dimension at the beginning of that storage:

*ps = n;

and then compute the address of the first element in the array itself:

++ps;

The allocated array is an array of widgets, but ps is a pointer to a size_t, so new_widget_array needs a cast (as part of an assignment) to obtain a pointer it can use to access the initial array element:

pw = (widget *)ps;

In my implementation of new_widget_array, I combined the increment and the assignment:

++ps;
pw = (widget *)ps;

into one expression:

pw = (widget *)++ps;

In the following discussion, it'll be easier to discuss the behavior if I keep the increment and assignment as two separate expressions.

< Previous
Page 1 of 4
Next >

Loading comments...