代码之家  ›  专栏  ›  技术社区  ›  personal_cloud

在GCC Linux X8664 C++中,(p+x)-x总是导致p和整数x的p吗?

  •  -2
  • personal_cloud  · 技术社区  · 6 年前

    假设我们有:

    char* p;
    int   x;
    

    正如最近讨论的那样 in another question 包括比较操作 无效指针在GCC Linux X8664 C++中会产生意外行为。这个新问题是关于表达式的 (p+x)-x :它是否会产生意外的行为(即,结果不是 p )在x86-64 linux上运行的任何现有GCC版本中?

    请注意,这个问题只是关于指针算法;我们完全无意 指定地点 *(p+x)

    non-zero-based arrays . 请注意 (p+x) 减去 x 在这些应用程序的代码中的不同位置发生。

    如果x86-64上的最新GCC版本可以显示为从不为 (p+x)-x型

    更新

    对于上述实际案例,我们也可以假设 p p != NULL .

    2 回复  |  直到 6 年前
        1
  •  1
  •   Peter Cordes    6 年前

    是的,对于gcc5.x和更高版本,这个特定的表达式很早就被优化到 p ,即使禁用了优化,也不考虑任何可能的运行时。

    gcc -fsanitize=undefined -Wall -Wextra -Wpedantic

    int *add(int *p, long long x) {
        return (p+x) - x;
    }
    
    int *visible_UB(void) {
        static int arr[100];
        return (arr+200) - 200;
    }
    

    使用 gcc -dump-tree-original 在gcc5.x和更新版本中 . (甚至发生在 -O0 ).

    ;; Function int* add(int*, long long int) (null)
    ;; enabled by -tree-original
    
    return <retval> = p;
    
    
    ;; Function int* visible_UB() (null)
    ;; enabled by -tree-original
    {
      static int arr[100];
    
        static int arr[100];
      return <retval> = (int *) &arr;
    }
    

    那是 from the Godbolt compiler explorer with gcc8.3 -O0公司 .

    x86-64 asm输出只是:

    ; g++8.3 -O0 
    add(int*, long long):
        mov     QWORD PTR [rsp-8], rdi
        mov     QWORD PTR [rsp-16], rsi    # spill args
        mov     rax, QWORD PTR [rsp-8]     # reload only the pointer
        ret
    visible_UB():
        mov     eax, OFFSET FLAT:_ZZ10visible_UBvE3arr
        ret
    

    -O3 mov rax, rdi


    gcc4.9和更早版本只在稍后的过程中进行优化,而不是在 -O0公司 :树转储仍然包含减法,并且x86-64 asm是

    # g++4.9.4 -O0
    add(int*, long long):
        mov     QWORD PTR [rsp-8], rdi
        mov     QWORD PTR [rsp-16], rsi
        mov     rax, QWORD PTR [rsp-16]
        lea     rdx, [0+rax*4]            # RDX = x*4 = x*sizeof(int)
        mov     rax, QWORD PTR [rsp-16]
        sal     rax, 2
        neg     rax                       # RAX = -(x*4)
        add     rdx, rax                  # RDX = x*4 + (-(x*4)) = 0
        mov     rax, QWORD PTR [rsp-8]
        add     rax, rdx                  # p += x + (-x)
        ret
    
    visible_UB():       # but constants still optimize away at -O0
        mov     eax, OFFSET FLAT:_ZZ10visible_UBvE3arr
        ret
    

    -fdump-tree-original 输出:

    return <retval> = p + ((sizetype) ((long unsigned int) x * 4) + -(sizetype) ((long unsigned int) x * 4));
    

    x*4


    作为更大函数的一部分,编译器可以推断一些范围信息,如 p[x] 是同一对象的一部分 p[0] ,因此允许读取中间/中间的内存,并且不会出错。e、 g.允许搜索循环的自动矢量化。

    但我怀疑海湾合作委员会是否会寻找这种机会,更不用说利用它了。

    (请注意,您的问题标题专门针对针对Linux上x86-64的gcc, 在gcc中,例如,如果在单独的语句中完成。我的意思是,是的,在实践中可能是安全的,但是在解析之后不会马上被优化掉。而且绝对不是关于C++的。


    我强烈推荐 这样做。使用 uintptr_t 就像你在更新你的答案一样 C++ gcc extension for non-zero-based array pointer allocation? .

        2
  •  1
  •   user11143275    6 年前

    本文件未规定要求的行为

    就这样。不多不少。该标准可以被认为是编译器供应商在生成有效程序时要遵循的一系列约束。当有未定义的行为时,所有的赌注都被取消。

    可以 不能

    在里面 你的计划。

    我的意思是,如果GCC有一个很好的特性(特别是无效指针上的数学)目前还没有命名,我们可以给它一个名称,然后在将来的版本中也需要它。

    然后它将是一个非标准的扩展,人们希望它被记录下来。我也非常怀疑这样一个特性是否会有很高的需求,因为它不仅允许人们编写不安全的代码,而且很难为用户生成可移植的程序。

        3
  •  1
  •   prl    6 年前

    下面是gcc扩展的列表。 https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html

    因此,gcc将您询问的指针算法的行为视为未定义的,条件与语言标准中描述的相同。