I'm developing some code in C and binding it to R. In the C code, I define a structure as
typedef struct motion_info_structure {
int motion_name;
int personalized;
int rep_counting_channel;
int rep_boundary_type;
int end_detection_channel;
VEC *end_detection_waveform;
} motion_info_container;
(VEC is a data type provided by a matrix math library called Meschach. It's a simple container for an array of real values:
typedef struct {
unsigned int dim, max_dim;
Real *ve;
} VEC;
Real is either float or double depending on the configuration. Meschach provides dynamic memory allocation functions to bundle allocation and deallocation of the Real vector and the container structure together into a single call.)
In my code, I define a global array of motion_info_containers (to be initialized on start and treated as immutable thereafter).
motion_info_container motion_info[N_SUPPORTED_MOTIONS];
I bound my initialization function to R; then for testing purposes I wrote a function to copy the data out of the C global variable and dump them to R. The initialization function itself takes a VEC* and just assigns it directly with no copying; the R binding code does the VEC creation dynamically. (The glue code that creates VECs dynamically and fills them with the values in double arrays passed from R is well-tested and reliable.) It seemed to work just fine.
A little bit later on I decided to expand the definition of the structure to:
typedef struct motion_info_structure {
int motion_name;
int personalized;
int rep_counting_channel;
int rep_boundary_type;
VEC *end_detection_min_threshold; // new VEC* member
VEC *end_detection_max_threshold; // new VEC* member
int end_detection_channel;
VEC *end_detection_waveform; // old VEC* member
} motion_info_container;
In storing and retrieving data, I treat the new VEC* members in exactly the same fashion as the old VEC* member. But somewhere after the end of the initialization function invocation and before or during the retrieval function invocation, the data in the Real members of the new VEC* members gets zeroed out! And only the new members are affected; the data in the old member is still intact and retrievable. (I used printf to verify that the initialization seems to work correctly while within the initialization function, and to verify that the pointer doesn't change the memory address it points to during the test.)
Utterly baffled, I had a vague notion that maybe this bizarre behavior was somehow related to the fact that "end_detection_waveform" is at the end of the structure definition; so I changed the order of the structure members.
typedef struct motion_info_structure {
int motion_name;
int personalized;
int rep_counting_channel;
int rep_boundary_type;
int end_detection_channel;
VEC *end_detection_waveform; // changed the order
VEC *end_detection_min_threshold;
VEC *end_detection_max_threshold;
} motion_info_container;
After making this change, attempting to initialize from R causes an instant crash (which is typical R behavior when C code bound to R corrupts memory somehow).
How is it possible that the code seems to work correctly when the structure contains a single VEC* member but misplaces data, or crashes outright, when new VEC* members are added?
Aucun commentaire:
Enregistrer un commentaire