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

将指针传递到与函数参数相同的地址

  •  -1
  • morimn  · 技术社区  · 2 年前

    假设我有一个weired add函数,它进行加法和增量,然后将结果存储到其他地方。

    void add(const int* a, const int* b, int* c) {
        *c = (*a)++ + *b;
    }
    

    如果我将相同的指针传递给a和c,

    int a = 1, b = 3;
    add(&a, &b, &a);
    

    然后它将最终修改所指向的值 a .

    我的问题:

    1. 这是否违反了第一个参数的const限定符?
    2. 操作是否未定义为典型的未排序操作 x = x++

    我觉得两个问题的答案都应该是肯定的,但我的gcc对这两个问题都没有给出警告。

    编辑:我知道为了代码起见,可以有一个更好的解决方法。我的问题更多的是关于语言特征本身。

    1 回复  |  直到 2 年前
        1
  •  2
  •   ikegami Gilles Quénot    2 年前

    你有

    void add(const int* a, const int* b, int* c) {
        *c = (*a)++ + *b;
    }
    

    这不会编译。

    <source>:2:14: error: increment of read-only location '*a'
        2 |     *c = (*a)++ + *b;
          |      
    

    我们可以移除 const .

    void add(int* a, const int* b, int* c) {
        *c = (*a)++ + *b;
    }
    

    但当我们遇到不明确的行为时 a == c || a == b 。我们可以使用 restrict 关键字。

    void add(int* restrict a, const int* restrict b, int* restrict c) {
        *c = (*a)++ + *b;
    }
    

    有了这个签名,调用就变成了UB add 有两个相同的论点。

        2
  •  1
  •   John Bollinger    2 年前
    1. 这是否违反了第一个参数的const限定符?

    (*a) 是类型为的左值表达式 const int 但是

    后缀递增或递减运算符[…]的操作数应为可修改的左值。

    (C17 6.5.2.4/1)

    A. 可修改的左值 是一个[…]没有常量限定类型[…]的左值

    (C17 6.3.2.1/1)

    因此 (*a)++ 表达了一种修改,因为 const -资格,违反语言要求。我认为这就是你所说的“违反const限定符”的意思。因此,程序具有未定义的行为。

    那个 *a *c 然而,are别名并没有进入其中。这个规则是基于表示访问的lvalues的类型,而不是它们所引用的对象的有效类型,所以对的赋值 *c 就这些特定条款而言,是可以的。


    1. 操作是否未定义为典型的未排序操作 x = x++

    是的,在所提供的特定函数调用中。

    规则是:

    如果标量对象上的副作用相对于 对同一标量对象或值的不同副作用 使用相同标量对象的值进行计算,行为为 未定义。

    (C17 6.5/2)

    特别要注意的是,这一规定是根据被访问的对象给出的,而不是通过其执行访问的lvalues。


    我的gcc对两者都不发出警告

    这很奇怪。

    6.5.2.4/1是一个语言约束,因此诊断违规是C实现的一致性要求。我的GCC确实对此进行了诊断:

    $ gcc -o ub -O2 ub.c
    ub.c: In function ‘add’:
    ub.c:3:14: error: increment of read-only location ‘*a’
         *c = (*a)++ + *b;
                  ^~
    

    如果你的不这样做,就会出问题。

    另一方面,6.5/2是 语言约束,在实践中,编译器在这种情况下可能无法识别UB。诊断此问题不需要实现。我没有发现任何选项组合能引起GCC对这一问题的诊断。

        3
  •  0
  •   gulpr    2 年前

    这是否违反了第一个参数的const限定符?

    它会编译,也不会编译( https://godbolt.org/z/Mfjf6bsMq ),但没有UB,因为将没有可执行文件以定义或未定义的方式进行行为。但是,如果您修改程序以将引用传递给 const 对象

    void add(int* a, const int* b, int* c) {
        *c = (*a)++ + *b;
    }
    
    int main(void)
    {
        const int a = 5;
        int b = 3, c = 4;
    
        add(&a, &b, &c);
    }
    

    它将调用 未定义的行为 .

    操作是否未定义为典型的未排序x=x++?

    只有将引用传递给同一对象时,才能对其进行未定义:

    void add(int* a, const int* b, int* c) {
        *c = (*a)++ + *b;
    }
    
    int main(void)
    {
        int a = 5;
        int c = 4;
    
        add(&a, &a, &c);
    }
    
    推荐文章