Thanks to everyone for their answers. It was gratifying to get so
many responses so quickly. Having spent another night mulling this
over, I have some more thoughts/questions, and will phrase them in
terms of Arthur's code:
"Arthur J. O'Dwyer" <aj*@andrew.cmu.edu> writes:
Obviously the other answers you've received are far better than this
"suggestion," so please don't use it in production code; but I see no
reason that this [untested code] wouldn't work.
extern void process(double);
extern struct _mystruct my_array[42];
extern int num_structs;
int i;
double *p = &(my_array->d);
for (i=0; i < num_structs; ++i)
{
process(*p);
p = (double *) ((unsigned char *)p + sizeof(struct _mystruct));
}
This is very similar to my original approach, except that you're
storing the pointer as a double* and then casting it as a char* to
move it (whereas I always stored as a char* and cast as a double* to
access it...). Unfortunately the cast to char* doesn't satisfy my
query since I was hoping to get rid of all references to char*...
That said, I'm in the same boat as you: I don't see why this type of
approach would not work. Tak-Shing Chan's response seems to assert
that such approaches aren't guaranteed to work (though in fact, in
practice it has on the dozens of platforms I've used, from desktop
machines to supercomputers). Could someone give a less elusive
explanation of why it isn't guaranteed to work? In particular, I'd be
curious to know why such an approach might fail due to the language
standard, as well as what would cause it to fail in practice with
actual compilers (since it never has that I've observed).
For those interested in continuing to pursue this conversation, I've
also remembered a second reason why I was uncomfortable with the
solution that walked a "pointer-to-struct" through memory,
dereferencing it to access a field at a time. The situation requires
that you understand that I have a "multidimensional array descriptor"
of sorts, a simplified version of which appears below:
struct _array {
void* data; /* pointer to data buffer */
int numdims; /* number of conceptual dimensions */
int block[MAXDIM]; /* per-dim multipliers in bytes */
} array;
(the real descriptor has additional fields supporting non-0 origins,
strided arrays, etc.)
For example, to initialize an n x n array of doubles, I would do
something like the following:
array mydblarray;
mydblarray.data = malloc(n*n*sizeof(double));
mydblarray.numdims = 2;
mydblarray.block[0] = sizeof(double);
mydblarray.block[1] = n * mydblarray.block[0];
Then, I can create functions that operate on these arrays. For
example, the following code zeros out the first k x k elements in a 2D
array of doubles (assuming the array size is >= k in each dim).
void zero_kxk_dbl_arr(array* dblarr, int k) {
int i, j;
char* wlk = dblarr->data;
bump_j = dblarr->block[0];
bump_i = dblarr->block[1] - (k*(dblarr->block[0]));
for (i=0; i<k; i++) {
for (j=0; j<k; j++) {
*(double*)wlk = 0.0;
wlk += bump_j;
}
wlk += bump_i;
}
}
Now, the nice thing about this approach is that I can resuse this code
for a double field in an array of structs by fiddling with the array
descriptor slightly. For example:
/* a struct with a double field */
typedef struct _mystruct {
short x;
double y;
} mystruct;
/* intialize an array of structs */
array mystructarray;
mystructarray.data = malloc(n*n*sizeof(mystruct));
mystructarray.numdims = 2;
mystructarray.block[0] = sizeof(mystruct);
mystructarray.block[1] = n * mystructarray.block[0];
/* spoof array using y field from mystructarray */
array tmparray = mystructarray
tmparray.data += CALC_OFFSET(y);
/* pass spoofed descriptor in as loosely strided double array */
zero_kxk_dbl_arr(tmparray, 5);
This has been a nice property for code reuse, in my experience
(especially given the fact that I'm not writing all of this code by
hand, but am generating it automatically from a higher-level array
language). Yet it relies on the char* approach since (as we learned
yesterday) we can't make assumptions about the distance between fields
in two adjacent structs. Furthermore, in this case, I can't use a
struct walker since the context in which the struct's field is being
utilized is unaware of the struct's existence.
So I guess my questions here are two: (1) Does anyone have other
suggestions for how to approach this without char*s, yet without
giving away the code reuse flexibility that they result in? and (2)
I'm sure someone will tell me that what I'm doing is illegal in C, and
I'm willing to believe that but I don't understand it. Why is it?
(probably same reason as Arthur's code is). Again, I'd be curious to
hear both why it's illegal according to the standard even though it
generally seems to work in practice, as well as how it would fail in
practice on real compilers (since I haven't observed that anywhere).
Thanks again for all the answers, I really do appreciate the information
I've been getting,
-Brad