C ++ Нарушение прав чтения для вектора - PullRequest
0 голосов
/ 30 января 2019

Я получаю исключение, когда пытаюсь использовать vector [int_number] и моя программа перестает работать.

            uint64_t data = 0xffeeddccbbaa5577;
            uint16_t *vector = (uint16_t*) data;

            int currentPosition = 0;

            while (currentPosition <= 3) {

            uint16_t header = vector[currentPosition]; // problem here

Visual Studio 2017 возвращает меня: возникла необработанная исключительная ситуация: нарушение прав чтения.вектор был 0x6111F12.

Я застрял здесь.Если у вас есть идеи, что мне делать, буду благодарен.Заранее спасибо!

Ответы [ 5 ]

0 голосов
/ 30 января 2019

Вот конкретный пример, который должен избегать любого неопределенного поведения из-за строгого наложения псевдонимов / «незаконных» приведений / и т. Д., Так как это, по-видимому, то, что вас действительно интересует.

Этот код принимает std::uint64_t, копирует его в массив из четырех std::uint16_t с, изменяет значения в массиве, а затем копирует их обратно в исходный std::uint64_t.

#include <cstdint>
#include <cstring>
#include <iostream>

int main() {
  std::uint64_t data = 0xffeeddccbbaa5577;
  std::uint16_t data_spliced[4];
  std::memcpy(&data_spliced, &data, sizeof(data));
  std::cout << "Original data:\n" << data << "\nOriginal, spliced data:\n";
  for (const auto spliced_value : data_spliced) {
    std::cout << spliced_value << " ";
  }
  std::cout << "\n\n";

  data_spliced[2] = 0xd00d;
  memcpy(&data, &data_spliced, sizeof(data));
  std::cout << "Modified data:\n" << data << "\nModified, spliced data:\n";
  for (const auto spliced_value : data_spliced) {
    std::cout << spliced_value << " ";
  }
  std::cout << '\n';
}

с выводом (на моей машине):

Original data:
18441921395520329079
Original, spliced data:
21879 48042 56780 65518

Modified data:
18441906281530414455
Modified, spliced data:
21879 48042 53261 65518
0 голосов
/ 30 января 2019

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

 uint16_t vector[] = { 0x5577, 0xbbaa, 0xddcc, 0xffee };

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

0 голосов
/ 30 января 2019

Вам нужно взять адрес этой переменной, если вы хотите присвоить его указателю

const uint16_t* vector = reinterpret_cast<const uint16_t*>( &data ) ;

ПРИМЕЧАНИЕ. Это работает в MSVC 2017, но ...

Это грузовик с неопределенным поведением!- Вирсавия

Как указано в reinterpret_cast, как говорится:

5) Любой указатель на объект типа T1 может быть преобразован в указательк объекту другого типа cv T2.Это в точности эквивалентно static_cast<cv T2*>(static_cast<cv void*>(expression)) (что подразумевает, что если требование выравнивания T2 не является более строгим, чем T1, значение указателя не изменяется, и преобразование полученного указателя обратно в его исходный тип приводит кпервоначальное значение).В любом случае результирующий указатель можно безопасно разыменовать, только если это разрешено правилами псевдонимов типов (см. Ниже)

...

Псевдонимы типов.Всякий раз, когда делается попытка прочитать или изменить сохраненное значение объекта типа DynamicType с помощью glvalue типа AliasedType, поведение не определено, если не выполняется одно из следующих условий:

  1. AliasedType и DynamicType являютсяаналогичный.
  2. AliasedType является (возможно, cv-квалифицированным) подписанным или неподписанным вариантом DynamicType.
  3. AliasedType - это std :: byte (начиная с C ++ 17), char или unsigned char: это позволяет проверить представление объекта любого объекта в виде массива байтов.

Обратите внимание, что многие компиляторы C ++ ослабляют это правило как нестандартное расширение языка, чтобы разрешить доступ неправильного типа через неактивный член объединения (такой доступ не является неопределенным в C)

Приведенный выше код не соответствует ни одному из правил псевдонимов.

0 голосов
/ 30 января 2019

Получая доступ к данным через указатель другого типа, который вы получили путем приведения, вы переходите в undefined-поведения-land .Вместо этого попробуйте следующее (обратите внимание, я также заменил ваш цикл while на цикл для дальней связи, избегая необходимости держать счетчик)

#include <iostream>
#include <cstring>

int main() {
    uint64_t data = 0xffeeddccbbaa5577;
    uint16_t vector[4];
    memcpy(vector, &data, sizeof(uint64_t));

    for (uint16_t header : vector)
    {
        std::cout << std::hex << header << std::endl;
    }
}

, уступая

5577
bbaa
ddcc
ffee

Если вы используетеreinterpret_cast у вас есть два указателя разного типа, указывающие на один и тот же адрес, что может легко привести к неопределенному поведению.memcpy позволяет избежать этого, создавая копию ячейки памяти, и вы можете получить доступ к ней с помощью указателя другого типа.Также обратите внимание на type-punning (как указано @DanielLangr)

0 голосов
/ 30 января 2019

Отбрасывая все неопределенное поведение, которое вы получаете из-за строгого нарушения псевдонимов нарушений, при текущем урожае чипов Intel и времени выполнения MSVC все указатели имеют 48 бит.

Итак 0xffeeddccbbaa5577является никогда действительным значением указателя.

Таким образом, поведение при разыменовании этого значения будет неопределенным.

Если вы хотите разбить data на четыре элементаподходящего типа, тогда одним из методов является создание uint16_t foo[4] скажем и memcpy данных, начиная с &data до foo.

...