C++ SSE:存储到数组后的未定义行为

Posted

技术标签:

【中文标题】C++ SSE:存储到数组后的未定义行为【英文标题】:C++ SSE: Undefined behavior after storing to an array 【发布时间】:2017-02-26 19:38:36 【问题描述】:

我正在使用 SSE 指令 _mm_loadu_si128 从 2 个未对齐的向量 vec1vec2 中读取 uint64_t 整数,该指令从对齐和未对齐的内存中读取数据。然后我进行按位和运算,然后使用_mm_storeu_si128 将结果存储到未对齐的内存中。存储的目标是一个名为arr1 的数组。似乎在商店之前,我可以访问arr1,但在商店之后访问arr1 给出了未定义的行为(有时是段错误)。

#include <vector>
#include <emmintrin.h>
#include <nmmintrin.h>
#include <chrono>
#include <smmintrin.h>
#include <iostream>

int main()


        std::vector<uint64_t> vec1(200);
        std::vector<uint64_t> vec2(200,1);
        std::vector<uint64_t> *p_vec1 = &vec1;
        std::vector<uint64_t> *p_vec2 = &vec2;

        int total_bits = 0;
        int k = 0;
        for(; k < 100; k+=2)
                __m128i* ptr1 = (__m128i*) (p_vec1 + k);
                __m128i* ptr2 = (__m128i*) (p_vec2 + k);
                __m128i val1_4 = _mm_loadu_si128(ptr1);
                __m128i val2_4 = _mm_loadu_si128(ptr2);
                uint64_t arr1[2] = 0,0;
                 std::cout << "val1 " << arr1[0] << std::endl;
                _mm_storeu_si128((__m128i*) arr1, _mm_and_si128(val1_4, val2_4)); // after storing into arr1, Accessing arr1 gives undefined behavior
                std::cout << "val2 " << arr1[0] << std::endl; // This should only output 0s but instead outputs random numbers
                total_bits += __builtin_popcountll(arr1[0]);
         

_mm_storeu_si128arr1的“改结构”吗,这就是我进店后无法访问的原因吗?

【问题讨论】:

&amp;vec1 为您提供vector 对象本身的地址,而不是存储在向量中的对象。您可以尝试使用uint64_t* ptr = &amp;vec1[0] 来获取存储的第一个值的地址。 uint64_t* ptr = vec1.data() 看起来更具可读性... 【参考方案1】:
std::vector<uint64_t> *p_vec1 = &vec1;
std::vector<uint64_t> *p_vec2 = &vec2;

绝对不是你想要的。这并不意味着p_vec1 指向向量中的元素,它指向向量本身。您稍后会执行 (__m128i*) (p_vec1 + k) 读取未定义行为:p_vec1 + 1 未指向向量的第二个值;它指向vec1 之后的向量(这没有任何意义)。

你可能想要一些类似的东西:

uint64_t *p_vec1 = vec1.data();
uint64_t *p_vec2 = vec2.data();

所以p_vec1p_vec2 指向向量的内容。

【讨论】:

以上是关于C++ SSE:存储到数组后的未定义行为的主要内容,如果未能解决你的问题,请参考以下文章

标头包含标准 C++ 库后的未定义符号

为啥在使用静态方法时取消引用 nullptr 而不是 C++ 中的未定义行为?

C++ 是不是使用放置新的未定义行为两次构造对象?

在 C++ 中比较两个 std::string 时的未定义行为 [关闭]

fieldoffset 的未定义行为[重复]

StructLayout 和 FieldOffset 的未定义行为