Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #148186 > unrolled thread

sizeof object got by dereferencing uniniitalized pointer

Started by"V.Subramanian, India" <subramanian100in@yahoo.com>
First post2019-11-28 05:20 -0800
Last post2019-11-30 15:28 -0800
Articles 15 on this page of 35 — 12 participants

Back to article view | Back to comp.lang.c


Contents

  sizeof object got by dereferencing uniniitalized pointer "V.Subramanian, India" <subramanian100in@yahoo.com> - 2019-11-28 05:20 -0800
    Re: sizeof object got by dereferencing uniniitalized pointer Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-11-28 05:37 -0800
      Re: sizeof object got by dereferencing uniniitalized pointer mark.bluemel@gmail.com - 2019-11-28 05:59 -0800
        Re: sizeof object got by dereferencing uniniitalized pointer David Brown <david.brown@hesbynett.no> - 2019-11-28 15:51 +0100
          Re: sizeof object got by dereferencing uniniitalized pointer Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-11-28 07:27 -0800
            Re: sizeof object got by dereferencing uniniitalized pointer Herbert Rosenau <nospam@hrosenau.de> - 2019-11-28 17:28 +0100
              Re: sizeof object got by dereferencing uniniitalized pointer Thiago Adams <thiago.adams@gmail.com> - 2019-11-28 08:51 -0800
                Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-28 17:36 +0000
                  Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-28 16:37 -0800
                    Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-29 01:27 +0000
                      Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-28 17:47 -0800
                        Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-28 21:16 -0500
                          Re: sizeof object got by dereferencing uniniitalized pointer Keith Thompson <kst-u@mib.org> - 2019-11-29 00:38 -0800
                            Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-29 09:03 -0500
                              Re: sizeof object got by dereferencing uniniitalized pointer Keith Thompson <kst-u@mib.org> - 2019-11-29 13:15 -0800
                                Re: sizeof object got by dereferencing uniniitalized pointer Keith Thompson <kst-u@mib.org> - 2019-11-29 13:38 -0800
                            Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-29 07:09 -0800
                          Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-29 06:10 -0800
                            Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-29 11:12 -0500
                              Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-12-01 09:32 -0800
                      Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-29 07:39 -0800
                        Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-29 22:00 +0000
                          Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-29 22:40 -0500
                            Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-30 11:19 +0000
                              Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-30 09:01 -0500
                          Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-12-01 10:28 -0800
                Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-28 12:49 -0500
                  Re: sizeof object got by dereferencing uniniitalized pointer Thiago Adams <thiago.adams@gmail.com> - 2019-11-28 11:26 -0800
                    Re: sizeof object got by dereferencing uniniitalized pointer Richard Damon <Richard@Damon-Family.org> - 2019-11-28 17:19 -0500
                      Re: sizeof object got by dereferencing uniniitalized pointer Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-28 17:26 -0800
                  Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-28 22:34 +0000
            Re: sizeof object got by dereferencing uniniitalized pointer Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-11-28 17:34 +0000
        You gotta wonder why people say things like this! (Was: sizeof object got by dereferencing uniniitalized pointer) gazelle@shell.xmission.com (Kenny McCormack) - 2019-11-28 17:19 +0000
      Re: sizeof object got by dereferencing uniniitalized pointer Keith Thompson <kst-u@mib.org> - 2019-11-28 14:55 -0800
        Re: sizeof object got by dereferencing uniniitalized pointer "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2019-11-30 15:28 -0800

Page 2 of 2 — ← Prev page 1 [2]


#148295

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-11-29 07:39 -0800
Message-ID<86k17i7kuh.fsf@linuxsc.com>
In reply to#148210
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:

> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>> <cut>
>>>
>>>> Only when applied to variably types, sizeof is in runtime.
>>>>
>>>>   int n = 10;
>>>>
>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>
>>> Why would you not write
>>>
>>>    int (*buf)[n] = malloc(sizof *buf);
>>>
>>> ?
>>
>> Perhaps because, technically speaking, the right-hand side here
>> has undefined behavior?
>
> Yes, it has.  That has always puzzled me.  Do you think that this usage
> was specifically being ruled out, or is it a casualty of having made an
> overly simple decision[1]?

I have now looked into this further and can offer a more informed
answer.  More below.

> This whole areas makes me uneasy.  I've never been able to find a
> reasonable example where an expression operand of sizeof needs to be
> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>
>   sizeof (int[f()])
>
> despite the standard not explaining what it means to evaluate a type
> (though its not hard to guess what is intended).  Do the rules make
> sense to you?  [I note your comment about _Generic_ but taking
> it out because it doesn't bear on what I have to say.]

The rules now make sense to me.  The authors wanted to be sure (or
so I believe, in case that needs saying) that they didn't miss any
corner cases, and so used general wording.  They did think about
needless evaluation in 'sizeof' operands, and the Standard does
have a provision which talks about that;  in fact that provision
evolved while C99 was being written.  (If anyone cares they might
want to compare n869 to later versions of the C99 standard.)

Now, I hear you ask, _what_ corner cases?  Here is one, taken from
my recent reponse to Keith Thompson:

    int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };

This declaration is legal C99, provided of course there are
suitable declarations for the identifiers 'something' and 'ident'
in scope.  The operand is an expression, not a type name, although
of course the expression does have a type name within it.  The
operand expression needs to be evaluated, at least in part, in
order to determine the result of the sizeof operator.  Rather than
concoct some carefully chosen language that does some sort of
partial evalution for just those subexpressions that are needed
to produce the result, the authors chose the simple rule that
any VLA operand argument is evaluated.  That decision has some
unfortunate consequences for undefined behavior, but in the 1990s
there wasn't as much hysteria regarding undefined behavior as
there is now.

So for better or worse there is my somewhat-more-informed current
view of the matter.

[toc] | [prev] | [next] | [standalone]


#148303

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-11-29 22:00 +0000
Message-ID<87pnha2vhx.fsf@bsb.me.uk>
In reply to#148295
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>
>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>
>>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>>> <cut>
>>>>
>>>>> Only when applied to variably types, sizeof is in runtime.
>>>>>
>>>>>   int n = 10;
>>>>>
>>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>
>>>> Why would you not write
>>>>
>>>>    int (*buf)[n] = malloc(sizof *buf);
>>>>
>>>> ?
>>>
>>> Perhaps because, technically speaking, the right-hand side here
>>> has undefined behavior?
>>
>> Yes, it has.  That has always puzzled me.  Do you think that this usage
>> was specifically being ruled out, or is it a casualty of having made an
>> overly simple decision[1]?
>
> I have now looked into this further and can offer a more informed
> answer.  More below.
>
>> This whole areas makes me uneasy.  I've never been able to find a
>> reasonable example where an expression operand of sizeof needs to be
>> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>>
>>   sizeof (int[f()])
>>
>> despite the standard not explaining what it means to evaluate a type
>> (though its not hard to guess what is intended).  Do the rules make
>> sense to you?  [I note your comment about _Generic_ but taking
>> it out because it doesn't bear on what I have to say.]
>
> The rules now make sense to me.  The authors wanted to be sure (or
> so I believe, in case that needs saying) that they didn't miss any
> corner cases, and so used general wording.

I didn't say what I meant, sorry.  I wanted to ask if you think the
rules are the right ones.  I feel the sacrifice (to undefined behaviour)
of patterns like

  memset(ptr, 0, sizeof *ptr);

to be unfortunate.  Mind you, VLAs, and the even more useful variably
modified types, have been thrown under the bus anyway.

> They did think about
> needless evaluation in 'sizeof' operands, and the Standard does
> have a provision which talks about that;  in fact that provision
> evolved while C99 was being written.

I would have argued for a limited form of evaluation -- evaluation
sufficient to determine the type.  I think the rules would not be too
complicated, and it would be the natural place to specify what
"evaluating" a type name means.

> (If anyone cares they might
> want to compare n869 to later versions of the C99 standard.)
>
> Now, I hear you ask, _what_ corner cases?  Here is one, taken from
> my recent reponse to Keith Thompson:
>
>     int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };
>
> This declaration is legal C99, provided of course there are
> suitable declarations for the identifiers 'something' and 'ident'
> in scope.  The operand is an expression, not a type name, although
> of course the expression does have a type name within it.  The
> operand expression needs to be evaluated, at least in part, in
> order to determine the result of the sizeof operator.

The same effect can be achieved without a compound literal by using a
cast.  Is there a subtle point I'm missing that relies on the CL?

> Rather than
> concoct some carefully chosen language that does some sort of
> partial evalution for just those subexpressions that are needed
> to produce the result, the authors chose the simple rule that
> any VLA operand argument is evaluated.  That decision has some
> unfortunate consequences for undefined behavior, but in the 1990s
> there wasn't as much hysteria regarding undefined behavior as
> there is now.

Maybe " evaluation sufficient to determine the type" is more complicated
than I imagine.  I wonder if it was every considered.

> So for better or worse there is my somewhat-more-informed current
> view of the matter.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#148308

FromRichard Damon <Richard@Damon-Family.org>
Date2019-11-29 22:40 -0500
Message-ID<wwlEF.120234$P37.83682@fx33.iad>
In reply to#148303
On 11/29/19 5:00 PM, Ben Bacarisse wrote:
> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
> 
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>>
>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>>
>>>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>>>> <cut>
>>>>>
>>>>>> Only when applied to variably types, sizeof is in runtime.
>>>>>>
>>>>>>   int n = 10;
>>>>>>
>>>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>>
>>>>> Why would you not write
>>>>>
>>>>>    int (*buf)[n] = malloc(sizof *buf);
>>>>>
>>>>> ?
>>>>
>>>> Perhaps because, technically speaking, the right-hand side here
>>>> has undefined behavior?
>>>
>>> Yes, it has.  That has always puzzled me.  Do you think that this usage
>>> was specifically being ruled out, or is it a casualty of having made an
>>> overly simple decision[1]?
>>
>> I have now looked into this further and can offer a more informed
>> answer.  More below.
>>
>>> This whole areas makes me uneasy.  I've never been able to find a
>>> reasonable example where an expression operand of sizeof needs to be
>>> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>>>
>>>   sizeof (int[f()])
>>>
>>> despite the standard not explaining what it means to evaluate a type
>>> (though its not hard to guess what is intended).  Do the rules make
>>> sense to you?  [I note your comment about _Generic_ but taking
>>> it out because it doesn't bear on what I have to say.]
>>
>> The rules now make sense to me.  The authors wanted to be sure (or
>> so I believe, in case that needs saying) that they didn't miss any
>> corner cases, and so used general wording.
> 
> I didn't say what I meant, sorry.  I wanted to ask if you think the
> rules are the right ones.  I feel the sacrifice (to undefined behaviour)
> of patterns like
> 
>   memset(ptr, 0, sizeof *ptr);
> 
> to be unfortunate.  Mind you, VLAs, and the even more useful variably
> modified types, have been thrown under the bus anyway.
> 

That isn't a good example, as if ptr isn't valid the fact that sizeof
might generate UB pales to the effect of doing a memset on what it
points to will do.

  ptr = malloc(sizeof *ptr);

on the other hand would be a reasonable example of what I think you are
referring to. Making this technically Undefined behavior is regrettable,
but if the choice is between making it UB, with the possibility that the
implementation can define that it just works as expected, or having the
standard make a promise that implementations can't meet, and thus be
defective (or maybe they can do it but at great inefficiency) then it
makes sense to err initially on the side of creating UB, and if desired,
later the rules on the implementation can be tightened up, and things
are backwards compatible, while adding restrictions on promises would be
a breaking change.

>> They did think about
>> needless evaluation in 'sizeof' operands, and the Standard does
>> have a provision which talks about that;  in fact that provision
>> evolved while C99 was being written.
> 
> I would have argued for a limited form of evaluation -- evaluation
> sufficient to determine the type.  I think the rules would not be too
> complicated, and it would be the natural place to specify what
> "evaluating" a type name means.
> 
>> (If anyone cares they might
>> want to compare n869 to later versions of the C99 standard.)
>>
>> Now, I hear you ask, _what_ corner cases?  Here is one, taken from
>> my recent reponse to Keith Thompson:
>>
>>     int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };
>>
>> This declaration is legal C99, provided of course there are
>> suitable declarations for the identifiers 'something' and 'ident'
>> in scope.  The operand is an expression, not a type name, although
>> of course the expression does have a type name within it.  The
>> operand expression needs to be evaluated, at least in part, in
>> order to determine the result of the sizeof operator.
> 
> The same effect can be achieved without a compound literal by using a
> cast.  Is there a subtle point I'm missing that relies on the CL?

Yes, a cast can do the same too.
> 
>> Rather than
>> concoct some carefully chosen language that does some sort of
>> partial evalution for just those subexpressions that are needed
>> to produce the result, the authors chose the simple rule that
>> any VLA operand argument is evaluated.  That decision has some
>> unfortunate consequences for undefined behavior, but in the 1990s
>> there wasn't as much hysteria regarding undefined behavior as
>> there is now.
> 
> Maybe " evaluation sufficient to determine the type" is more complicated
> than I imagine.  I wonder if it was every considered.

It may be that such wording seems imprecise. For instance, if the
expression includes the comma operator, does the fact that the first
value not effect the results mean it doesn't get evaluated? what if it
includes a function call so there is a possibility of side effects? What
if it somehow knows that it doesn't have side effects, but might invoke UB?
> 
>> So for better or worse there is my somewhat-more-informed current
>> view of the matter.
> 

[toc] | [prev] | [next] | [standalone]


#148310

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-11-30 11:19 +0000
Message-ID<875zj1393a.fsf@bsb.me.uk>
In reply to#148308
Richard Damon <Richard@Damon-Family.org> writes:

> On 11/29/19 5:00 PM, Ben Bacarisse wrote:
>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>> 
>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>
>>>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>>>
>>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>>>
>>>>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>>>>> <cut>
>>>>>>
>>>>>>> Only when applied to variably types, sizeof is in runtime.
>>>>>>>
>>>>>>>   int n = 10;
>>>>>>>
>>>>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>>>
>>>>>> Why would you not write
>>>>>>
>>>>>>    int (*buf)[n] = malloc(sizof *buf);
>>>>>>
>>>>>> ?
>>>>>
>>>>> Perhaps because, technically speaking, the right-hand side here
>>>>> has undefined behavior?
>>>>
>>>> Yes, it has.  That has always puzzled me.  Do you think that this usage
>>>> was specifically being ruled out, or is it a casualty of having made an
>>>> overly simple decision[1]?
>>>
>>> I have now looked into this further and can offer a more informed
>>> answer.  More below.
>>>
>>>> This whole areas makes me uneasy.  I've never been able to find a
>>>> reasonable example where an expression operand of sizeof needs to be
>>>> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>>>>
>>>>   sizeof (int[f()])
>>>>
>>>> despite the standard not explaining what it means to evaluate a type
>>>> (though its not hard to guess what is intended).  Do the rules make
>>>> sense to you?  [I note your comment about _Generic_ but taking
>>>> it out because it doesn't bear on what I have to say.]
>>>
>>> The rules now make sense to me.  The authors wanted to be sure (or
>>> so I believe, in case that needs saying) that they didn't miss any
>>> corner cases, and so used general wording.
>> 
>> I didn't say what I meant, sorry.  I wanted to ask if you think the
>> rules are the right ones.  I feel the sacrifice (to undefined behaviour)
>> of patterns like
>> 
>>   memset(ptr, 0, sizeof *ptr);
>> 
>> to be unfortunate.  Mind you, VLAs, and the even more useful variably
>> modified types, have been thrown under the bus anyway.
>> 
>
> That isn't a good example, as if ptr isn't valid the fact that sizeof
> might generate UB pales to the effect of doing a memset on what it
> points to will do.

Yes, thanks.  A chain of edits led to this silly example because I
wanted something other than

>   ptr = malloc(sizeof *ptr);

which had already come up.  I thought I'd used memcpy(d, s, sizeof *s)
but then edited it to something daft!

>
> on the other hand would be a reasonable example of what I think you are
> referring to. Making this technically Undefined behavior is regrettable,
> but if the choice is between making it UB, with the possibility that the
> implementation can define that it just works as expected, or having the
> standard make a promise that implementations can't meet, and thus be
> defective (or maybe they can do it but at great inefficiency) then it
> makes sense to err initially on the side of creating UB, and if desired,
> later the rules on the implementation can be tightened up, and things
> are backwards compatible, while adding restrictions on promises would be
> a breaking change.
>
>>> They did think about
>>> needless evaluation in 'sizeof' operands, and the Standard does
>>> have a provision which talks about that;  in fact that provision
>>> evolved while C99 was being written.
>> 
>> I would have argued for a limited form of evaluation -- evaluation
>> sufficient to determine the type.  I think the rules would not be too
>> complicated, and it would be the natural place to specify what
>> "evaluating" a type name means.
>> 
>>> (If anyone cares they might
>>> want to compare n869 to later versions of the C99 standard.)
>>>
>>> Now, I hear you ask, _what_ corner cases?  Here is one, taken from
>>> my recent reponse to Keith Thompson:
>>>
>>>     int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };
>>>
>>> This declaration is legal C99, provided of course there are
>>> suitable declarations for the identifiers 'something' and 'ident'
>>> in scope.  The operand is an expression, not a type name, although
>>> of course the expression does have a type name within it.  The
>>> operand expression needs to be evaluated, at least in part, in
>>> order to determine the result of the sizeof operator.
>> 
>> The same effect can be achieved without a compound literal by using a
>> cast.  Is there a subtle point I'm missing that relies on the CL?
>
> Yes, a cast can do the same too.
>> 
>>> Rather than
>>> concoct some carefully chosen language that does some sort of
>>> partial evalution for just those subexpressions that are needed
>>> to produce the result, the authors chose the simple rule that
>>> any VLA operand argument is evaluated.  That decision has some
>>> unfortunate consequences for undefined behavior, but in the 1990s
>>> there wasn't as much hysteria regarding undefined behavior as
>>> there is now.
>> 
>> Maybe " evaluation sufficient to determine the type" is more complicated
>> than I imagine.  I wonder if it was every considered.
>
> It may be that such wording seems imprecise. For instance, if the
> expression includes the comma operator, does the fact that the first
> value not effect the results mean it doesn't get evaluated? what if it
> includes a function call so there is a possibility of side effects? What
> if it somehow knows that it doesn't have side effects, but might
> invoke UB?

If the second operand is not a variable length array type, then the
first operand need not be evaluated.  But when there is a variably
modified type in the second, you raise an interesting point -- to what
extent should the standard specify when evaluation can be skipped?  For
example, in

  int n = 42, a[n];
  return sizeof (f(), a);

f() can't effect the type, not even if the call were sizeof (f(&n), a).
But in

  extern int n;
  n = 42;
  return sizeof *(f(), (int (*)[n])0);

it can.  I'd go for the simplest rule: evaluate if second operand is a
variably modified type.

As usual with simple rules, it's starting to get complicated!

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#148311

FromRichard Damon <Richard@Damon-Family.org>
Date2019-11-30 09:01 -0500
Message-ID<RCuEF.216995$oT5.125153@fx47.iad>
In reply to#148310
On 11/30/19 6:19 AM, Ben Bacarisse wrote:
> Richard Damon <Richard@Damon-Family.org> writes:
> 
>> On 11/29/19 5:00 PM, Ben Bacarisse wrote:
>>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>>
>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>>
>>>>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>>>>
>>>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>>>>
>>>>>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>>>>>> <cut>
>>>>>>>
>>>>>>>> Only when applied to variably types, sizeof is in runtime.
>>>>>>>>
>>>>>>>>   int n = 10;
>>>>>>>>
>>>>>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>>>>
>>>>>>> Why would you not write
>>>>>>>
>>>>>>>    int (*buf)[n] = malloc(sizof *buf);
>>>>>>>
>>>>>>> ?
>>>>>>
>>>>>> Perhaps because, technically speaking, the right-hand side here
>>>>>> has undefined behavior?
>>>>>
>>>>> Yes, it has.  That has always puzzled me.  Do you think that this usage
>>>>> was specifically being ruled out, or is it a casualty of having made an
>>>>> overly simple decision[1]?
>>>>
>>>> I have now looked into this further and can offer a more informed
>>>> answer.  More below.
>>>>
>>>>> This whole areas makes me uneasy.  I've never been able to find a
>>>>> reasonable example where an expression operand of sizeof needs to be
>>>>> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>>>>>
>>>>>   sizeof (int[f()])
>>>>>
>>>>> despite the standard not explaining what it means to evaluate a type
>>>>> (though its not hard to guess what is intended).  Do the rules make
>>>>> sense to you?  [I note your comment about _Generic_ but taking
>>>>> it out because it doesn't bear on what I have to say.]
>>>>
>>>> The rules now make sense to me.  The authors wanted to be sure (or
>>>> so I believe, in case that needs saying) that they didn't miss any
>>>> corner cases, and so used general wording.
>>>
>>> I didn't say what I meant, sorry.  I wanted to ask if you think the
>>> rules are the right ones.  I feel the sacrifice (to undefined behaviour)
>>> of patterns like
>>>
>>>   memset(ptr, 0, sizeof *ptr);
>>>
>>> to be unfortunate.  Mind you, VLAs, and the even more useful variably
>>> modified types, have been thrown under the bus anyway.
>>>
>>
>> That isn't a good example, as if ptr isn't valid the fact that sizeof
>> might generate UB pales to the effect of doing a memset on what it
>> points to will do.
> 
> Yes, thanks.  A chain of edits led to this silly example because I
> wanted something other than
> 
>>   ptr = malloc(sizeof *ptr);
> 
> which had already come up.  I thought I'd used memcpy(d, s, sizeof *s)
> but then edited it to something daft!

My gut feeling is that the malloc example is basically the fundamental
case for this issue. You basically need an operation that needs the size
of a region without actually using the region, and creating one is the
simplest case. Maybe you could also be creating an array/list/dictionary
of such objects or the like, but that still is an allocation.
> 
>>
>> on the other hand would be a reasonable example of what I think you are
>> referring to. Making this technically Undefined behavior is regrettable,
>> but if the choice is between making it UB, with the possibility that the
>> implementation can define that it just works as expected, or having the
>> standard make a promise that implementations can't meet, and thus be
>> defective (or maybe they can do it but at great inefficiency) then it
>> makes sense to err initially on the side of creating UB, and if desired,
>> later the rules on the implementation can be tightened up, and things
>> are backwards compatible, while adding restrictions on promises would be
>> a breaking change.
>>
>>>> They did think about
>>>> needless evaluation in 'sizeof' operands, and the Standard does
>>>> have a provision which talks about that;  in fact that provision
>>>> evolved while C99 was being written.
>>>
>>> I would have argued for a limited form of evaluation -- evaluation
>>> sufficient to determine the type.  I think the rules would not be too
>>> complicated, and it would be the natural place to specify what
>>> "evaluating" a type name means.
>>>
>>>> (If anyone cares they might
>>>> want to compare n869 to later versions of the C99 standard.)
>>>>
>>>> Now, I hear you ask, _what_ corner cases?  Here is one, taken from
>>>> my recent reponse to Keith Thompson:
>>>>
>>>>     int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };
>>>>
>>>> This declaration is legal C99, provided of course there are
>>>> suitable declarations for the identifiers 'something' and 'ident'
>>>> in scope.  The operand is an expression, not a type name, although
>>>> of course the expression does have a type name within it.  The
>>>> operand expression needs to be evaluated, at least in part, in
>>>> order to determine the result of the sizeof operator.
>>>
>>> The same effect can be achieved without a compound literal by using a
>>> cast.  Is there a subtle point I'm missing that relies on the CL?
>>
>> Yes, a cast can do the same too.
>>>
>>>> Rather than
>>>> concoct some carefully chosen language that does some sort of
>>>> partial evalution for just those subexpressions that are needed
>>>> to produce the result, the authors chose the simple rule that
>>>> any VLA operand argument is evaluated.  That decision has some
>>>> unfortunate consequences for undefined behavior, but in the 1990s
>>>> there wasn't as much hysteria regarding undefined behavior as
>>>> there is now.
>>>
>>> Maybe " evaluation sufficient to determine the type" is more complicated
>>> than I imagine.  I wonder if it was every considered.
>>
>> It may be that such wording seems imprecise. For instance, if the
>> expression includes the comma operator, does the fact that the first
>> value not effect the results mean it doesn't get evaluated? what if it
>> includes a function call so there is a possibility of side effects? What
>> if it somehow knows that it doesn't have side effects, but might
>> invoke UB?
> 
> If the second operand is not a variable length array type, then the
> first operand need not be evaluated.  But when there is a variably
> modified type in the second, you raise an interesting point -- to what
> extent should the standard specify when evaluation can be skipped?  For
> example, in
> 
>   int n = 42, a[n];
>   return sizeof (f(), a);
> 
> f() can't effect the type, not even if the call were sizeof (f(&n), a).
> But in
> 
>   extern int n;
>   n = 42;
>   return sizeof *(f(), (int (*)[n])0);
> 
> it can.  I'd go for the simplest rule: evaluate if second operand is a
> variably modified type.
> 
> As usual with simple rules, it's starting to get complicated!
> 

And that is part of the point. The standard allows for fairly simple
implementations, but also allows for fairly sophisticated ones too. It
tries to avoid specifying how smart an implementation must be. In
general evaluating something can have visible side effects, so the
Standard tries to be very explicit in what gets evaluated, (or at least
appears to by the as-if rule, a smart implementation can omit doing
stuff in KNOWS can't be visible). This rule seems to basically be the
result of trying to be consistent without mandating a level of smartness
to the implementation beyond what was already needed.

There probably is a better wording that limits what is evaluated, but
likely when the rule was being created they weren't sure if it covered
everything correctly, and now that we have the rule this way, changing
it might break some existing programs, so it is hard to fix it.

[toc] | [prev] | [next] | [standalone]


#148331

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-12-01 10:28 -0800
Message-ID<86y2vv6guh.fsf@linuxsc.com>
In reply to#148303
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:

> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>>>
>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>>>
>>>>> Thiago Adams <thiago.adams@gmail.com> writes:
>>>>> <cut>
>>>>>
>>>>>> Only when applied to variably types, sizeof is in runtime.
>>>>>>
>>>>>>   int n = 10;
>>>>>>
>>>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>>
>>>>> Why would you not write
>>>>>
>>>>>    int (*buf)[n] = malloc(sizof *buf);
>>>>>
>>>>> ?
>>>>
>>>> Perhaps because, technically speaking, the right-hand side here
>>>> has undefined behavior?
>>>
>>> Yes, it has.  That has always puzzled me.  Do you think that this usage
>>> was specifically being ruled out, or is it a casualty of having made an
>>> overly simple decision[1]?
>>
>> I have now looked into this further and can offer a more informed
>> answer.  More below.
>>
>>> This whole areas makes me uneasy.  I've never been able to find a
>>> reasonable example where an expression operand of sizeof needs to be
>>> evaluated[2] (as the standard mandates) but most compilers evaluate f in
>>>
>>>   sizeof (int[f()])
>>>
>>> despite the standard not explaining what it means to evaluate a type
>>> (though its not hard to guess what is intended).  Do the rules make
>>> sense to you?  [I note your comment about _Generic_ but taking
>>> it out because it doesn't bear on what I have to say.]
>>
>> The rules now make sense to me.  The authors wanted to be sure (or
>> so I believe, in case that needs saying) that they didn't miss any
>> corner cases, and so used general wording.
>
> I didn't say what I meant, sorry.  I wanted to ask if you think the
> rules are the right ones.  I feel the sacrifice (to undefined behaviour)
> of patterns like
>
>   memset(ptr, 0, sizeof *ptr);
>
> to be unfortunate.  Mind you, VLAs, and the even more useful variably
> modified types, have been thrown under the bus anyway.

If what you're asking is, do I think it would be an improvement
to have a different rule that allows more of the typical cases to
be evaluation free and so avoid any possibility of undefined
behavior, the answer is Yes.

>> They did think about
>> needless evaluation in 'sizeof' operands, and the Standard does
>> have a provision which talks about that;  in fact that provision
>> evolved while C99 was being written.
>
> I would have argued for a limited form of evaluation -- evaluation
> sufficient to determine the type.  I think the rules would not be too
> complicated, and it would be the natural place to specify what
> "evaluating" a type name means.

I think I would propose a different approach, namely, a rule that
is both easy to state and easy to test without any deep thinking,
and that covers the usual patterns without any possibility of
undefined behavior.  Without thinking about it too deeply, I might
suggest that operands of sizeof are not evaluated unless the type
is a variable length array type and the operand expression has at
least one 'type-name' in it that declares a variably modified type
(perhaps with a footnote that says variable length array types
may involve some run-time computation to produce the value but
the expression is not evaluated in the usual C sense unless it
satisfies the aforementioned 'type-name' criterion).[*]  If this
works I think it would be a reasonable candidate, especially if
I had a time machine so I could go back and get it into C99, and
similarly go back and manage to get VLAs out from under the bus
where they were so unceremoniously thrown.

[*] Ideally with much better phrasing than that abomination of a
sentence.

>> (If anyone cares they might
>> want to compare n869 to later versions of the C99 standard.)
>>
>> Now, I hear you ask, _what_ corner cases?  Here is one, taken from
>> my recent reponse to Keith Thompson:
>>
>>     int foo = sizeof *(int (*)[ ident( 1 ) ]){ &something };
>>
>> This declaration is legal C99, provided of course there are
>> suitable declarations for the identifiers 'something' and 'ident'
>> in scope.  The operand is an expression, not a type name, although
>> of course the expression does have a type name within it.  The
>> operand expression needs to be evaluated, at least in part, in
>> order to determine the result of the sizeof operator.
>
> The same effect can be achieved without a compound literal by using a
> cast.  Is there a subtle point I'm missing that relies on the CL?

Not that I know of, I think casts work fine too for this purpose.
Using a compound literal is just the first example that popped
into my head.

>> Rather than
>> concoct some carefully chosen language that does some sort of
>> partial evalution for just those subexpressions that are needed
>> to produce the result, the authors chose the simple rule that
>> any VLA operand argument is evaluated.  That decision has some
>> unfortunate consequences for undefined behavior, but in the 1990s
>> there wasn't as much hysteria regarding undefined behavior as
>> there is now.
>
> Maybe " evaluation sufficient to determine the type" is more
> complicated than I imagine.  I wonder if it was every considered.

I think the main thing is this formulation is more complicated
than it needs to be.  Also it seems prone to interpretation
arguments, much like the infamous pre-C11 "the prior value shall
be read only to determine the value to be stored."  Even if that
is unambiguous, it isn't always obvious what is implied.  Here I
think having the boundaries be immediately evident is a decidely
worthwhile property.

[toc] | [prev] | [next] | [standalone]


#148196

FromRichard Damon <Richard@Damon-Family.org>
Date2019-11-28 12:49 -0500
Message-ID<7NTDF.74422$Jw6.61134@fx35.iad>
In reply to#148192
On 11/28/19 11:51 AM, Thiago Adams wrote:
> On Thursday, November 28, 2019 at 1:28:13 PM UTC-3, Herbert Rosenau wrote:
>> Am 28.11.19 um 16:27 schrieb Malcolm McLean:
>>> On Thursday, 28 November 2019 14:51:48 UTC, David Brown  wrote:
>>>>
>>>>
>>>> Personally, I dislike writing "sizeof *p" - to me, it looks like a
>>>> multiplication.  I'd rather have it looking like a function, even though
>>>> it is not really like a function - I'd write "sizeof(*p)".
>>>>
>>> Also, you frequently need the size of an array of objects. So the
>>> form
>>>
>>> sizeof *p * N
>>>
>>> is very common.
>>>
>>
>> sifeof p /* gives the size of p, the pointer p can point to */
>> sizeof *p /* gives the size of a type p can point to */
> 
> Only when applied to variably types, sizeof is in runtime.
> 
>   int n = 10;
>   
>   int(*buf)[n] = malloc(sizeof(int) * n);
>   
>   printf("sizeof buf=%d", (int)(sizeof * buf)); //prints 40
> 
> 


But you could also have had

int n = 10

int (*buf)[n] = 0

and sizeof *buf would still be 40

buf is NOT evaluated.

[toc] | [prev] | [next] | [standalone]


#148197

FromThiago Adams <thiago.adams@gmail.com>
Date2019-11-28 11:26 -0800
Message-ID<371f212f-1008-443a-ba0e-3c9acd732902@googlegroups.com>
In reply to#148196
On Thursday, November 28, 2019 at 2:50:04 PM UTC-3, Richard Damon wrote:
> On 11/28/19 11:51 AM, Thiago Adams wrote:
> > On Thursday, November 28, 2019 at 1:28:13 PM UTC-3, Herbert Rosenau wrote:
> >> Am 28.11.19 um 16:27 schrieb Malcolm McLean:
> >>> On Thursday, 28 November 2019 14:51:48 UTC, David Brown  wrote:
> >>>>
> >>>>
> >>>> Personally, I dislike writing "sizeof *p" - to me, it looks like a
> >>>> multiplication.  I'd rather have it looking like a function, even though
> >>>> it is not really like a function - I'd write "sizeof(*p)".
> >>>>
> >>> Also, you frequently need the size of an array of objects. So the
> >>> form
> >>>
> >>> sizeof *p * N
> >>>
> >>> is very common.
> >>>
> >>
> >> sifeof p /* gives the size of p, the pointer p can point to */
> >> sizeof *p /* gives the size of a type p can point to */
> > 
> > Only when applied to variably types, sizeof is in runtime.
> > 
> >   int n = 10;
> >   
> >   int(*buf)[n] = malloc(sizeof(int) * n);
> >   
> >   printf("sizeof buf=%d", (int)(sizeof * buf)); //prints 40
> > 
> > 
> 
> 
> But you could also have had
> 
> int n = 10
> 
> int (*buf)[n] = 0
> 
> and sizeof *buf would still be 40
> 
> buf is NOT evaluated.

I think, internally it creates a new variable that is initialized
when the array is created. Let's say n2 is created and it assumes
the value of n that is 10. if you change n still 10.
the initialization is in runtime and the value is read from some internal variable.

[toc] | [prev] | [next] | [standalone]


#148198

FromRichard Damon <Richard@Damon-Family.org>
Date2019-11-28 17:19 -0500
Message-ID<cKXDF.34374$P82.23324@fx15.iad>
In reply to#148197
On 11/28/19 2:26 PM, Thiago Adams wrote:
> On Thursday, November 28, 2019 at 2:50:04 PM UTC-3, Richard Damon wrote:
>> On 11/28/19 11:51 AM, Thiago Adams wrote:
>>> On Thursday, November 28, 2019 at 1:28:13 PM UTC-3, Herbert Rosenau wrote:
>>>> Am 28.11.19 um 16:27 schrieb Malcolm McLean:
>>>>> On Thursday, 28 November 2019 14:51:48 UTC, David Brown  wrote:
>>>>>>
>>>>>>
>>>>>> Personally, I dislike writing "sizeof *p" - to me, it looks like a
>>>>>> multiplication.  I'd rather have it looking like a function, even though
>>>>>> it is not really like a function - I'd write "sizeof(*p)".
>>>>>>
>>>>> Also, you frequently need the size of an array of objects. So the
>>>>> form
>>>>>
>>>>> sizeof *p * N
>>>>>
>>>>> is very common.
>>>>>
>>>>
>>>> sifeof p /* gives the size of p, the pointer p can point to */
>>>> sizeof *p /* gives the size of a type p can point to */
>>>
>>> Only when applied to variably types, sizeof is in runtime.
>>>
>>>   int n = 10;
>>>   
>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>   
>>>   printf("sizeof buf=%d", (int)(sizeof * buf)); //prints 40
>>>
>>>
>>
>>
>> But you could also have had
>>
>> int n = 10
>>
>> int (*buf)[n] = 0
>>
>> and sizeof *buf would still be 40
>>
>> buf is NOT evaluated.
> 
> I think, internally it creates a new variable that is initialized
> when the array is created. Let's say n2 is created and it assumes
> the value of n that is 10. if you change n still 10.
> the initialization is in runtime and the value is read from some internal variable.
> 

Yes, typically the creation of a variable sized type will create some
code to save the variable information about the type in some manner (it
might get optimized away if the information is available for any needed
range is available from other sources.

The sizeof may need to access that information to compute the size of
that variably modified type, so it will likely have code, but it doesn't
need the variable to have a 'good' value, as the type needed doesn't
depend on value of the variable.

using sizeof on a TYPE, that is variable modified might need to run code
to determine the size. For example sizeof( int[foo()])) will need to
call foo() to determine the size of the array that the type is using.

[toc] | [prev] | [next] | [standalone]


#148209

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-11-28 17:26 -0800
Message-ID<865zj38oc4.fsf@linuxsc.com>
In reply to#148198
Richard Damon <Richard@Damon-Family.org> writes:

> On 11/28/19 2:26 PM, Thiago Adams wrote:
>
>> On Thursday, November 28, 2019 at 2:50:04 PM UTC-3, Richard Damon wrote:
>>
>>> On 11/28/19 11:51 AM, Thiago Adams wrote:
>>>
>>>> On Thursday, November 28, 2019 at 1:28:13 PM UTC-3, Herbert Rosenau wrote:
>>>>
>>>>> Am 28.11.19 um 16:27 schrieb Malcolm McLean:
>>>>>
>>>>>> On Thursday, 28 November 2019 14:51:48 UTC, David Brown  wrote:
>>>>>>>
>>>>>>>
>>>>>>> Personally, I dislike writing "sizeof *p" - to me, it looks like a
>>>>>>> multiplication.  I'd rather have it looking like a function, even though
>>>>>>> it is not really like a function - I'd write "sizeof(*p)".
>>>>>>
>>>>>> Also, you frequently need the size of an array of objects.  So the
>>>>>> form
>>>>>>
>>>>>> sizeof *p * N
>>>>>>
>>>>>> is very common.
>>>>>
>>>>> sifeof p /* gives the size of p, the pointer p can point to */
>>>>> sizeof *p /* gives the size of a type p can point to */
>>>>
>>>> Only when applied to variably types, sizeof is in runtime.
>>>>
>>>>   int n = 10;
>>>>
>>>>   int(*buf)[n] = malloc(sizeof(int) * n);
>>>>
>>>>   printf("sizeof buf=%d", (int)(sizeof * buf)); //prints 40
>>>
>>> But you could also have had
>>>
>>> int n = 10
>>>
>>> int (*buf)[n] = 0
>>>
>>> and sizeof *buf would still be 40
>>>
>>> buf is NOT evaluated.
>>
>> I think, internally it creates a new variable that is initialized
>> when the array is created.  Let's say n2 is created and it assumes
>> the value of n that is 10. if you change n still 10.
>> the initialization is in runtime and the value is read from some
>> internal variable.
>
> Yes, typically the creation of a variable sized type will create some
> code to save the variable information about the type in some manner (it
> might get optimized away if the information is available for any needed
> range is available from other sources.
>
> The sizeof may need to access that information to compute the size of
> that variably modified type, so it will likely have code, but it doesn't
> need the variable to have a 'good' value, [...]

Strictly speaking, if the variable used does not have a 'good'
value (which 'buf' doesn't in this case), the result of doing
the sizeof has undefined behavior.  As a practical matter that
is unlikely to have any ill effects, but as far as the letter of
the law goes 'sizeof *buf' here is a programming transgression.
The operand of sizeof is evaluated, with all the requirements
that go with that, when the operand has a type that is a variable
length array type, which is the case for the operand '*buf' here.

[toc] | [prev] | [next] | [standalone]


#148199

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-11-28 22:34 +0000
Message-ID<871rtr4om3.fsf@bsb.me.uk>
In reply to#148196
Richard Damon <Richard@Damon-Family.org> writes:
<cut>
> But you could also have had
>
> int n = 10
>
> int (*buf)[n] = 0
>
> and sizeof *buf would still be 40
>
> buf is NOT evaluated.

The operand of sizeof is evaluated when its type is a variable length
array type (6.5.3.4 p2).  gcc agrees:

  $ cat t.c
  #include <stdio.h>
  #include <stdlib.h>

  int exit_zero(void) { exit(EXIT_FAILURE); return 0; }

  int main(void)
  {
  #ifdef VLA
       int n = 10;
  #else
  #define n 10
  #endif
       int (*buf)[n];
       printf("size = %zu\n", sizeof buf[exit_zero()]);
  }
  $ gcc -std=c99 t.c
  $ ./a.out
  size = 40
  $ gcc -DVLA -std=c99 t.c
  $ ./a.out		
  $

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#148194

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-11-28 17:34 +0000
Message-ID<87d0db52hd.fsf@bsb.me.uk>
In reply to#148190
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:

> On Thursday, 28 November 2019 14:51:48 UTC, David Brown  wrote:
>> 
>> 
>> Personally, I dislike writing "sizeof *p" - to me, it looks like a
>> multiplication.  I'd rather have it looking like a function, even though
>> it is not really like a function - I'd write "sizeof(*p)".  
>>
> Also, you frequently need the size of an array of objects. So the
> form
>
> sizeof *p * N
>
> is very common.

That's sad.  The form

  N * sizeof *p

should be more common than the above, and there's nothing to stop you
writing

  N * (sizeof *p)

You favour extra parentheses, if I recall correctly, so I this should be
the form you prefer.

For plain sizeof, if you write functions like this: sin(x), one style
that avoids both the "looks like a function" and the "change easily to
using a type" problem is to write

  sizeof (*p)

but I've never seen the need to worry about this.  Operators with names
are commonplace as far as I am concerned, but that's just down to the
set if languages I happen to know.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#148193 — You gotta wonder why people say things like this! (Was: sizeof object got by dereferencing uniniitalized pointer)

Fromgazelle@shell.xmission.com (Kenny McCormack)
Date2019-11-28 17:19 +0000
SubjectYou gotta wonder why people say things like this! (Was: sizeof object got by dereferencing uniniitalized pointer)
Message-ID<qrovjn$uae$1@news.xmission.com>
In reply to#148188
In article <76a74489-4a1f-4561-a01b-b2962a10bae4@googlegroups.com>,
 <mark.bluemel@gmail.com> wrote:
...
>It's perhaps worth mentioning that Malcolm's view isn't universally held.

*No* view is universally held.

Example: the Earth is round.

Example: void main() is a bad idea.

Example: You not cast the return value of malloc.

You get the idea...
-- 
You are a dreadful man, Kenny, for all your ways are the ways of death.
    - Rick C Hodgin -

(P.S. -> https://www.youtube.com/watch?v=sMmTkKz60W8)

[toc] | [prev] | [next] | [standalone]


#148200

FromKeith Thompson <kst-u@mib.org>
Date2019-11-28 14:55 -0800
Message-ID<lnpnhbvcf8.fsf@kst-u.example.com>
In reply to#148187
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
[...]
> The sizeof keyword looks like a function, but it is in fact a built-in
> operator. You are not really derefencing the pointer when you pass it *p,
> you are telling it, at compile time, to take the size of the type p
> points to.

sizeof is an operator.  sizeof(expr) only looks like a function call
because of the unnecessary parentheses.  Similarly, return(expr);
looks like a function call because it's written that way.
Which is exactly why I write `sizeof expr` and `return expr;`.
(And `while (condition)` rather than `while(condition)`.)

sizeof is a special case in C because it's the only operator (other
than _Alignof, but that doesn't take an expression as its operand)
whose symbol is a keyword rather than punctuation, and keywords
look like identifiers.  If C had used $ as the symbol instead,
there wouldn't be any temptation to write `$(expr)`; instead, we'd
just write `$ expr` or even `$expr` (unless parentheses are needed
for disambiguation).

> This is undesireable, and my view is that the form sizeof(*p) should be
> avoided. Some people recommend it because the code won't break if the
> type p points to changes. However you will certainly need to review every
> use of p in this case anyway, so I stick to my advice.

Just use `sizeof *p`.  Understanding how sizeof works should be a
prerequisite to writing code that uses it.  (And that includes being
able to read either `sizeof *p` or `sizeof(*p)` without confusion.)
You need to distinguish clearly between identifiers and keywords.

(I wouldn't write `sizeof*p`, though.)

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#148317

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2019-11-30 15:28 -0800
Message-ID<qrutv6$192g$1@gioia.aioe.org>
In reply to#148200
On 11/28/2019 2:55 PM, Keith Thompson wrote:
> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> [...]
[...]
> Just use `sizeof *p`.  Understanding how sizeof works should be a
> prerequisite to writing code that uses it.  (And that includes being
> able to read either `sizeof *p` or `sizeof(*p)` without confusion.)
> You need to distinguish clearly between identifiers and keywords.
> 
> (I wouldn't write `sizeof*p`, though.)
> 

struct foo* p = malloc(sizeof(*p));

Can be fairly handy. The unnecessary parentheses never bothered me.

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.c


csiph-web