【问题标题】:C++ Read access violation for vector向量的 C++ 读取访问冲突
【发布时间】:2019-01-30 08:02:18
【问题描述】:

当我尝试使用 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。

我被困在这里。如果你知道我应该做什么,我将不胜感激。提前致谢!

【问题讨论】:

  • 你到底想用这段代码做什么?因为无论它是什么,这都不是正确的方法。提出正确的问题,您可能会得到有用的答案。
  • 如何获得一个 16 位的四个元素的向量? IE。矢量[0] = 5577,矢量[1]=bbaa 等?
  • @Flavinia 声明一个由四个 16 位数字组成的数组并使用 memcpydata 复制字节。 IRC,这是唯一合法的方式。顺便说一句,你可能想写uint16_t* vector = (uint16_t*)(&amp;data);

标签: c++ visual-studio exception access-violation


【解决方案1】:

撇开由于严格别名违规而导致的所有未定义行为,在当前的英特尔芯片和 MSVC 运行时中,所有指针都是 48 位。

所以0xffeeddccbbaa5577 从不是一个有效的指针值。

因此,取消引用该值的行为将是未定义的。

如果您想将data 分解为适当类型的四个元素,那么一种方法是创建一个uint16_t foo[4]memcpy,数据从&amp;data 开始到foo

【讨论】:

  • 好的,我明白了。但是我能做些什么来获得一个在 16 位上具有四个元素的向量呢? IE。矢量[0] = 5577,矢量[1]=bbaa 等?
  • @Flavinia 这个答案显示了真正的问题:在您的代码中,您将 data 的值解释为指针,而不是像其他答案中所建议的那样,将变量的含义重新解释为 4 的数组16 位值。
【解决方案2】:

通过你通过转换获得的不同类型的指针访问数据,你会进入undefined-behavior-land。取而代之的是,请尝试以下操作(注意,我还用范围 for 循环替换了您的 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 (as pointed out by @DanielLangr)

【讨论】:

  • Gcc 看穿了 memcpy,并通过优化完成了预期的事情。 MSVC 呢?
  • @RobertAndrzejuk 在这个非常简单的例子中,see compiler explorer。我希望我没有这么快犯错。
【解决方案3】:

这真的很容易,但是您与最初的尝试相差太远,以至于让所有人都感到困惑。

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

提出正确的问题,如果您提出了您在 cmets 中遇到的问题,我们会更快到达那里。

【讨论】:

  • uint64_t data = 0xffeeddccbbaa5577; 这里只是一个例子。我从内存中获取 64 位数据,我问这个问题的原因是因为我现在不应该使用 reinterpret_cast
  • @Flavinia 以我的拙见,您不应该使用 reinterpret_cast 进行类型双关,即使它可能适用于某些编译器(请参阅类型双关)。这样做会导致标准的阴暗面 -- undefined-behavior ;)
  • 我真的不知道为什么,但是我已经运行了这个项目,它工作得非常好。
  • @Flavinia 这就是 undefined-behavior 的意思。它今天可以在您的编译器上运行,但明天可能会在补丁后失败(这是非常不可能的,因为这会破坏许多以前工作的代码并且经常惹恼人们),但是,如果您知道替代方案,请执行不使用它。您在这里的替代方法是使用您应该使用的memcpy 制作副本。为什么不走安全路线?
  • 哦,好的。我现在知道了。非常感谢!
【解决方案4】:

这是一个具体示例,应该避免由于严格的别名/“非法”强制转换等导致的任何未定义行为,因为这似乎是您真正感兴趣的内容。

此代码采用std::uint64_t,将其复制到包含四个std::uint16_ts 的数组中,修改数组中的值,然后将它们复制回原始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

【讨论】:

    【解决方案5】:

    如果要将变量分配给指针,则需要获取该变量的地址

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

    注意: 这适用于 MSVC 2017,但是...

    这是一大堆未定义的行为! ——芭丝谢芭

    正如 reinterpret_cast 的 cpprefrence 所说:

    5) 任何指向T1 类型对象的指针都可以转换为指向另一种类型cv T2 的对象的指针。这完全等同于static_cast&lt;cv T2*&gt;(static_cast&lt;cv void*&gt;(expression))(这意味着如果T2 的对齐要求不比T1 更严格,则指针的值不会改变并且将结果指针转换回其原始类型会产生原始值)。在任何情况下,只有在类型别名规则允许的情况下,才能安全地取消引用结果指针(见下文)

    ...

    类型别名。 每当尝试通过 AliasedType 类型的 glvalue 读取或修改 DynamicType 类型的对象的存储值时,除非满足以下条件之一,否则该行为是未定义的:

    1. AliasedType 和 DynamicType 类似。
    2. AliasedType 是 (可能是 cv 限定的)DynamicType 的有符号或无符号变体。
    3. AliasedType 是 std::byte(C++17 起)、char 或 unsigned char:this 允许将任何对象的对象表示检查为 字节数组。

    请注意,作为非标准语言扩展,许多 C++ 编译器放宽了此规则,以允许通过联合的非活动成员进行错误类型访问(这种访问在 C 中不是未定义的)

    以上代码不满足任何别名规则。

    【讨论】:

    • 这是一大堆未定义的行为!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 2023-02-12
    相关资源
    最近更新 更多