Есть ли разница между приведением указателя на int и на символ? в C ++ - PullRequest
0 голосов
/ 15 марта 2019
#include <iostream>

char** make2D(const int dim1, const int dim2)
{
    char* toAlloc;
    const int size = (dim1 * dim2) + dim2;
    toAlloc = new char[size];

    for(int i = 0; i < dim2; i++)
    {
        toAlloc[i] = reinterpret_cast<char>(&toAlloc[(dim2 + (dim1 * i))]);
    }

    return reinterpret_cast<char**>(toAlloc);
}

int main(void)
{
    int dim1 = 8;
    int dim2 = 10;
    char** array2D = make2D(dim1, dim2);

    for (int i = 0; i < dim2; ++i)
    {
        array2D[i][i % dim1] = i + 100; // << Crash
    }

    return 0;
}

Я пытался выделить двумерный массив одним выделением. Итак, мой алгоритм состоял в том, что первые 10 (в этом коде dim2) элементов имеют указатель на первый элемент каждой строки.

Когда я попробовал это указателем на 'int',

int** make2D(const int dim1, const int dim2)
{
    int* toAlloc;
    const int size = (dim1 * dim2) + dim2;
    toAlloc = new int[size];

    for(int i = 0; i < dim2; i++)
    {
        toAlloc[i] = reinterpret_cast<int>(&toAlloc[(dim2 + (dim1 * i))]);
    }

    return reinterpret_cast<int**>(toAlloc);
}

int main(void)
{
    int dim1 = 8;
    int dim2 = 10;

    int** array2D = make2D(dim1, dim2);

    for (int i = 0; i < dim2; ++i)
    {
        array2D[i][i % dim1] = i + 100;
    }

    return 0;
}

он работает нормально, но когда я делаю это в char, он вылетает в закомментированной строке в приведенном выше коде.

Я думал о сбое, когда я делал reinterpret_cast, что-то происходило из-за разрыва памяти между указателем (8 байт) и символом (1 байт). Звучит как смешно ... изменение указателя (8 байт) на int (4 байт) было бы хорошо, но когда я выполняю приведение более резко (от 8 байт до 1 байт), это вызывает некоторые проблемы ...

Я понятия не имею, почему char не работает, но int работает. Не могли бы вы дать несколько советов, чтобы сделать случай с чарсом?

Ответы [ 2 ]

0 голосов
/ 15 марта 2019

Проблема в «несовместимости» размеров объектов:

  • sizeof(char) - это 1
  • sizeof(int) обычно 4 или 8 (но не менее 2).
  • sizeof(T*), который обычно 4 или 8, std::uintp_t может содержать значение void*, что необязательно в случае с int (и даже меньше с char).

Вы не можете безопасно хранить void* в char или int. Бывает, он работает на int, но не переносим.

reinterpret_cast, как правило, неправильный инструмент.

Проще было бы создать class Matrix, с std::vector<T> и средством доступа для исправления индексации. (Возможно, у вас даже есть прокси, чтобы разрешить синтаксис m[2][3]).

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

0 голосов
/ 15 марта 2019

Чтобы ответить на вопрос, да, есть огромная разница: на многих платформах указатель может вписываться в int, а на очень немногих платформах - в char.На современных ПК, которые являются 64-битными, ни один из них не является безопасным способом хранения указателя.

Используйте контейнеры, такие как vector или array , если размер статический.Попробуйте что-то вроде:

array<array<T, dim2>, dim1> variable{};

, если вы действительно хотите двумерный массив типа T;так как вам, кажется, нужен массив указателей, попробуйте что-то вроде:

array<array<T *, dim2>, dim1> variable{};

Это позаботится о создании массива соответствующего типа для хранения указателей для вашей платформы, независимо от того, насколько большими на самом деле являются указатели,очевидно, вам следует заменить T на соответствующий тип данных, на которые вы хотите указать, это обеспечит правильную математику указателя.Размеры массивов будут рассчитываться во время компиляции. Если вам нужны динамические размеры, вы должны использовать vector, после вызова выделения resize для вектора и всех подвекторов, чтобы убедиться, что вы выделяете всю память за несколько секунд.проходит как можно скорее.

Пожалуйста, также не используйте reinterpret_cast или броски в стиле c, это рецепт катастрофы, если вы не очень хорошо знаете, что делаете.

Не знаюкакую книгу вы читаете или кто преподает вам C ++, но, пожалуйста, измените свой источник знаний.

Использование необработанных указателей-владельцев не рекомендуется, и то, как вы их используете, неверно во многих отношениях.Никогда не храните указатель ни в чем, кроме типа указателя.Даже в простой C вы должны привести к как минимум void *, если вам нужно вообще.

Пожалуйста, прочитайте о unique_ptr или shared_ptr , если вы действительно хотите сохранить/ передать указатели напрямую.

Если вы настаиваете на использовании необработанных указателей для контейнеров, попробуйте собрать ваш код с помощью таких дезинфицирующих средств, как очиститель адресов, очиститель памяти (они поддерживаются, по крайней мере, clang и gcc, возможно, и другими компиляторами.дней)

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