Undefined behaviour with Non-static, non-polymorphic + null pointer?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Alan Woodland

    #1

    Undefined behaviour with Non-static, non-polymorphic + null pointer?

    Hi,

    I'm fairly sure this is undefined behaviour, despite the fact that
    it compiles and 'runs' (prints "this doesn't exist") on all my platforms:

    #include <iostream>

    class foo {
    public:
    void bar() {
    std::cout << "hello evil world!" << std::endl;
    if (this) {
    std::cout << "this exists" << std::endl;
    }
    else {
    std::cout << "this doesn't exist!" << std::endl;
    }
    }
    };

    int main() {
    foo *inst = 0;
    inst->bar();

    return 0;
    }

    Can someone please quote chapter and verse on this one and help me win
    my current "this is a very bad idea" argument I'm having? I'd have
    expected it to be forbidden under some general rule, and no exceptions
    to have been made for it? Or is it actually really legal and defined
    because nothing ever dereferences the this pointer?

    Thanks,
    Alan
  • Victor Bazarov

    #2
    Re: Undefined behaviour with Non-static, non-polymorphic + null pointer?

    Alan Woodland wrote:
    I'm fairly sure this is undefined behaviour, despite the fact that
    it compiles and 'runs' (prints "this doesn't exist") on all my
    platforms:
    >
    #include <iostream>
    >
    class foo {
    public:
    void bar() {
    std::cout << "hello evil world!" << std::endl;
    if (this) {
    std::cout << "this exists" << std::endl;
    }
    else {
    std::cout << "this doesn't exist!" << std::endl;
    }
    }
    };
    >
    int main() {
    foo *inst = 0;
    inst->bar();
    Here you're "using" the pointer that has an invalid value (does not
    point to any object). That's undefined behavour. I could not quickly
    locate the exact passage in the Standard that says that it is, but I
    am sure you can find a mention of it in the archives, just search for
    "dereferenc e null pointer".
    >
    return 0;
    }
    >
    Can someone please quote chapter and verse on this one and help me win
    my current "this is a very bad idea" argument I'm having? I'd have
    expected it to be forbidden under some general rule, and no exceptions
    to have been made for it? Or is it actually really legal and defined
    because nothing ever dereferences the this pointer?
    The expression

    inst->bar()

    is in fact

    (*inst).bar()

    which already dereferences the null pointer 'inst'.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask


    Comment

    • Alan Woodland

      #3
      Re: Undefined behaviour with Non-static, non-polymorphic + null pointer?

      Victor Bazarov wrote:
      Alan Woodland wrote:
      >I'm fairly sure this is undefined behaviour, despite the fact that
      >it compiles and 'runs' (prints "this doesn't exist") on all my
      >platforms:
      >>
      >#include <iostream>
      >>
      >class foo {
      >public:
      > void bar() {
      > std::cout << "hello evil world!" << std::endl;
      > if (this) {
      > std::cout << "this exists" << std::endl;
      > }
      > else {
      > std::cout << "this doesn't exist!" << std::endl;
      > }
      > }
      >};
      >>
      >int main() {
      > foo *inst = 0;
      > inst->bar();
      >
      Here you're "using" the pointer that has an invalid value (does not
      point to any object). That's undefined behavour. I could not quickly
      locate the exact passage in the Standard that says that it is, but I
      am sure you can find a mention of it in the archives, just search for
      "dereferenc e null pointer".
      >
      > return 0;
      >}
      >>
      >Can someone please quote chapter and verse on this one and help me win
      >my current "this is a very bad idea" argument I'm having? I'd have
      >expected it to be forbidden under some general rule, and no exceptions
      >to have been made for it? Or is it actually really legal and defined
      >because nothing ever dereferences the this pointer?
      >
      The expression
      >
      inst->bar()
      >
      is in fact
      >
      (*inst).bar()
      >
      which already dereferences the null pointer 'inst'.
      Thanks. It's funny, I'd never actually though about the implications of
      that in this context before. Just found the following quote which ought
      to convince certain people:

      The Standard says that "p->" is
      converted to "(*p)." (see section 5.2.5) and no matter how you slice it,
      *p is a dereference. Dereferencing a null pointer results in undefined
      behaviour.

      Some compilers may ignore the conversion, but that's part of the
      "undefined" part of the behaviour. You cannot rely on it happening on
      all compilers - not even future releases of your current compiler.

      Alan

      Comment

      • Marco Manfredini

        #4
        Re: Undefined behaviour with Non-static, non-polymorphic + null pointer?

        Alan Woodland wrote:
        Thanks. It's funny, I'd never actually though about the implications
        of that in this context before. Just found the following quote which
        ought to convince certain people:
        Event funnier: According to 5.2.5, this code:
        struct X { static const int x=0; };
        int main() {
        X*x=0;
        x->n;
        }

        invokes UB.

        And if I'd was not to lazy to look it up, I could tell you if

        struct X { enum {x=0}; };
        int main() {
        X*x=0;
        x->n;
        }

        invokes UB or not.

        (I mean, they could really make an appendix "Authoritat ive List of
        UB's", because it's really a nuisance to find these only scattered
        around in the Standard)

        --
        IYesNo yes=YesNoFactor y.getFactoryIns tance().YES;
        yes.getDescript ion().equals(ar ray[0].toUpperCase()) ;

        Comment

        • Victor Bazarov

          #5
          Re: Undefined behaviour with Non-static, non-polymorphic + null pointer?

          Marco Manfredini wrote:
          Alan Woodland wrote:
          >
          >Thanks. It's funny, I'd never actually though about the implications
          >of that in this context before. Just found the following quote which
          >ought to convince certain people:
          >
          Event funnier: According to 5.2.5, this code:
          struct X { static const int x=0; };
          int main() {
          X*x=0;
          x->n;
          }
          >
          invokes UB.
          >
          And if I'd was not to lazy to look it up, I could tell you if
          >
          struct X { enum {x=0}; };
          int main() {
          X*x=0;
          x->n;
          }
          >
          invokes UB or not.
          >
          (I mean, they could really make an appendix "Authoritat ive List of
          UB's", because it's really a nuisance to find these only scattered
          around in the Standard)
          I am not sure how such a list would help. You would still have to
          understand that the postfix expression (x->) dereferences the pointer
          regardless what's following it. How would mentioning that if one
          dereferences a null pointer it's UB help understanding that x->n
          does in fact dereference 'x' (if 'n' is a static member)?

          V
          --
          Please remove capital 'A's when replying by e-mail
          I do not respond to top-posted replies, please don't ask


          Comment

          • werasm

            #6
            Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

            On Nov 21, 4:29 pm, Alan Woodland <aj...@aber.ac. ukwrote:
            The Standard says that "p->" is
            converted to "(*p)." (see section 5.2.5) and no matter how you slice it,
            *p is a dereference. Dereferencing a null pointer results in undefined
            behaviour.
            Yes, but doing this:

            sizeof( static_cast<P*> (0)->member ); //or
            sizeof( *static_cast<P* >(0)->member )

            would not invoke cause behavior (for interest sake) as
            this dereference is "sliced" at compile time.

            W

            Comment

            • Marco Manfredini

              #7
              Re: Undefined behaviour with Non-static, non-polymorphic + null pointer?

              Victor Bazarov wrote:
              >(I mean, they could really make an appendix "Authoritat ive List of
              >UB's", because it's really a nuisance to find these only scattered
              >around in the Standard)
              >
              I am not sure how such a list would help. You would still have to
              understand that the postfix expression (x->) dereferences the pointer
              regardless what's following it. How would mentioning that if one
              dereferences a null pointer it's UB help understanding that x->n
              does in fact dereference 'x' (if 'n' is a static member)?
              Well, for an example 5.2.5 just says that x->y is dereferenced during
              evaluation. So glancing over the paragraph I might remember that
              "dereferenc e" can invoke UB, but what are the details? If *what* is
              dereferenced? And then there is sizeof (and soon decltype) which do not
              evaluate their argument - so am I getting this right that sizeof(x->y)
              should always be defined? I remember that there was a debate about that
              question some time ago on clmc++.

              So I think, that it would be nice, if an (effectual) Appendix would turn
              the UBs inside out and list all UBs with pointers back to the context
              of their premises, like:

              Dereferencing
              If t is of pointer type T and *t(1) is evaluated(2) and t does not point
              to an object of type T (3), it's UB

              (1) When is *t implicitely formed?: see "->"
              (2) When is *t not evaluated? see: sizeof, decltype
              (3) How can t not point to an object of it's declared type: see union,
              reinterpret_cas t, null pointer etc..

              I bet that was shocking!

              --
              IYesNo yes=YesNoFactor y.getFactoryIns tance().YES;
              yes.getDescript ion().equals(ar ray[0].toUpperCase()) ;

              Comment

              • Old Wolf

                #8
                Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

                On Nov 22, 5:59 am, Marco Manfredini <ok_nospam...@p hoyd.netwrote:
                >
                Event funnier: According to 5.2.5, this code:
                struct X { static const int x=0; };
                int main() {
                X*x=0;
                x->n;
                }
                >
                invokes UB.
                It requires a diagnostic, as X has no member 'n'.
                struct X { enum {x=0}; };
                int main() {
                X*x=0;
                x->n;
                }
                >
                invokes UB or not.
                Also requires a diagnostic, as X has no member 'n'.

                Comment

                • Marco Manfredini

                  #9
                  Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

                  Old Wolf wrote:
                  On Nov 22, 5:59 am, Marco Manfredini <ok_nospam...@p hoyd.netwrote:
                  >Event funnier: According to 5.2.5, this code:
                  >struct X { static const int x=0; };
                  >int main() {
                  >X*x=0;
                  >x->n;
                  >}
                  >>
                  >invokes UB.
                  >
                  It requires a diagnostic, as X has no member 'n'.
                  Thank you for your time.

                  s/int x=0/int n=0/g

                  Comment

                  • James Kanze

                    #10
                    Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

                    On Nov 21, 7:15 pm, Marco Manfredini <ok_nospam...@p hoyd.netwrote:
                    Victor Bazarov wrote:
                    (I mean, they could really make an appendix "Authoritat ive List of
                    UB's", because it's really a nuisance to find these only scattered
                    around in the Standard)
                    I am not sure how such a list would help. You would still have to
                    understand that the postfix expression (x->) dereferences the pointer
                    regardless what's following it. How would mentioning that if one
                    dereferences a null pointer it's UB help understanding that x->n
                    does in fact dereference 'x' (if 'n' is a static member)?
                    Well, for an example 5.2.5 just says that x->y is dereferenced
                    during evaluation. So glancing over the paragraph I might
                    remember that "dereferenc e" can invoke UB, but what are the
                    details? If *what* is dereferenced?
                    The pointer. Dereferencing is a run-time action, the result of
                    the * operator.
                    And then there is sizeof (and soon decltype) which do not
                    evaluate their argument - so am I getting this right that
                    sizeof(x->y) should always be defined?
                    Yes. The standard explicitly says that the arguments to sizeof
                    are not evaluated. No run-time behavior.
                    I remember that there was a debate about that question some
                    time ago on clmc++.
                    So I think, that it would be nice, if an (effectual) Appendix
                    would turn the UBs inside out and list all UBs with pointers
                    back to the context of their premises,
                    There's not much to say about pointers: dereferencing a null
                    pointer, or a pointer to one past the end of an array, is
                    undefined behavior (in C++---in C, there are certain special
                    cases where one past the end of an array is allowed).
                    like:
                    Dereferencing
                    If t is of pointer type T and *t(1) is evaluated(2) and t does not point
                    to an object of type T (3), it's UB
                    (1) When is *t implicitely formed?: see "->"
                    Implicit or explicit has nothing to do with it. If the standard
                    says (and it does) that p->f() has the semantics of (*p).f(),
                    then it has the semantics of (*p).f(). I don't see what more
                    needs to be said.
                    (2) When is *t not evaluated? see: sizeof, decltype
                    Again, the standard is fairly explicit, although perhaps not
                    where you'd expect. §3.2/1: "An expression is potentially
                    evaluated unless it is either the operand of the sizeof
                    operator, or the operand of the typeid operator and does not
                    designate an lvalue of polymorphic class type."
                    (3) How can t not point to an object of it's declared type:
                    see union, reinterpret_cas t, null pointer etc..
                    A pointer value can be considered as having one of four
                    categories:

                    -- it points to an object (no problem there),

                    -- it points to one past the end of an array (dereference
                    illegal, but pointer arithmetic still allowed).

                    -- it is null (no dereference, and I think, no pointer
                    arithmetic---but I'm not sure about p+0), and

                    -- anything else (nothing allowed, even lvalue to rvalue
                    conversion is undefined behavior)

                    With regards to unions, nothing changes. A union contains one
                    (and only one) of its members at a time. Any attempt to access
                    any other member is undefined behavior.

                    --
                    James Kanze (GABI Software) email:james.kan ze@gmail.com
                    Conseils en informatique orientée objet/
                    Beratung in objektorientier ter Datenverarbeitu ng
                    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                    Comment

                    • James Kanze

                      #11
                      Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

                      On Nov 21, 6:42 pm, werasm <wer...@gmail.c omwrote:
                      On Nov 21, 4:29 pm, Alan Woodland <aj...@aber.ac. ukwrote:
                      The Standard says that "p->" is
                      converted to "(*p)." (see section 5.2.5) and no matter how you slice it,
                      *p is a dereference. Dereferencing a null pointer results in undefined
                      behaviour.
                      Yes, but doing this:
                      sizeof( static_cast<P*> (0)->member ); //or
                      sizeof( *static_cast<P* >(0)->member )
                      would not invoke cause behavior (for interest sake) as
                      this dereference is "sliced" at compile time.
                      There's no slicing involved, but the standard explicitly says
                      that the arguments of sizeof are not evaluated, so no runtime
                      undefined behavior can result.

                      Note that the fact that they are not evaluated has other
                      implications as well. For example, if you write "sizeof(f() )",
                      you're not required to provide an implementation of f. And if
                      you write "sizeof(f<int>( ))", the template function f is not
                      instantiated for int.

                      --
                      James Kanze (GABI Software) email:james.kan ze@gmail.com
                      Conseils en informatique orientée objet/
                      Beratung in objektorientier ter Datenverarbeitu ng
                      9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                      Comment

                      • werasm

                        #12
                        Re: Undefined behaviour with Non-static, non-polymorphic + nullpointer?

                        On Nov 22, 2:53 pm, James Kanze <james.ka...@gm ail.comwrote:
                        On Nov 21, 6:42 pm, werasm <wer...@gmail.c omwrote:
                        >
                        On Nov 21, 4:29 pm, Alan Woodland <aj...@aber.ac. ukwrote:
                        The Standard says that "p->" is
                        converted to "(*p)." (see section 5.2.5) and no matter how you slice it,
                        *p is a dereference. Dereferencing a null pointer results in undefined
                        behaviour.
                        Yes, but doing this:
                        sizeof( static_cast<P*> (0)->member ); //or
                        sizeof( *static_cast<P* >(0)->member )
                        would not invoke cause behavior (for interest sake) as
                        this dereference is "sliced" at compile time.
                        >
                        There's no slicing involved, but the standard explicitly says
                        that the arguments of sizeof are not evaluated, so no runtime
                        undefined behavior can result.
                        I was not referring to slicing in C++, but to Alan's use of it (i.e
                        "no matter how you "cut/slice/look at" it).
                        Note that the fact that they are not evaluated has other
                        implications as well. For example, if you write "sizeof(f() )",
                        you're not required to provide an implementation of f. And if
                        you write "sizeof(f<int>( ))", the template function f is not
                        instantiated for int.
                        Yes, this is typically used in SFINAE. Good to mention.

                        Regards,

                        Werner

                        Comment

                        Working...