Приведение указателя структуры к типу, который лежит первым полем в структуре

Допустим, у меня есть некая структура, так называемая структура для подсчета ссылок:

#ifndef LIBKODIK_API_OBJECT_BASE
#   define LIBKODIK_API_OBJECT_BASE             \
uint32_t                        i_refs_count;   \
struct libkodik_api_object_t    *p_parent       \

#endif /* !LIBKODIK_API_OBJECT_BASE */

typedef struct libkodik_api_object_t {
    LIBKODIK_API_OBJECT_BASE;
} libkodik_api_object_t;

uint32_t
libkodik_api_object_retain(libkodik_api_object_t *p_object);

uint32_t
libkodik_api_object_release(libkodik_api_object_t *p_object);
uint32_t
libkodik_api_object_retain(libkodik_api_object_t *p_object) {
    libkodik_api_object_t *p_current = p_object;
    while (NULL != p_current) {
        p_current->i_refs_count++;
        p_current = p_current->p_parent;
    }
    return p_object->i_refs_count;
}

uint32_t
libkodik_api_object_release(libkodik_api_object_t *p_object) {
    libkodik_api_object_t *p_current = p_object;
    while (NULL != p_current) {
        p_current->i_refs_count--;
        p_current = p_current->p_parent;
    }
    return p_object->i_refs_count;
}

И есть другая структура, в которой первым полем лежит libkodik_api_object_t:

struct kodik_api_countries_t {
    libkodik_api_object_t s_api_base;
    ...
};

Могу ли я безопасно вызывать libkodik_api_object_retain, libkodik_api_object_release делая простой каст указателя на другой тип струкруты?

libkodik_api_object_retain((libkodik_api_object_t *) p_countries_api);
libkodik_api_object_release((libkodik_api_object_t *) p_countries_api);

P.S. libkodik_api_object_release не освобождает память, а просто уменьшает кол-во ссылок на 1.


Ответы (1 шт):

Автор решения: eanmos

Да, согласно стандарту (§ 6.5 / 7)

(7) An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

А также (§ 6.7.2.1 / 15)

(15) Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. 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. There may be unnamed padding within a structure object, but not at its beginning.

Другими словами, указатель на структуру может быть приведен к указателю на первый элемент этой структуры и это не будет являться нарушением strict aliasing rule:

#include <stdio.h>

struct A {
    int value;
};

struct B {
    struct A a;
};

void print_value(struct A *);

int
main(void)
{
    struct B b = {{42}};
    print_value((struct A *) &b);
}

void
print_value(struct A *a)
{
    printf("%d\n", a->value);
}

ISO/IEC 9899:2011 (E).

→ Ссылка