Тип памяти - PullRequest
       15

Тип памяти

1 голос
/ 31 мая 2019

Первоначально я задавал этот вопрос: Тип Punning с Unions и Heap

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

После прочтения этого сайта: https://kristerw.blogspot.com/2016/05/type-based-aliasing-in-c.html

В нижней части он говорит о памяти malloc'd. Можно ли с уверенностью сказать, что приведение из одного типа указателя к другому типу указателя безопасно, когда память находится в куче?

Пример:

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

struct test1
{
    int a;
    char b;
};

struct test2
{
    int c;
    char d;
};

void printer(const struct test2* value);

int main()
{
    struct test1* aQuickTest = malloc(sizeof(struct test1));
    aQuickTest->a = 42;
    aQuickTest->b = 'a';
    printer((struct test2*)aQuickTest); //safe because memory was malloc'd???
    return 0;
}

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

И думаю, что это небезопасно. Каков будет правильный способ сделать это с memcpy? Я попытаюсь написать пример с функцией того, что, как мы надеемся, сработает?

struct test2* converter(struct test1* original);

int main()
{
    struct test1* aQuickTest = malloc(sizeof(struct test1));
    aQuickTest->a = 42;
    aQuickTest->b = 'a';
    struct test2* newStruct = converter(aQuickTest);
    printer(newStruct);
    return 0;
}

struct test2* converter(struct test1* original)
{
    struct test2* temp;
    memcpy(&temp, &original, sizeof(struct test2));
    return temp;
}

1 Ответ

3 голосов
/ 31 мая 2019
void *pnt = malloc(sizeof(struct test1));

Какой тип памяти имеет указатель pnt?Нет типа.Он неинициализирован (его значение «неопределенное»).Есть только «память».

Тогда вы делаете:

struct test1* aQuickTest = malloc(sizeof(struct test1));

Вы только наведите указатель.Здесь ничего не происходит.Сборка не создается.Чтение неинициализированной памяти является неопределенным поведением, поэтому вы не можете читать из aQuickTest->a (пока).Но вы можете назначить:

aQuickTest->a = 1;

Это записывает объект struct test1 в памяти.Это задание.Теперь вы можете прочитать aQuickTest->a, т.е.выведите его.
Но следующее

printf("%d", ((struct test2*)aQuickTest)->a);

- неопределенное поведение (хотя оно будет / должно работать).Вы получаете доступ к базовому объекту (т.е. struct test1), используя несовпадающий тип указателя struct test2*.Это называется "строгое нарушение псевдонима".Разыменование объекта (т. Е. Выполнение -> или *) с использованием дескриптора не совместимого типа приводит к неопределенному поведению.Неважно, что struct test1 и struct test2 «выглядят одинаково».Они разного типа.Правило соответствует стандарту C11 6.5p7 .

В первом фрагменте кода происходит неопределенное поведение внутри printf("Int: %i Char: %c",value->c.Доступ value-> обращается к основной памяти с помощью несовместимого дескриптора.

Во втором фрагменте кода переменная temp является только указателем.Также original является указателем.Выполнение memcpy(&temp, &original, sizeof(struct test2)); недопустимо, потому что &temp записывает в указатель temp и &original записывает в original pointer. No to the memory behind pointers. As you write out of bounds into & temp pointer and read of bounds from & original pointer (because most probably sizeof (temp) and sizeof(оригинал)

В любом случае, даже если бы это было:

    struct test1* original = &(some valid struct test1 object).
    struct test2 temp;
    memcpy(&temp, original, sizeof(struct test2));
    printf("%d", temp.a); // undefined behavior

доступ к памяти за переменной temp все еще недействителен.Поскольку у original не было struct test2 объекта, он все еще недействителен.memcpy не меняет тип объекта в памяти.

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