矢量的C ++读取访问冲突

时间:2019-01-30 08:02:18

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

当我尝试使用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 :(得分:5)

搁置由于严格别名违反而获得的所有未定义行为,在当前的Intel芯片和MSVC运行时中,所有指针均为48位。

因此0xffeeddccbbaa5577 从不是有效的指针值。

因此取消引用该值的行为将是不确定的。

如果您想将data分解为适当类型的四个元素,则一种方法是创建一个uint16_t foo[4]的声明,并memcpy始于{{1} }到&data

答案 1 :(得分:4)

通过使用不同类型的指针访问数据,该指针是通过将您漫游到 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)

答案 2 :(得分:1)

这真的很容易,但是与原始尝试(让所有人感到困惑)相距很远。

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

问正确的问题,如果您在评论中提出了问题,我们会更快地解决问题。

答案 3 :(得分:1)

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

此代码采用一个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

答案 4 :(得分:-2)

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

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

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

  

这是卡车的未定义行为! –芭丝谢芭

reinterpret_cast的cpprefrence说:

  

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

     

...

     

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

     
      
  1. AliasedType和DynamicType相似。
  2.   
  3. AliasedType是   (可能具有CV资格)DynamicType的有符号或无符号变体。
  4.   
  5. AliasedType是std :: byte(从C ++ 17开始),char或unsigned char:   允许将任何对象的对象表示形式检查为   字节数组。
  6.   
     

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

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