关于是否已经处理数组未定义行为基本指针申请后递减? [英] Does applying post-decrement on a pointer already addressing the base of an array invoke undefined behavior?

查看:191
本文介绍了关于是否已经处理数组未定义行为基本指针申请后递减?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

狩猎有关以下无济于事相关的或重复的问题(我只能做边缘正义来形容的标记用C指针算法和后递减问题的数量之多,但我只想说:容载量之后做了严重的不公平到结果集数)我在澄清或转诊到躲避我重复的希望折腾这个环。

如果递减后的操作者施加到指针如低于,阵列序列的简单反向迭代中,将执行以下操作code调用未定义行为?

 的#include<&stdio.h中GT;
#包括LT&;&string.h中GT;诠释的main()
{
    个char [] =一些字符串;
    为const char * T = S +的strlen(S);    而(T - > S)
        的fputc(* T,标准输出);
    的fputc('\\ n',标准输出);    返回0;
}

这是最近提议,我认为6.5.6.p8添加剂经营者,与6.5.2.p4结合,后缀增量和减量运算符,即使指定的执行的一个时,后减 T 时,它已经包含的基地址取值调用未定义行为,不管是否造成<$ C $的价值C> T (不是 T - 前pression结果)评估与否。我只是想知道这是否是真实的情况。

该标准的引用部分是:


  

6.5.6加法运算符


  
  <醇开始=8>
  
  • 如果指针操作数,结果点的两个元素
      相同的数组对象,或者一个过去的数组对象的最后一个元素,
      该评估也不得产生溢出;否则,该行为
      是不确定的。

  •   

    和与......其近紧耦合关系


      

    6.5.2.4后缀增量和减量运算约束


      
      

        
    1. 的增加或减少后缀运算符的操作数应
        原子,合格,不合格的或真实或指针类型,并为一个
        修改的左值。

    2.   

      
      

    语义


      
      <醇开始=2>
      
  • 后缀++运算的结果是操作数的值。作为副作用,操作数对象的值被递增(即,相应的类型的值1被添加到它)。见添加剂经营者对信息的限制,类型和转换的讨论和复合赋值和操作对指针的影响。结果的值计算更新的操作数的存储值的副作用之前测序。相对于一个indeterminately测序函数调用,后缀++的操作是单个评估。后缀++与原子类型的对象上是memory_order_seq_cst内存为了semantics.98读 - 修改 - 写操作)


  •   
  • 后缀 - 运营商类似于后缀++运算符,只是操作数的值递减(即相应类型的值1从减它)。


  •   
      
      

    转发引用:加法运算符(6.5.6),复合赋值(6.5.16.2)


    的非常原因帐样品中使用递减后操作者是要避免的评估的针对数组的基地址的最终-无效地址值。例如,code以上是下面的一个重构

     的#include&LT;&stdio.h中GT;
    #包括LT&;&string.h中GT;诠释的main()
    {
        个char [] =一些字符串;    为size_t LEN = strlen的(S);
        字符* T = S + LEN - 1;
        而(T&GT; = S)
        {
            的fputc(* T,标准输出);
            T = - 1;
        }
        的fputc('\\ n',标准输出);
    }

    暂时忘掉这对非零长度字符串s ,这个一般的算法显然有问题(也许不那么清楚一些)。如果 S [] 是代替,那么 T 将分配 S-1 ,值本身是不是在取值的有效范围内通过其单过去的地址,以及对评估比较小号随之而来也是白搭。如果取值具有非零长度,即解决了的初始 S-1 的问题,但只是暂时的,因为最终这是的还是的该值计算(不管它是什么)对违反比较是​​有效的S 来终止循环。还可能会更糟糕的。它可能一直天真地:

     为size_t长度= strlen的(S) -  1;
        字符* T = S + LEN;

    这如果取值写了这一切都有灾难是一个零长度字符串。这个问题与打开的重构code的旨在解决所有这些问题。但是......

    我的偏执可能会得到我的,但如果他们真的都出来让你,我不是偏执狂。因此,按标准(这些部分,或者其他人),请问原来的code(滚动到这种新颖的顶部,如果你忘记了是什么样子现在)确实是未定义行为或不?


    解决方案

    我是pretty肯定的是,在这种情况下,后减的结果确实是不确定的行为。该减量后清楚地减去一个来自一个指向对象的开始,所以结果并不指向同一个数组的元素,并通过指针运算的定义(教派; 6.5.6 / 8,如引在OP)这是未定义的行为。你从来不使用所产生的指针的事实是无关紧要的。

    这有什么错:

     的char * T = S +的strlen(S);
    而(T&GT S)的fputc(* - T,标准输出);


    值得关注,但实际上无关:逆向迭代器的++库通常在反向迭代持有指向包含一个过去的目标元素C标准的实施。这允许反向迭代正常而没有涉及的指针一开始前的容器,这将是UB,如上所述可以使用。

    After hunting for a related or duplicate question concerning the following to no avail (I can only do marginal justice to describe the sheer number of pointer-arithmetic and post-decrement questions tagged with C, but suffice it to say "boatloads" does a grave injustice to that result set count) I toss this in the ring in hopes of clarification or a referral to a duplicate that eluded me.

    If the post-decrement operator is applied to a pointer such as below, a simple reverse-iteration of an array sequence, does the following code invoke undefined behavior?

    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char s[] = "some string";
        const char *t = s + strlen(s);
    
        while(t-->s)
            fputc(*t, stdout);
        fputc('\n', stdout);
    
        return 0;
    }
    

    It was recently proposed to me that 6.5.6.p8 Additive operators, in conjunction with 6.5.2.p4, Postfix increment and decrement operators, specifies even performing a post-decrement upon t when it already contains the base-address of s invokes undefined behavior, regardless of whether the resulting value of t (not the t-- expression result) is evaluated or not. I simply want to know if that is indeed the case.

    The cited portions of the standard were:

    6.5.6 Additive Operators

    1. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

    and its nearly tightly coupled relationship with...

    6.5.2.4 Postfix increment and decrement operators Constraints

    1. The operand of the postfix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.

    Semantics

    1. The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand. With respect to an indeterminately-sequenced function call, the operation of postfix ++ is a single evaluation. Postfix ++ on an object with atomic type is a read-modify-write operation with memory_order_seq_cst memory order semantics.98)

    2. The postfix -- operator is analogous to the postfix ++ operator, except that the value of the operand is decremented (that is, the value 1 of the appropriate type is subtracted from it).

    Forward references: additive operators (6.5.6), compound assignment (6.5.16.2).

    The very reason for using the post-decrement operator in the posted sample is to avoid evaluating an eventually-invalid address value against the base address of the array. For example, the code above was a refactor of the following:

    #include <stdio.h>
    #include <string.h>
    
    int main() 
    {
        char s[] = "some string";
    
        size_t len = strlen(s);    
        char *t = s + len - 1;
        while(t >= s) 
        {
            fputc(*t, stdout);
            t = t - 1;
        }
        fputc('\n', stdout);
    }
    

    Forgetting for a moment this has a non-zero-length string for s, this general algorithm clearly has issues (perhaps not as clearly to some). If s[] were instead "", then t would be assigned a value of s-1, which itself is not in the valid range of s through its one-past-address, and the evaluation for comparison against s that ensues is no good. If s has non-zero length, that addresses the initial s-1 problem, but only temporarily, as eventually this is still counting on that value (whatever it is) being valid for comparison against s to terminate the loop. It could be worse. it could have naively been:

        size_t len = strlen(s) - 1;
        char *t = s + len;
    

    This has disaster written all over it if s were a zero-length string. The refactored code of this question opened with was intended to address all of these issues. But...

    My paranoia may be getting to me, but it isn't paranoia if they're really all out to get you. So, per the standard (these sections, or perhaps others), does the original code (scroll to the top of this novel if you forgot what it looks like by now) indeed invoke undefined behavior or not?

    解决方案

    I am pretty certain that the result of the post-decrement in this case is indeed undefined behaviour. The post-decrement clearly subtracts one from a pointer to the beginning of an object, so the result does not point to an element of the same array, and by the definition of pointer arithmetic (§6.5.6/8, as cited in the OP) that's undefined behaviour. The fact that you never use the resulting pointer is irrelevant.

    What's wrong with:

    char *t = s + strlen(s);
    while (t > s) fputc(*--t, stdout);
    


    Interesting but irrelevant fact: The implementation of reverse iterators in the standard C++ library usually holds in the reverse iterator a pointer to one past the target element. This allows the reverse iterator to be used normally without ever involving a pointer to "one before the beginning" of the container, which would be UB, as above.

    这篇关于关于是否已经处理数组未定义行为基本指针申请后递减?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆