Это vali, чтобы позвонить бесплатно с указателем на первый член?

Можно ли бесплатно позвонить по указателю, который указывает на первый член структуры (а структура - та, которая связана с malloc)? Я знаю, в принципе, указатель указывает на правильную вещь в любом случае...

struct s {int x;};
//in main
struct s* test;
test = (struct s*) malloc(sizeof(*test));
int* test2;
test2 = &(test->x);
free(test2); //is this okay??

Также будет изменен ответ, если int x будет заменен на struct?

Обновление: зачем мне писать такой код?

struct s {int x;};
struct sx1 {struct s test; int y;}; //extending struct s
struct sx2 {struct s test; int z;}; //another
// ** some functions to keep track of the number of references to each variable of type struct s 
int release(struct s* ptr){
  //if the number of references to *ptr is 0 call free on ptr
}
int main(){
    struct sx1* test1;
    struct sx2* test2;
    test1 = (sx1*) malloc(sizeof(*sx1));
    test2 = (sx2*) malloc(sizeof(*sx2));
    //code that changes the number of references to test1 and test2, calling functions defined in **
    release(test1);
    release(test2);
}

Ответ 1

Да, это нормально.

6.7.2.1

  1. Внутри объекта структуры небитовые поля и единицы, в которых бит-поля имеют адреса, которые увеличиваются в том порядке, в котором они объявлены. Указатель на структурный объект, соответствующим образом преобразованный, указывает на его первоначальный член (или если этот элемент является бит-поле, затем в блок, в котором он находится) и наоборот. Может быть неназванный заполнение внутри объекта структуры, но не в начале.

Это означает, что это определено:

struct s {int x;};
struct s* test;
test = (struct s*) malloc(sizeof(*test));
int* p = &(test->x);
free(p);

Ответ 2

В соответствии со стандартом C11, глава §6.7.2.1

[...] Может быть неназванный заполнение внутри объекта структуры, но не в начале.

что означает, что в начале структуры не может быть отступов. Таким образом, первый член будет иметь тот же адрес, что и структурная переменная.

free() нужен указатель, который ранее был возвращен malloc() или семейством.

В вашем случае вы передаете тот же адрес, что и malloc(). Итак, вы хорошо пойдете.