Тип Punning с помощью союзов и кучи - PullRequest
1 голос
/ 31 мая 2019

Я много читал о наказании типов и о том, как нехорошо просто использовать приведение.

oldType* data = malloc(sizeof(oldType));
((newtype*)data)->newElement;

Это приводит к неопределенному поведению. Таким образом, решение состоит в том, чтобы использовать union, чтобы компилятор знал, что эти два указателя связаны друг с другом, поэтому он не делает смешных вещей со строгим псевдонимом.

При этом союзы также выглядели так:

union testing
{
    struct test1 e;
    struct test2 f;
}

Определено ли поведение, если в объединении используются указатели?

union testing
{
    struct test1* e;
    struct test2* f;
}

Вот полный пример:

#include <stdio.h>
#include <stdlib.h>

struct test1
{
    int a;
    char b;
};

struct test2
{
    int c;
    char d;
};

union testing
{
    struct test1* e;
    struct test2* f;
};

void printer(const struct test2* value);

int main()
{
    struct test1* aQuickTest = malloc(sizeof(struct test1));
    aQuickTest->a = 42;
    aQuickTest->b = 'a';
    printer(((union testing)aQuickTest).f);
    ((union testing)aQuickTest.f)->c = 111; // using -> not .
    return 0;
}

void printer(const struct test2* value)
{
    printf("Int: %i Char: %c",value->c, value->d);
}

Или мне нужно использовать союзы без указателей. А затем используйте printer(&(((union testing)aQuickTest).f));&), чтобы получить адрес f.

Ответы [ 2 ]

2 голосов
/ 31 мая 2019

Преобразование в тип объединения не соответствует, как это делает ваш код:

    printer(((union testing)aQuickTest).f);

По этой причине ваш код имеет неопределенное поведение в отношении стандарта.

Точнее, однако, нет, ваш подход к добавлению указателей в объединение не исключает строгих нарушений псевдонимов в отношении указанных типов, даже без проблемы приведения. В вашем случае эффект заключается в том, что там, где ваш union testing находится в области видимости, реализации не могут предполагать, что объекты типа struct test1 ** и struct test2 ** не имеют псевдонимов друг друга. Это не мешает неопределенному определенному поведению, возникающему в результате доступа к объекту с эффективным типом struct test1 через lvalue типа struct test2.

0 голосов
/ 31 мая 2019

Предположим, что вы хотите ввести типы каламбура X и Y, вам следует использовать объединение -

typedef union {
    X x;
    Y y;
}X_Y;

Это позволяет вам совместно использовать битовое представление X с Y и наоборот.Если вы используете -

typedef union {
    X* x;
    Y* y;
}X_Y_p;

, вы делите битовые представления для указателя.Для системы, которая использует одно и то же битовое представление для всех указателей, вы по существу приводите указатель X к указателю Y, который, как вы указали, вызывает неопределенное поведение.

Недопустимо иметь что-то X_Y_p, потому что X* и Y* сами по себе являются типами.Но они достигают чего-то другого.Они позволяют вводить указатели каламбура, а это не то, что вам нужно (и в большинстве случаев это необязательно, поскольку указатели имеют общее представление в большинстве систем).В ролях должно быть хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...