Is incompatible pointer assign necessary to implement polymorphism in C

后端 未结 1 1666
后悔当初
后悔当初 2021-01-21 11:37

I try to simulate C++\'s polymorphism in C with following code:

#include

typedef struct Base {
  void (*out) (void);
} Base;

typedef struct Deri         


        
相关标签:
1条回答
  • 2021-01-21 12:07

    Incompatible pointer assignment is not necessary. Per C 2011 [draft N1570] 6.7.2.1 15, “A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.” Thus, when the first element of Derived is a Base object, a pointer to Derived may be converted to a pointer to a Base. And a pointer to a Base that is the first member in a Derived may be converted to a pointer to Derived.

    So this is legal. The compiler is likely warning you about it just to be safe, since it is unusual. Using an explicit cast may avoid that:

    p = (Base *) &d;
    

    Alternatively, you can take the address of the base directly:

    p = &d.base;
    

    However, going from a Base back to a Derived will still require a cast:

    Derived *x = (Derived *) p;
    

    As for putting the base somewhere other than as the first member, then you are on shakier ground. The derived-to-base direction is still easy; p = &d.base is fine. To go the other way, this may be as close as you can get to legal C:

    1. Get a character-type pointer to the base: char *c = (char *) p. In C, it is legal to convert any pointer to an object to a pointer to a character type, and this gives you a pointer to the first byte of the object.

    2. Subtract the number of bytes from the beginning of a Derived to the Base member: c -= offsetof(Derived, base). (This requires including <stddef.h>.) Since c points to the first byte of a Base in Derived, it points to a byte in Derived, and you are mostly (see below) allowed to treat Derived as an array of bytes, so we can subtract to get back to the beginning. The offsetof macro provides the number of bytes for that.

    3. Convert the character-pointer to a pointer to Derived: Derived *x = (Derived *) c. Here, we are on shaky ground. If we converted a pointer to Derived to a character pointer, we are allowed to convert it back immediately. But we did not get this pointer in that way. It points to the same byte, but it is not clear to me that the C standard supports this. (Actually, step 2 above has a similar problem; an object can be interpreted as an array of characters, but we are coming at it from a direction that I am not sure is actually supported by the C standard.)

    Putting these together, the conversion would be:

    Derived *x = (Derived *) ((char *) p - offsetof(Derived, base));
    

    Given the uncertainties in support, I would use this only in experimental code or as a classroom exercise, unless I had guarantees from the C implementation I was using that this pointer arithmetic is supported. It would be preferable to stick with the base as the first element, as those conversions are strictly conforming C.

    0 讨论(0)
提交回复
热议问题