C++ uintptr\t上的指针算法

C++ uintptr\t上的指针算法,c++,pointers,language-lawyer,undefined-behavior,C++,Pointers,Language Lawyer,Undefined Behavior,我必须实现一个“带通”滤波器。让a和b表示两个整数,它们产生半开区间[a,b)。如果某个参数x位于该区间内(即a 使用uintptr\u t是否有效且没有未定义的行为 是的。从指针到整数的转换(具有足够的大小,例如uintptr\u t)定义良好,整数算法定义良好 另一件需要注意的事情是,将修改后的uintpttr\u t转换回指针是否会返回您想要的。标准提供的唯一保证是,将指针转换回整数后会返回相同的地址。幸运的是,这一保证对您来说足够了,因为您总是使用转换器的精确值d指针 如果您使用的不是指

我必须实现一个“带通”滤波器。让
a
b
表示两个整数,它们产生半开区间
[a,b)
。如果某个参数
x
位于该区间内(即
a
使用
uintptr\u t
是否有效且没有未定义的行为

是的。从指针到整数的转换(具有足够的大小,例如
uintptr\u t
)定义良好,整数算法定义良好

另一件需要注意的事情是,将修改后的
uintpttr\u t
转换回指针是否会返回您想要的。标准提供的唯一保证是,将指针转换回整数后会返回相同的地址。幸运的是,这一保证对您来说足够了,因为您总是使用转换器的精确值d指针


如果您使用的不是指向窄字符的指针,我认为您需要在转换结果上使用
std::launder

标准不要求实现以有用的方式处理
uintptru
-指针转换,即使
uintptru
值为从指针到整数的转换

extern int x[5],y[5];
int *px5 = x+5, *py0 = y;
指针
px5
py0
可能比较相等,无论它们是否相等,代码都可以使用
px5[-1]
访问
x[4]
,或
py0[0]
访问
y[0]
,但不能访问
px5[0]
py0[-1]
。如果指针恰好相等,并且代码尝试访问
((int*)(uintptpr___)px5)[-1]
,编译器可以将
(int*)(uintptpr__)px5
替换为
py0
,因为该指针将与
px5
进行比较,但在尝试访问
py0[-1]时会跳转轨道
。同样地,如果代码试图访问
((int*)(uintpttr\u t)py0)[0]
,编译器可以用
px5
替换
(int*)(uintpttr\u t)py0
,然后在尝试访问
px5[0]
时跳过轨道

虽然编译器做这样的事情似乎很迟钝,但clang变得更疯狂。考虑一下:

#include <stdint.h>
extern int x[],y[];
int test(int i)
{
    y[0] = 1;
    uintptr_t px = (uintptr_t)(x+5);
    uintptr_t py = (uintptr_t)(y+i);
    int flag = (px==py);
    if (flag)
        y[i] = 2;
    return y[0];
}
#包括
外部内部x[],y[];
int测试(int i)
{
y[0]=1;
uintptr_t px=(uintptr_t)(x+5);
uintptr_t py=(uintptr_t)(y+i);
int标志=(px==py);
国际单项体育联合会(旗)
y[i]=2;
返回y[0];
}

如果
px
py
重合相等且
i
为零,这将导致clang将
y[0]
设置为2,但返回1。有关生成的代码,请参阅(“mov-eax,1/ret”表示“返回1”)

我不能说出C++标准规范的观点,但是<代码> uTutpRtTyt <代码>只是一个32/64位的无符号数,所以它与另一个数字相乘根本不应该是问题。<代码> RealTytRask> /Cord>一般可以被认为是调用未定义的行为,因为在重铸对象后特别是LIF。结果对象的时间未定义,但将其与指向
char*
的指针一起使用也不应该是一个问题。@WolverinDEV
reinterpret\u cast
通常不被视为调用未定义的行为。结果对象的生存期定义良好。它只是一个PR值(除非转换为引用).这不是更快吗(不过它会写入/读取内存…)叮当声better@LanguageLawyer更有趣的是,Clang将两个版本编译为完全相同的程序集。这一版本肯定属于Clang。基准测试表明,显然提供了相同的性能。@MaximeGroushkin整数从不递增。它乘以1或0,然后加上0。您确定
std::launder
?我记得
std::launder
是访问在另一个对象的存储中分配的对象所必需的,该对象的生存期已经结束。我的示例不涉及生存期和/或存储语义,只涉及指针来源和算术。@JonasMüller这只是因为标准说转换整数r to pointer给出的值与原始指针相同,但从技术上讲,拥有正确的值并不足以拥有指向该对象的指针。我不是100%确定这是必要的。但这不会造成伤害,而且规则也很复杂,因此安全性比抱歉要好:)标准可能会将从任何有效指针到
uintptr\t
的转换视为所有情况下定义的行为,以及任何两个
uintpttr\u t
值之间的转换,作为所有情况下定义的行为,但这并不意味着从指针“刚刚过去”生成
uintptpr\u t
的代码一个数组的结尾和另一个数组的结尾,从指针指向紧跟其后的数组,并比较这两个
uintpttr\u t
值,将不会导致clang无意义地处理附近的代码。@supercat程序中从不比较转换的指针。比较是在其他参数整数上进行的。仅对i从指针转换成的NTEGER是乘法1,乘法0,加法0。计算结果始终是转换值中的一个,然后转换成指针。我知道你有一个用GCC和CLAN的2020代C和C++优化的斧头,但这实际上并没有回答问题。n(接着是指针出处的抨击)。@JonasMüller:我的观点是(1)标准不要求实现将从
uintptpr_t
转换生成的指针视为可用于任何特定用途,以及(2)尽管许多人可能期望编译器在转换到/从
uintpttr_t
时保持谨慎,不管标准是否会迫使他们这样做,但有些编译器甚至在转换前后都会非常积极地进行优化。因为OP代码失败的唯一原因是编译器对指针做出了不必要的假设出处
#include <cstdint>

const char* funky_bandpass(int a, int b, int x, const char* low,
    const char* high)
{
    const bool withinInterval { (a <= x) && (x < b) };
    const auto low_ptr = reinterpret_cast<uintptr_t>(low) * (!withinInterval);
    const auto high_ptr = reinterpret_cast<uintptr_t>(high) * withinInterval;

    const auto ptr_sum = low_ptr + high_ptr;
    const auto* result = reinterpret_cast<const char*>(ptr_sum);
    return result;
}
funky_bandpass(int, int, int, char const*, char const*):
        mov     r9d, esi
        cmp     edi, edx
        mov     esi, edx
        setle   dl
        cmp     esi, r9d
        setl    al
        and     edx, eax
        mov     eax, edx
        and     edx, 1
        xor     eax, 1
        imul    rdx, r8
        movzx   eax, al
        imul    rcx, rax
        lea     rax, [rcx+rdx]
        ret
extern int x[5],y[5];
int *px5 = x+5, *py0 = y;
#include <stdint.h>
extern int x[],y[];
int test(int i)
{
    y[0] = 1;
    uintptr_t px = (uintptr_t)(x+5);
    uintptr_t py = (uintptr_t)(y+i);
    int flag = (px==py);
    if (flag)
        y[i] = 2;
    return y[0];
}