Dereferencing pointer to array of void
I am attempting to learn more about C and its arcane hidden powers, and I attempted to make a sample struct containing a pointer to a void, intended to use as array. EDIT: Important note: This is for raw C code.
Let's say I have this struct.
typedef struct mystruct {
unsigned char foo;
unsigned int max;
enum data_t type;
void* data;
} mystruc开发者_高级运维t;
I want data to hold max of either unsigned chars, unsigned short ints, and unsigned long ints, the data_t enum contains values for those 3 cases.
enum Grid_t {gi8, gi16, gi32}; //For 8, 16 and 32 bit uints.
Then I have this function that initializes and allocates one of this structs, and is supposed to return a pointer to the new struct.
mystruct* new(unsigned char foo, unsigned int bar, long value) {
mystruct* new;
new = malloc(sizeof(mystruct)); //Allocate space for the struct.
assert(new != NULL);
new->foo = foo;
new->max = bar;
int i;
switch(type){
case gi8: default:
new->data = (unsigned char *)calloc(new->max, sizeof(unsigned char));
assert(new->data != NULL);
for(i = 0; i < new->max; i++){
*((unsigned char*)new->data + i) = (unsigned char)value;
//Can I do anything with the format new->data[n]? I can't seem
//to use the [] shortcut to point to members in this case!
}
break;
}
return new;
}
The compiler returns no warnings, but I am not too sure about this method. Is it a legitimate way to use pointers?
Is there a better way©?
I missed calling it. like mystruct* P; P = new(0,50,1024);
Unions are interesting but not what I wanted. Since I will have to approach every specific case individually anyway, casting seems as good as an union. I specifically wanted to have much larger 8-bit arrays than 32-bits arrays, so an union doesn't seem to help. For that I'd make it just an array of longs :P
No, you cannot dereference a void*
pointer, it is forbidden by the C language standard. You have to cast it to a concrete pointer type before doing so.
As an alternative, depending on your needs, you can also use a union
in your structure instead of a void*
:
typedef struct mystruct {
unsigned char foo;
unsigned int max;
enum data_t type;
union {
unsigned char *uc;
unsigned short *us;
unsigned int *ui;
} data;
} mystruct;
At any given time, only one of data.uc
, data.us
, or data.ui
is valid, as they all occupy the same space in memory. Then, you can use the appropriate member to get at your data array without having to cast from void*
.
What about
typedef struct mystruct
{
unsigned char foo;
unsigned int max;
enum data_t type;
union
{
unsigned char *chars;
unsigned short *shortints;
unsigned long *longints;
};
} mystruct;
That way, there is no need to cast at all. Just use data_t
to determine which of the pointers you want to access.
Is type
supposed to be an argument to the function? (Don't name this function or any variable new
or any C++ programmer who tries to use it will hunt you down)
If you want to use array indices, you can use a temporary pointer like this:
unsigned char *cdata = (unsigned char *)new->data;
cdata[i] = value;
I don't really see a problem with your approach. If you expect a particular size (which I think you do given the name gi8
etc.) I would suggest including stdint.h
and using the typedefs uint8_t
, uint16_t
, and uint32_t
.
A pointer is merely an address in the memory space. You can choose to interpret it however you wish. Review union
for more information on how you can interpret the same memory location in multiple ways.
casting between pointer types is common in C and C++, and the use of void* implies that you dont want users to accidentally dereference (dereferencing a void* will cause an error, but dereferencing the same pointer when cast to int* will not)
精彩评论