Re: sizeof(const int *) > sizeof(int *)

Discussion in 'C Programming' started by Ben Bacarisse, Jun 14, 2010.

  1. "paul" <no@email> writes:

    > I'm working on an embedded platform where there
    > is over 64kb of flash, but only a few kb of ram.
    >
    > The ram is low in the memory, so a pointer
    > to a non-const integer is 2 bytes.
    > A pointer to a const however is 3 bytes.
    >
    > Obviously casting from a pointer to const,
    > to a pointer to non-const, looses the most
    > significant byte, which makes bad things happen.
    >
    > In this case, it is obviously bad practice, but
    > does the standard say that the cast is UB?


    There is no requirement that a conversion from a pointer to a qualified
    type may be converted to a pointer to the non-qualified type (the
    reverse, converting from T * to T *const, *is* required to work). By
    not saying anything about conversions from T *const to T *, the standard
    leaves the behaviour undefined; even before you use the converted
    pointer for reading [6.3.2.3 p2].

    > (assuming it then only read, and not written to)
    >
    > Also, does the standard state that a pointer to
    > non-const cannot be larger than a pointer to const?
    > (that really would cause problems).


    No, I think that is permitted though I would not be surprised if I have
    missed something that indirectly rules it out. Wait for at least a
    couple more replies!

    --
    Ben.
     
    Ben Bacarisse, Jun 14, 2010
    #1
    1. Advertising

  2. Ben Bacarisse <> writes:

    > "paul" <no@email> writes:

    <snip>
    >> Also, does the standard state that a pointer to
    >> non-const cannot be larger than a pointer to const?
    >> (that really would cause problems).

    >
    > No, I think that is permitted though I would not be surprised if I have
    > missed something that indirectly rules it out.


    Indeed, I missed paragraph 27 of 6.2.5. Typical!

    --
    Ben.
     
    Ben Bacarisse, Jun 15, 2010
    #2
    1. Advertising

  3. Ben Bacarisse <> writes:
    <snip>
    > There is no requirement that a conversion from a pointer to a qualified
    > type may be converted to a pointer to the non-qualified type (the
    > reverse, converting from T * to T *const, *is* required to work).


    Oh dear. The words make my point clear (as does the context of the
    reply) but I've written the wrong type. 6.3.2.3 p2 is about T * and T
    const *.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Jun 15, 2010
    #3
  4. On 14 Giu, 13:21, Ben Bacarisse <> wrote:

    > There is no requirement that a conversion from a pointer to a qualified
    > type may be converted to a pointer to the non-qualified type (the
    > reverse, converting from T * to T *const, *is* required to work).  By
    > not saying anything about conversions from T *const to T *, the standard
    > leaves the behaviour undefined; even before you use the converted
    > pointer for reading [6.3.2.3 p2].


    I don't think that merely converting from T *const to T * is undefined
    behavior because, 6.3.2.3 p8] says "A pointer to an object or
    incomplete type may be converted to a pointer to a different object or
    incomplete type. If the resulting pointer
    is not correctly aligned for the pointed-to type, the behavior is
    undefined. Otherwise, when converted back again, the
    result shall compare equal to the original pointer.".
    This obviously applies also to the case where T * const is converted
    to T *.
    Since "pointers to qualified or unqualified versions of
    compatible types shall have the same representation and alignment
    requirements" [6.2.5 p27], I think that the following is true:

    int * const pci;
    int * pi;
    const int x=2;

    pci = &x;
    /*
    the following line would be constraint violation:
    *pci = 0;
    */
    pi = (int *)pci; // pi is the convertion of pci1
    pci = pi; // converting back gives the same value [6.3.2.3 p8] pci
    had
    // before;
    // at the same time converting from unqualified to
    qualified
    // does not change value [6.3.2.3 p2];
    // hence, pi points to x
    *pi = 0; // this is not constr.v., but it's undef.b. according to
    [6.7.3 p5]

    I think that the purpose of [6.7.3 p5] is to rule out (as undef.b.)
    such
    things. If merely converting from T *const to T * were undefined
    behavior,
    which would be the purpose of [6.7.3 p5] ?

    my 2 cents
    Luca
     
    Luca Forlizzi, Jun 16, 2010
    #4
  5. Luca Forlizzi <> writes:

    > On 14 Giu, 13:21, Ben Bacarisse <> wrote:
    >
    >> There is no requirement that a conversion from a pointer to a qualified
    >> type may be converted to a pointer to the non-qualified type (the
    >> reverse, converting from T * to T *const, *is* required to work).  By
    >> not saying anything about conversions from T *const to T *, the standard
    >> leaves the behaviour undefined; even before you use the converted
    >> pointer for reading [6.3.2.3 p2].

    >
    > I don't think that merely converting from T *const to T * is undefined
    > behavior


    No it's not, but that is not the issue. Did you see the correction I
    posted where I explained that I'd written the wrong type? The words
    describe what I was saying but the types should be T * and T const*.

    > because, 6.3.2.3 p8]


    (You may be using a different document. n1256.pdf has this as p7.)

    > says "A pointer to an object or
    > incomplete type may be converted to a pointer to a different object or
    > incomplete type. If the resulting pointer
    > is not correctly aligned for the pointed-to type, the behavior is
    > undefined. Otherwise, when converted back again, the
    > result shall compare equal to the original pointer.".
    > This obviously applies also to the case where T * const is converted
    > to T *.


    Again you've imported my typo. The question is whether this applies to
    the conversion from T const* to T*. It looks like it does (and we know
    alignment can't be a problem here) but I have a nagging doubt whether
    differently qualified object types count as different object types for
    this purpose.

    The reason for this doubt is that paragraph 2 makes special provision
    for conversion between pointers to qualified and unqualified versions of
    types, and it is specifically one-way:

    For any qualifier q, a pointer to a non-q-qualified type may be
    converted to a pointer to the q-qualified version of the type; the
    values stored in the original and converted pointers shall compare
    equal.

    If we take your view of p7, then the conversion from T const* to T * is
    not UB (provided the alignment is OK) but the two pointers need not
    compare equal! I am not sure what that means in practise.

    [I am going to snip your example because it uses int *const when I think
    you meant int const*. It only adds to the confusion.]

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Jun 16, 2010
    #5
  6. On 16 Giu, 14:20, Ben Bacarisse <> wrote:


    > No it's not, but that is not the issue.  Did you see the correction I
    > posted where I explained that I'd written the wrong type?  The words
    > describe what I was saying but the types should be T * and T const*.


    yeah, sorry

    >
    > > because, 6.3.2.3 p8]

    >
    > (You may be using a different document.  n1256.pdf has this as p7.)


    yes I was using N1336. Now I switched to 1256!

    >
    > Again you've imported my typo.  The question is whether this applies to
    > the conversion from T const* to T*.  It looks like it does (and we know
    > alignment can't be a problem here) but I have a nagging doubt whether
    > differently qualified object types count as different object types for
    > this purpose.


    sorry for importing the typo. I can't see any reason why
    6.3.2.3 p7 (in 1256) should not apply to the conversion
    form const T * to T *.

    >
    > The reason for this doubt is that paragraph 2 makes special provision
    > for conversion between pointers to qualified and unqualified versions of
    > types, and it is specifically one-way:
    >
    >   [snip]


    I agree that it is strange that p2 is explicitely one way.
    On the other hand, if the conversion form const T * to T *
    was undef. b., I would find useless (thus strange) the first sentence
    of 6.7.3 p5, because without conversion const T* to T*
    I can't see no way to modify a const object without breaking
    a constraint.

    >
    > If we take your view of p7, then the conversion from T const* to T * is
    > not UB (provided the alignment is OK) but the two pointers need not
    > compare equal!  I am not sure what that means in practise.


    I claim that the conversion
    from const T * to T * gives a pointer that compares equal with the
    original pointer.
    Given const int * pci1, *pci2;
    int * pi;

    pi = (int *) pci1; // 1
    pci2 = pi; // 2

    After the execution of lines 1 and 2:
    Since in lines 1 and 2 the value in pci1 is converted to int * and
    back, 6.3.2.3 p7 implies that pci1 and pci2 should compare equal.
    Since in line 2 the value in pi is converted to const int *,
    6.3.2.3 p2 implies that pi and pci2 should compare equal.
    I think that pointer comparison is transitive (is it?) so
    pi and pci1 should compare equal.

    do you agree? :-}

    my 2 cents
    Luca
     
    Luca Forlizzi, Jun 16, 2010
    #6
  7. Luca Forlizzi <> writes:

    > On 16 Giu, 14:20, Ben Bacarisse <> wrote:

    <snip>
    >> [...]  The question is whether this applies to
    >> the conversion from T const* to T*.  It looks like it does (and we know
    >> alignment can't be a problem here) but I have a nagging doubt whether
    >> differently qualified object types count as different object types for
    >> this purpose.

    >
    > [...] I can't see any reason why
    > 6.3.2.3 p7 (in 1256) should not apply to the conversion
    > form const T * to T *.


    Well, I only have this:

    >> The reason for this doubt is that paragraph 2 makes special provision
    >> for conversion between pointers to qualified and unqualified versions of
    >> types, and it is specifically one-way:


    which obviously does not persuade you. I must say it is only a small
    worry.

    >>   [snip]

    >
    > I agree that it is strange that p2 is explicitely one way.
    > On the other hand, if the conversion form const T * to T *
    > was undef. b., I would find useless (thus strange) the first sentence
    > of 6.7.3 p5, because without conversion const T* to T*
    > I can't see no way to modify a const object without breaking
    > a constraint.


    const int x = 42;
    memset(&x, 0, sizeof x);

    or, to make the use of an lvalue more explicit:

    char *cp = (char *)&x;
    cp[0] = 0;

    Neither is undefined because of p2. Only 6.7.3 p5 makes it undefined.

    >> If we take your view of p7, then the conversion from T const* to T * is
    >> not UB (provided the alignment is OK) but the two pointers need not
    >> compare equal!  I am not sure what that means in practise.

    >
    > I claim that the conversion
    > from const T * to T * gives a pointer that compares equal with the
    > original pointer.
    > Given const int * pci1, *pci2;
    > int * pi;
    >
    > pi = (int *) pci1; // 1
    > pci2 = pi; // 2
    >
    > After the execution of lines 1 and 2:


    [Lets assume that pci1 is not indeterminate]

    > Since in lines 1 and 2 the value in pci1 is converted to int * and
    > back, 6.3.2.3 p7 implies that pci1 and pci2 should compare equal.
    > Since in line 2 the value in pi is converted to const int *,
    > 6.3.2.3 p2 implies that pi and pci2 should compare equal.
    > I think that pointer comparison is transitive (is it?) so
    > pi and pci1 should compare equal.
    >
    > do you agree? :-}


    Nice argument.

    I don't think pointer comparison is transitive in general simply because
    it is not always defined:

    int x, *ip = &x;
    char *cp = (char *)ip;
    void *vp = cp;

    Now cp == vp and vp == ip but cp and ip can't be tested for equality
    without first converting one of them to some other type. However, that
    is a technicality. When it is defined amongst all the participants it
    must be transitive (this follows from it's definition) so I think your
    argument is valid.

    --
    Ben.
     
    Ben Bacarisse, Jun 16, 2010
    #7
  8. On 16 Giu, 20:20, Ben Bacarisse <> wrote:

    > >> The reason for this doubt is that paragraph 2 makes special provision
    > >> for conversion between pointers to qualified and unqualified versions of
    > >> types, and it is specifically one-way:

    >
    > which obviously does not persuade you.  I must say it is only a small
    > worry.


    :)
    well it would if p7 didn't exist! ;-)



    > > I can't see no way to modify a const object without breaking
    > > a constraint.

    >
    >   const int x = 42;
    >   memset(&x, 0, sizeof x);


    ah fine, I didn't know memset

    > or, to make the use of an lvalue more explicit:
    >
    >   char *cp = (char *)&x;
    >   cp[0] = 0;
    >
    > Neither is undefined because of p2.  Only 6.7.3 p5 makes it undefined.


    right. I see (char *)&x defined by the same sentence that defines (int
    *)&x,
    (given that x is a const int).


    > Nice argument.


    thanks! :)

    >
    > I don't think pointer comparison is transitive in general simply because
    > it is not always defined:
    >
    >   int x, *ip = &x;
    >   char *cp = (char *)ip;
    >   void *vp = cp;
    >
    > Now cp == vp and vp == ip but cp and ip can't be tested for equality
    > without first converting one of them to some other type.  However, that
    > is a technicality.  When it is defined amongst all the participants it
    > must be transitive (this follows from it's definition) so I think your


    that's a nice properties, I was a little worried about hidden
    consequences of
    equality not being transitive (when defined)

    Luca
     
    Luca Forlizzi, Jun 17, 2010
    #8
  9. Luca Forlizzi <> writes:

    > On 16 Giu, 20:20, Ben Bacarisse <> wrote:

    <snip>
    >>   const int x = 42;

    <snip>
    >>   char *cp = (char *)&x;
    >>   cp[0] = 0;
    >>
    >> Neither is undefined because of p2.  Only 6.7.3 p5 makes it undefined.

    >
    > right. I see (char *)&x defined by the same sentence that defines (int
    > *)&x,
    > (given that x is a const int).


    First a small point: (char *)&x is much better defined than a conversion
    to some other object type because there is special wording about it: it
    points to the lowest addressable byte. Converting some pointer to, say,
    int * is not nearly so well-defined. It is defined to convert back to
    an equal pointer of the original type (alignment permitting) but that is
    just about all one can be sure of.

    Now to the only remaining difference in our points of view... char *
    and int const* are unambiguously "pointers to different object types" so
    6.3.2.3 p7 clearly applies. However, int * and int const* are pointers
    to "differently qualified versions of [the] type" int. This is the
    wording from the section on types (6.2.5) paragraph 26. Is that enough
    to match the words of 6.3.2.3 p7? I am not 100% sure.

    Note also that section 6.2.5 does not concern itself with qualified types
    until the very end. It states for example that there a five standard
    signed integer types (signed char, short int, int, long int and long
    long int). If int and const int are really different object types then
    surely there should be ten of them? Note also that qualification of a
    type is not one of the ways to derived new types form others. The new
    types are derived by defining array, structure, union, function and
    pointer types.

    When type qualifiers are, finally, explained in p26 the wording does not
    clear up my doubt. I agree it does say that "[the] qualified or
    unqualified versions of a type are distinct types" but that they "belong
    to the same type category". Earlier they are described as "versions of
    [the] type" which is not enough for me to be sure about the conversion
    we are discussing, one way or the other.

    I would be sure of my argument if 6.3.2.3 p7 said "pointers to different
    object type categories" but it does not; the plain words do support your
    interpretation.

    I don't think the matter merits quite the volume of text I have
    generated on it but I hope I have at least explained the nagging doubt I
    have.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Jun 17, 2010
    #9
  10. On 17 Giu, 15:05, Ben Bacarisse <> wrote:

    > First a small point: (char *)&x is much better defined than a conversion
    > to some other object type because there is special wording about it: it
    > points to the lowest addressable byte.  Converting some pointer to, say,
    > int * is not nearly so well-defined.  It is defined to convert back to
    > an equal pointer of the original type (alignment permitting) but that is
    > just about all one can be sure of.


    yes, but in our case the alignment for (int *) was ok...

    >
    > Now to the only remaining difference in our points of view...  char *
    > and int const* are unambiguously "pointers to different object types" so
    > 6.3.2.3 p7 clearly applies.  However, int * and int const* are pointers
    > to "differently qualified versions of [the] type" int.  This is the
    > wording from the section on types (6.2.5) paragraph 26.  Is that enough
    > to match the words of 6.3.2.3 p7?  I am not 100% sure.


    ahh... I see. Now I understand where your doubt comes from.
    Mmm...
    It would be strange if the conversion from, say, (int const *) to
    (double *)
    is defined (in an implementation where alignment requirement is met)
    while the one from (int const *) to (int *) is not.
    Anyway C standard is full of surprises.

    > I don't think the matter merits quite the volume of text I have
    > generated on it but I hope I have at least explained the nagging doubt I
    > have.


    It has. I am tempted to pose the question in comp.std.c, even if it's
    a useless one... :)
     
    Luca Forlizzi, Jun 17, 2010
    #10
  11. Ben Bacarisse

    Tim Rentsch Guest

    Ben Bacarisse <> writes:

    > Luca Forlizzi <> writes:
    >
    >> On 16 Giu, 20:20, Ben Bacarisse <> wrote:

    > <snip>
    >>> const int x = 42;

    > <snip>
    >>> char *cp = (char *)&x;
    >>> cp[0] = 0;
    >>>
    >>> Neither is undefined because of p2. Only 6.7.3 p5 makes it undefined.

    >>
    >> right. I see (char *)&x defined by the same sentence that defines (int
    >> *)&x,
    >> (given that x is a const int).

    >
    > First a small point: (char *)&x is much better defined than a conversion
    > to some other object type because there is special wording about it: it
    > points to the lowest addressable byte. Converting some pointer to, say,
    > int * is not nearly so well-defined. It is defined to convert back to
    > an equal pointer of the original type (alignment permitting) but that is
    > just about all one can be sure of.
    >
    > Now to the only remaining difference in our points of view... char *
    > and int const* are unambiguously "pointers to different object types" so
    > 6.3.2.3 p7 clearly applies. However, int * and int const* are pointers
    > to "differently qualified versions of [the] type" int. This is the
    > wording from the section on types (6.2.5) paragraph 26. Is that enough
    > to match the words of 6.3.2.3 p7? I am not 100% sure.
    >
    > Note also that section 6.2.5 does not concern itself with qualified types
    > until the very end. It states for example that there a five standard
    > signed integer types (signed char, short int, int, long int and long
    > long int). If int and const int are really different object types then
    > surely there should be ten of them? Note also that qualification of a
    > type is not one of the ways to derived new types form others. The new
    > types are derived by defining array, structure, union, function and
    > pointer types.
    >
    > When type qualifiers are, finally, explained in p26 the wording does not
    > clear up my doubt. I agree it does say that "[the] qualified or
    > unqualified versions of a type are distinct types" but that they "belong
    > to the same type category". Earlier they are described as "versions of
    > [the] type" which is not enough for me to be sure about the conversion
    > we are discussing, one way or the other.
    >
    > I would be sure of my argument if 6.3.2.3 p7 said "pointers to different
    > object type categories" but it does not; the plain words do support your
    > interpretation.
    >
    > I don't think the matter merits quite the volume of text I have
    > generated on it but I hope I have at least explained the nagging doubt I
    > have.


    Perhaps this will help.

    The statements in 6.3.2.3 p7 certainly apply to (const int *) and
    (int *). The reason that 6.3.2.3 p2 is written separately from
    6.3.2.3 p2 is that the conditions are different: conversions
    between pointers to types that have the same alignment (as do
    (int) and (const int)) always succeed; also, a (const int *)
    pointer can be compared directly to an (int *) pointer. Because
    the behavior of a qualified type relative to the unqualifed
    version of a type (and pointers to those types) is more defined,
    more can be said about such cases. That doesn't invalidate
    6.3.2.3 p7.

    Admittely the phrasing in 6.2.5 p26 is, shall we say awkward, but
    isn't more than a red herring. The term 'type category', as far
    as I can tell, isn't used in normative text outside of 6.2.5
    p26,27. The construction 'qualified version' of a type is used in
    many places to change between an unqualified type and a qualified
    form of the same unqualified type, when talking about a referenced
    abstract type (eg, "an appropriately qualified version of the
    composite type"). In other words this construction is simply a
    linguistic convenience, not one that carries any deeper
    significance. The key property is the one you pointed out --
    types with different sets (including the empty set) of qualifiers
    are _distinct types_ -- not a defined term but regular text. The
    defined term "qualified version" is simply being defined for
    convenience in later text.
     
    Tim Rentsch, Jun 20, 2010
    #11
  12. Ben Bacarisse

    Tim Rentsch Guest

    Ben Bacarisse <> writes:

    > "paul" <no@email> writes:
    >
    >> I'm working on an embedded platform where there
    >> is over 64kb of flash, but only a few kb of ram.
    >>
    >> The ram is low in the memory, so a pointer
    >> to a non-const integer is 2 bytes.
    >> A pointer to a const however is 3 bytes.
    >>
    >> Obviously casting from a pointer to const,
    >> to a pointer to non-const, looses the most
    >> significant byte, which makes bad things happen.
    >>
    >> In this case, it is obviously bad practice, but
    >> does the standard say that the cast is UB?

    >
    > There is no requirement that a conversion from a pointer to a qualified
    > type may be converted to a pointer to the non-qualified type (the
    > reverse, converting from T * to T *const, *is* required to work). By
    > not saying anything about conversions from T *const to T *, the standard
    > leaves the behaviour undefined; even before you use the converted
    > pointer for reading [6.3.2.3 p2].
    >
    > [Subsequently emended to (T *) and (const T *) ]


    There was a long thread on this some years back. (At least, I
    hope it was more than just last year, hopefully my memory is
    better than that.) If we have two types X and Y, and X and Y
    are the same size and have the same alignment requirements,
    converting from (X*) to (Y*) always works and points to the
    same bytes of memory. In fact it always works if X and Y are
    the same size and it simply happens that the pointer of type
    (X*) is aligned appropriately for an object of type Y. This
    follows from the observation that /pointers always point at an
    object/ (assuming they aren't null of course) and /converting
    to a (void *) points to the same object as the original
    pointer/. This last statement isn't said explicitly in the
    Standard in so many words, but different parts taken together
    lead to that conclusion. After that just follow the logical
    implications of converting to/from/through (void *); the
    conclusion follows.

    (Interested folks can search for the thread, I'm sure it was
    in comp.lang.c or comp.std.c but don't have any better reference
    for it at the moment.)
     
    Tim Rentsch, Jun 20, 2010
    #12
  13. On Wed, 16 Jun 2010 19:20:06 +0100, Ben Bacarisse
    <> wrote:

    > Luca Forlizzi <> writes:


    > > I agree that it is strange that [6.3.2.3]p2 is explicitely one way.
    > > On the other hand, if the conversion form const T * to T *
    > > was undef. b., I would find useless (thus strange) the first sentence
    > > of 6.7.3 p5, because without conversion const T* to T*
    > > I can't see no way to modify a const object without breaking
    > > a constraint.

    >

    (Tiny aside: In English we don't negate both verb and object:
    "I can't see any way" or "I can see no way" or "I see no way".
    But in context it was clear what you meant.)

    > const int x = 42;
    > memset(&x, 0, sizeof x);
    >

    With no cast and assuming <string.h> that's a CV; 6.5.16.1p1 plus
    6.5.2.2p2,7. (Note the '[de]qualified' is on the parameter itself i.e.
    foo (void * const p){ ... } ... foo (&x) assigns to p even though p
    is const and can't be assigned again within the body of foo.)
    (Not-prototyped gets into more issues and I don't want to digress.)

    > or, to make the use of an lvalue more explicit:
    >
    > char *cp = (char *)&x;
    > cp[0] = 0;
    >
    > Neither is undefined because of p2. Only 6.7.3 p5 makes it undefined.
    >

    With a de-const cast in both cases I agree.

    There is a somewhat obscure way to lose const *without* a cast:
    strstr, strchr, memchr, and the strto<num> series. memchr can safely
    be used on any bytes (thus representation of any value of any type);
    str* requires a string, but if plain char doesn't have any trap rep
    (and plain-char-unsigned can't) any other-type representation can be
    put in a buffer plus a '\0' and satisfy the string requirement.
     
    David Thompson, Jul 2, 2010
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Spiros Bousbouras
    Replies:
    40
    Views:
    1,485
    Keith Thompson
    Jan 20, 2007
  2. Replies:
    11
    Views:
    1,108
  3. Javier
    Replies:
    2
    Views:
    565
    James Kanze
    Sep 4, 2007
  4. 0m
    Replies:
    26
    Views:
    1,122
    Tim Rentsch
    Nov 10, 2008
  5. Stargazer

    Re: sizeof(const int *) > sizeof(int *)

    Stargazer, Jun 14, 2010, in forum: C Programming
    Replies:
    14
    Views:
    672
    Ben Bacarisse
    Jul 8, 2010
Loading...

Share This Page