开发者

C: Array with elements varying in size

Is it possible to have an array of elements (structs) where the values vary in size?

The issue I am faced with is that I do not know how to access an element since it requires a cast. The "array" just contains pointers to the 开发者_如何学Pythonstructs. Therefore I have decided to go with void**. As not each of the elements is of the same type, the type needs to be stored in the array as well so that I would know what to cast to without making rough guesses. This does not sound very efficient though. Is there not a more efficient way?


No, in C each element of an array have to be of the same type (and thus the same size as well).

You might need to add another layer of abstraction, in this case you need to annotate the elements with the type it has e.g. you could make an array of structs that look like:

enum ElemType {
  TypeNull,
  TypeFoo,
  TypeBar
];

struct Elem {
 enum ElemType type;
 void *realElem;
};

You'd have to update the 'type' with the actual type you insert, and make decisions on that when you read the array, and you store a pointer to the actual element in the 'realElem' member.

struct Elem arr[42];
...
switch(arr[k].type) {
  case TypeFoo:
     handleFoo(arr[k].realElem);
     break;
  ...
}


If you don't like casts, you can always used unions ( and a flag to indicate which type the union should be interpreted as )

#include <stdio.h>

typedef struct {
    int a;
} A;

typedef struct B {
    double b;
} B;

typedef struct C {
    char c[20];
} C;

typedef enum {
    TypeA,
    TypeB,
    TypeC,
} type;

typedef struct {
    type type;
    union { A*a; B*b; C*c; } p;
} TypedPointer ;

void foreach (TypedPointer* list, void (*fn)(TypedPointer))
{
    while (list->p.a) {
        fn(*list);
        ++list;
    }
}

void print_member (TypedPointer ptr)
{
    switch (ptr.type) {
        case TypeA: printf("A (%d)\n", ptr.p.a->a); break;
        case TypeB: printf("B (%f)\n", ptr.p.b->b); break;
        case TypeC: printf("C (%s)\n", ptr.p.c->c); break;
    }
}

int main ()
{
    A a = { .a = 42 };
    B b = { .b = 1.01 };
    C c = { .c = "Hello World!" };

    TypedPointer ptrs[] = {
        { .type = TypeA, .p.a = &a },
        { .type = TypeB, .p.b = &b },
        { .type = TypeC, .p.c = &c }, 
        { .type = 0, .p.a = 0} };

    foreach(ptrs, print_member);

    return 0;
}


No, C arrays have a constant size that must be known at compiletime. But you can go around this by using pointers and dynamic allocation.

The problem here is not efficiency as much as as it is type safety. Casting has no runtime performance cost, it's only a mechanism for talking to the compiler, ask it to "trust you" and assume there's a certain type behind a sequence of bits. We're talking about safety because if you're wrong then anything at all can happen in runtime.

It is impossible in C (a static language) to construct an array of different types. What you can do is construct an array of a certain struct but have that struct hold pointers to objects of varying sizes. For instance, have the struct hold a char* pointer, in which case each object holds a string of one length or another and thus have a variable size (in terms of total memory used in runtime, not in terms of contiguous space used).

If you want each of these objects to behave differently then others then you can emulate polymorphism by using an array of function pointers and then add a function pointer to the struct that points to any of the functions as necessary.


Casts are not inefficient since they just tell the computer how to interpret the region of memory. When they are compiled to assembly, the compiler will create assembly that interprets the region of memory as needed.


For this, we use an array of pointers to the actual objects.

The pointers are all the same size.

The objects to which they point can be different sizes.

A "discriminated union" works well for this.

typedef struct { ... } this_type;
typedef struct { ... } that_type;
typedef struct { 
     int subtype;
     union {
         this_type this;
         that_type that
     }
} discriminated_union;

An array pointers to discriminated_union works out well.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜