Доступ к переменной указателя как к указателю на другой тип в C ++ - PullRequest
1 голос
/ 18 мая 2019

Это хорошая практика для доступа к переменной указателя путем разыменования указателя на указатель, который указывает на другой тип или void? Может ли это нарушить строгие правила наложения имен? C и C ++ имеют некоторые различия в правилах наложения имен. В этом вопросе мы сосредоточимся на C ++. Другой вопрос, касающийся C, можно найти здесь . В следующем примере к double* обращаются как void*.

int create_buffer(void** ptr, ...)
{
    *ptr = malloc(...);
    ...
}

int main(void)
{
    double* buffer;

    // The problematic code is here, double**
    // is coerced to void**, which is later
    // dereferenced by the function
    create_buffer(reinterpret_cast<void**>(&buffer), ...);
    ...
}

Если это вызывает UB, как насчет следующего?

// process A
int* p;  ...
printf("%p", p); // UB?

// process B
int* p;
scanf("%p", &p); // UB?

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

Ответы [ 2 ]

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

Это хорошая практика для доступа ...

Нет.void* не является типом для полиморфизма и повторного использования в C ++, даже без учета проблем с алиасами в исходном коде.С помощью богатого механизма шаблонов вы можете сделать свой код строго типизированным и безопасным для загрузки.Очевидное улучшение заключается в использовании шаблонов и вводе безопасного выделения:

template<typename T>
int create_buffer(T** ptr, ...)
{
    *ptr = new T[...];
    ...
}

Но, чтобы идти по касательной, это не совсем так, как будет выглядеть хороший C ++.Хороший C ++ - это правильное управление сложностью.А отслеживание буфера - сложная задача.Хороший подход C ++ - не делать это вручную, а заключать его в отдельный класс (шаблон).Фактически, это настолько распространенная задача, что стандартная библиотека предоставляет решение.

Хорошей практикой является использование std::vector<double> вместо функции создания буфера.Шаблон класса для типовой задачи типа часто побьет любое использование void*.Это позволит полностью избежать проблем с псевдонимами, поскольку всегда используется правильный тип.

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

Это UB, поскольку вы присваиваете переменную double*, как если бы она была переменной void* (Это имеет тот же эффект, что и reinterpret_cast<void*&>(buffer) = malloc(...); в C ++. Это будет нормально в большинстве систем, как void* и double*, как правило, точно такие же, но это все еще UB, поэтому может работать не во всех реализациях.

Решением было бы присвоить другую переменную и затем переназначить ее:

int main(void)
{
    double* buffer;

    {
        void* result;
        create_buffer(&result, ...);
        buffer = (double*) result;  // Cast needed for C++
    }
    ...
}
...