Neatest way to get the end pointer?

  • Follow


I commonly use pointers to iterate thru an array. For example:

    int my_array[X];

    int *p = my_array;
    int const *const pend = my_array + sizeof my_array/sizeof*my_array;

    do *p++ = 42;
    while (pend != p);

(Yes I realise the lack of spaces in the sizeof thing above is 
disgusting, but I've gotten so sick of writing it out that I make it as 
compact as possible)

I can't count how many times I use this construct in my code every day. 
It's a right pain in the ass to always have to write out the long-winded 
intialiser for pend, so I'm considering switching to initialising pend 
as follows:

    int const *const pend = *(&my_array+1);

1) my_array is an int[X]
2) &my_array is an int(*)[X]
3) &my_array+1 is the address of the non-existant array located after 
the current one.
4) *(&my_array+ 1) decays to the address of the first element in the 
non-existant array after the current one, which is also the "pend" 
address for the array that actually exists.

It's a hell of a lot shorter to write, and also I think it's a little 
less vulnerable to typos because you'll most likely get a type mismatch 
if its written wrongly.

Anyway, just wondering what people think of the alternative. Saves me 
that little rush of pissed-off-ness every time I've to write out the 
tedious sizeof thing.

(Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) 
because then I'd have to worry about including the necessary header 
file... which is also the reason why I don't use NULL.)

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/5/2008 10:40:37 PM

Tomás Ó hÉilidhe wrote:

> I commonly use pointers to iterate thru an array. For example:
> 
>     int my_array[X];
> 
>     int *p = my_array;
>     int const *const pend = my_array + sizeof my_array/sizeof*my_array;
> 
>     do *p++ = 42;
>     while (pend != p);
Use a understandable macro for the array size, then you can have pend =
my_array + X.
(Also, I'd write it as `for (p = my_array; p < pend; p++) *p = 42;`. Or
even directly p < my_array + X, since a decent compiler should optimize
that, shouldn't it?)

-- 
Army1987 (Replace "NOSPAM" with "email")
0
Reply army1987 (668) 2/5/2008 11:24:52 PM


On Feb 6, 12:40 am, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> I commonly use pointers to iterate thru an array. For example:
>
>     int my_array[X];
>
>     int *p =3D my_array;
>     int const *const pend =3D my_array + sizeof my_array/sizeof*my_array;
>
>     do *p++ =3D 42;
>     while (pend !=3D p);
>
> (Yes I realise the lack of spaces in the sizeof thing above is
> disgusting, but I've gotten so sick of writing it out that I make it as
> compact as possible)
>
> I can't count how many times I use this construct in my code every day.
> It's a right pain in the ass to always have to write out the long-winded
> intialiser for pend, so I'm considering switching to initialising pend
> as follows:
>
>     int const *const pend =3D *(&my_array+1);
>
> 1) my_array is an int[X]
> 2) &my_array is an int(*)[X]
> 3) &my_array+1 is the address of the non-existant array located after
> the current one.
And you invoke undefined behavior.
> It's a hell of a lot shorter to write, and also I think it's a little
> less vulnerable to typos because you'll most likely get a type mismatch
> if its written wrongly.
You could have a macro, or not use pointers (use that X).
> Anyway, just wondering what people think of the alternative. Saves me
> that little rush of pissed-off-ness every time I've to write out the
> tedious sizeof thing.
But it invokes undefined behavior.
> (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array)
> because then I'd have to worry about including the necessary header
> file... which is also the reason why I don't use NULL.)
NULL is declared in many standard C header files, among them is
<stdio.h>
I cannot believe you don't use NULL because you worry about it not
being defined.
0
Reply vippstar (1211) 2/5/2008 11:30:40 PM

vippstar@gmail.com writes:
> On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote:
>> I commonly use pointers to iterate thru an array. For example:
>>
>>     int my_array[X];
[snip]
>> 1) my_array is an int[X]
>> 2) &my_array is an int(*)[X]
>> 3) &my_array+1 is the address of the non-existant array located after
>> the current one.
> And you invoke undefined behavior.
[...]

No, he doesn't.  &my_array+1 is a valid address, just past the end of
my_array.  Computing this address is ok; attempting to dereference it
would invoke UB.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 12:08:16 AM

vippstar@gmail.com wrote:
> On Feb 6, 12:40 am, "Tom?s ? h?ilidhe" <t...@lavabit.com> wrote:
<snip>
> > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array)
> > because then I'd have to worry about including the necessary header
> > file... which is also the reason why I don't use NULL.)
> NULL is declared in many standard C header files, among them is
> <stdio.h>
> I cannot believe you don't use NULL because you worry about it not
> being defined.

I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
be cast when used from a comma expression and the return type of your
function is a pointer, otherwise it might be evaluated as an int, which
might not be wide enough if (sizeof int != sizeof (void *)).

	if (some_test_fails)
		return (errno = ESOMETHING), (void *)NULL;

Likewise, you have to cast it when passing as a vararg.

	execl("/bin/true", "true", (char *)NULL);

Usually I just use an unadorned `0' in my code. I do this in part because,
like the OP, I don't feel like including <stddef.h> or <stdio.h> or any
other header that I don't need. I comment my include statements to describe
what I'm [trying] to import; it got really old doing:

	#include <stddef.h>	/* NULL */

But, to each his own. I don't know very many people who do this. I'm okay
being alone ;)

0
Reply William 2/6/2008 12:09:44 AM

On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote:
> vipps...@gmail.com writes:
> > On Feb 6, 12:40 am, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> >> I commonly use pointers to iterate thru an array. For example:
>
> >>     int my_array[X];
> [snip]
> >> 1) my_array is an int[X]
> >> 2) &my_array is an int(*)[X]
> >> 3) &my_array+1 is the address of the non-existant array located after
> >> the current one.
> > And you invoke undefined behavior.
>
> [...]
>
> No, he doesn't.  &my_array+1 is a valid address, just past the end of
> my_array.  Computing this address is ok; attempting to dereference it
> would invoke UB.
I think that when the result of an expression is a non-valid pointer,
the behavior is undefined.
Correct me if I am wrong, but I have also seen comments in GNU code
like this:
--
/* ANSI C violation */
char * s =3D p - 1;
--
Where p points to the start of a string passed. 's' is never
dereferenced, only used for comparing purposes (while(p > s) or
something similar)
0
Reply vippstar (1211) 2/6/2008 12:14:53 AM

Tom?s ? h?ilidhe <toe@lavabit.com> wrote:
<snip>
> (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) 
> because then I'd have to worry about including the necessary header 
> file... which is also the reason why I don't use NULL.)

I keep all my macros in a single file, e.g. `eval.h'. It has ARRAYLEN,
STRINGIFY, PASTE, XPASTE, PP_NARG (thanks Laurent!), most of *BSD's
<sys/param.h> (MIN, MAX, howmany, etc), and a few other gems I've concocted.

It's rare that I don't use at least one of these in a project. I've never
satisfactorily packaged my other tools, but this seems to work well (though
I'm still torn between `arraylen' and `ARRAYLEN').

0
Reply William 2/6/2008 12:22:17 AM

On Feb 6, 2:09 am, William Ahern <will...@wilbur.25thandClement.com>
wrote:
> vipps...@gmail.com wrote:
> > On Feb 6, 12:40 am, "Tom?s ? h?ilidhe" <t...@lavabit.com> wrote:
> <snip>
> > > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array)
> > > because then I'd have to worry about including the necessary header
> > > file... which is also the reason why I don't use NULL.)
> > NULL is declared in many standard C header files, among them is
> > <stdio.h>
> > I cannot believe you don't use NULL because you worry about it not
> > being defined.
>
> I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
> macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
> be cast when used from a comma expression and the return type of your
> function is a pointer, otherwise it might be evaluated as an int, which
> might not be wide enough if (sizeof int != sizeof (void *)).
I don't think that can happend.
>
>         if (some_test_fails)
>                 return (errno = ESOMETHING), (void *)NULL;
As i said, NULL does not need to be casted here.
You don't need the comma operator either. the expression '(x, y, z)'
evaluates to the type of z with value z.
> Likewise, you have to cast it when passing as a vararg.
You have to cast 0 to (char *)0 too.
>         execl("/bin/true", "true", (char *)NULL);
>
> Usually I just use an unadorned `0' in my code. I do this in part because,
> like the OP, I don't feel like including <stddef.h> or <stdio.h> or any
> other header that I don't need.
That in my opinion is very bad practise for a project.
> I comment my include statements to describe
> what I'm [trying] to import; it got really old doing:
>
>         #include <stddef.h>       /* NULL */
>
> But, to each his own. I don't know very many people who do this. I'm okay
> being alone ;)
Other programmers in your project might not be okay with that thought,
ask them first.
As for me, I wouldn't. Consider
--
char *p;
/* ... */
if(p == 0) /* did you really mean p == 0 or *p == 0 ? */
if(p == NULL) /* clearly ment p == NULL */
--
0
Reply vippstar (1211) 2/6/2008 12:27:40 AM

In article <8fan75-8gn.ln1@wilbur.25thandClement.com>,
William Ahern  <william@wilbur.25thandClement.com> wrote:

>I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
>macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
>be cast when used from a comma expression and the return type of your
>function is a pointer, otherwise it might be evaluated as an int, which
>might not be wide enough if (sizeof int != sizeof (void *)).

>	if (some_test_fails)
>		return (errno = ESOMETHING), (void *)NULL;

No, if the return type of your function is a pointer then the
return value will be converted to the appropriate type as if by
assignment. Assigning a compile-time 0 to a pointer type is guaranteed
to result in a NULL pointer constant.
-- 
   "Okay, buzzwords only. Two syllables, tops."  -- Laurie Anderson
0
Reply roberson2 (8067) 2/6/2008 12:32:20 AM

vipps...@gmail.com wrote:
> Keith Thompson <ks...@mib.org> wrote:
> > vipps...@gmail.com writes:
> > > "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> > > > I commonly use pointers to iterate thru an array.
> > > > For example:
> > > > =A0 =A0 int my_array[X];
> > [snip]
> > > > 1) my_array is an int[X]
> > > > 2) &my_array is an int(*)[X]
> > > > 3) &my_array+1 is the address of the non-existant
> > > > array located after the current one.
> > >
> > > And you invoke undefined behavior.
> >
> > No, he doesn't. =A0&my_array+1 is a valid address,

Though it requires a conversion back to int * if used
as a pointer 'index' compared against an int * iterator.

> > just past the end of my_array. =A0Computing this address
> > is ok; attempting to dereference it would invoke UB.
>
> I think that when the result of an expression is a non-
> valid pointer, the behavior is undefined.

True, but one byte past the end of an array _is_ a valid
pointer.

> Correct me if I am wrong,

Keith already has.

> but I have also seen comments in GNU code like this:
> --
> /* ANSI C violation */
> char * s =3D p - 1;

One byte beyond is fine (though you can't dereference it),
but there are no special rights for one (or more) byte(s)
_before_.

> --
> Where p points to the start of a string passed. 's' is
> never dereferenced, ...

Doesn't matter. Implementations do not have to cope with
'one byte before'. An allocation can be made at the start
of a memory page. Merely calculating an address prior to
such a page may trap. In contrast, implementations _must_
cope with one byte beyond, which simply means that at
least one byte (but typically _at most_ one byte) is
reserved at the end of the memory space.

--
Peter
0
Reply airia (1802) 2/6/2008 12:34:25 AM

On Feb 6, 2:34 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote:
> > Keith Thompson <ks...@mib.org> wrote:
> > > vipps...@gmail.com writes:
> > > > "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> > > > > I commonly use pointers to iterate thru an array.
> > > > > For example:
> > > > >     int my_array[X];
> > > [snip]
> > > > > 1) my_array is an int[X]
> > > > > 2) &my_array is an int(*)[X]
> > > > > 3) &my_array+1 is the address of the non-existant
> > > > > array located after the current one.
>
> > > > And you invoke undefined behavior.
>
> > > No, he doesn't.  &my_array+1 is a valid address,
>
> Though it requires a conversion back to int * if used
> as a pointer 'index' compared against an int * iterator.
>
> > > just past the end of my_array.  Computing this address
> > > is ok; attempting to dereference it would invoke UB.
>
> > I think that when the result of an expression is a non-
> > valid pointer, the behavior is undefined.
>
> True, but one byte past the end of an array _is_ a valid
> pointer.
Ah I see, thanks a lot.
> > Correct me if I am wrong,
>
> Keith already has.
Hehe, sorry mr Keith for doubting :-P.

However, as you said mr Nilsson, he (the OP) is then dereferencing it,
and invoking undefined behavior.
Also, the whole topic is moot.
if sizeof foo / sizeof *foo gives you the elements of foo, then foo
must be an array.
If it's an array it must be defined as 'foo[X]' or similar.
If X is a constant, foo + X is the last pointer (that is not valid to
dereference), if X is an object, it is still foo + X, however foo is
then a variable length array.
Therefore, there is no need for foo + sizeof foo / sizeof *foo.
0
Reply vippstar (1211) 2/6/2008 12:41:12 AM

vippstar@gmail.com wrote in
news:a6def63a-34ea-4248-a6d4-0c13e642e59b@1g2000hsl.googlegroups.com: 

> On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote:
>> vipps...@gmail.com writes:
>> > On Feb 6, 12:40 am, "Tom�s � h�ilidhe" <t...@lavabit.com> wrote:
>> >> I commonly use pointers to iterate thru an array. For example:
>>
>> >>     int my_array[X];
>> [snip]
>> >> 1) my_array is an int[X]
>> >> 2) &my_array is an int(*)[X]
>> >> 3) &my_array+1 is the address of the non-existant array located
>> >> after the current one.
>> > And you invoke undefined behavior.
>>
>> [...]
>>
>> No, he doesn't.  &my_array+1 is a valid address, just past the end of
>> my_array.  Computing this address is ok; attempting to dereference it
>> would invoke UB.
> I think that when the result of an expression is a non-valid pointer,
> the behavior is undefined.
> Correct me if I am wrong, but I have also seen comments in GNU code
> like this:
> --
> /* ANSI C violation */
> char * s = p - 1;
> --
> Where p points to the start of a string passed. 's' is never
> dereferenced, only used for comparing purposes (while(p > s) or
> something similar)

That is different, though. Pointing one past the end of the array is
valid whereas pointing oen before the start of the array is not. 

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

�6.5.6, page 83

Moreover, if the expression P points to the last
element of an array object, the expression (P)+1 points one past the
last element of the array object, and if the expression Q points one
past the last element of an array object, the expression (Q)-1 points to
the last element of the array object. If both the pointer operand and
the result point to elements of the same array object, or one past the
last element of the array object, the evaluation shall not produce an
overflow; otherwise, the behavior is undefined. If the result points one
past the last element of the array object, it shall not be used as the
operand of a unary * operator that is evaluated. 


Sinan


-- 
A. Sinan Unur <1usa@llenroc.ude.invalid>
(remove .invalid and reverse each component for email address)
clpmisc guidelines: <URL:http://www.rehabitation.com/clpmisc.shtml>

0
Reply 1usa (108) 2/6/2008 12:43:22 AM

vippstar@gmail.com writes:

> On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote:
>> I commonly use pointers to iterate thru an array. For example:
>>
>>     int my_array[X];
>>
>>     int *p = my_array;
>>     int const *const pend = my_array + sizeof my_array/sizeof*my_array;
<snip>
>> It's a right pain in the ass to always have to write out the long-winded
>> intialiser for pend, so I'm considering switching to initialising pend
>> as follows:
>>
>>     int const *const pend = *(&my_array+1);
>>
>> 1) my_array is an int[X]
>> 2) &my_array is an int(*)[X]
>> 3) &my_array+1 is the address of the non-existant array located after
>> the current one.

> And you invoke undefined behavior.

Presumably not from the pointer arithmetic alone?  Is it the deference
that you think causes UB?

That is my worry.  One can construct a pointer "one past the end" of
any object, but * can't be applied "if it is evaluated" (6.5.6 p8).  I
take that to refer to the rule that says that when & is applied to *,
neither are evaluated (6.5.3.2 p3).

I can't see anything wrong with:

  int *pend = (void *)(&my_array + 1);

from a language point of view, but it is fragile in that it breaks
when the code moves to a function and my_array becomes a pointer.

Since "C has no array values" is one way of looking at the while
array/pointer relationship in C, one could argue that the * is not
evaluated in

  int *pend = *(&my_array + 1);

Is the utility of this code sufficient to warrant a change to the
meaning of * (or definition of pointer arithmetic) to permit its use
when the "value" would be an array and would therefore be immediately
converted to a pointer?

-- 
Ben.
0
Reply ben.usenet (6515) 2/6/2008 1:01:10 AM

In article <foav6k$pfa$1@canopus.cc.umanitoba.ca>,
Walter Roberson <roberson@ibd.nrc-cnrc.gc.ca> wrote:

>>	if (some_test_fails)
>>		return (errno = ESOMETHING), (void *)NULL;

>No, if the return type of your function is a pointer then the
>return value will be converted to the appropriate type as if by
>assignment.

But the code is revolting anyway.  I can see the temptation to
do something like

    if(condition) a=1, b=2;

to avoid a block, but shoving an extra assignment into a return
statement is an unprovoked assault on readability.

-- Richard
-- 
:wq
0
Reply richard91 (3683) 2/6/2008 1:01:43 AM

vippstar@gmail.com writes:
> On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote:
>> vipps...@gmail.com writes:
>> > On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote:
>> >> I commonly use pointers to iterate thru an array. For example:
>>
>> >>     int my_array[X];
>> [snip]
>> >> 1) my_array is an int[X]
>> >> 2) &my_array is an int(*)[X]
>> >> 3) &my_array+1 is the address of the non-existant array located after
>> >> the current one.
>> > And you invoke undefined behavior.
>>
>> [...]
>>
>> No, he doesn't.  &my_array+1 is a valid address, just past the end of
>> my_array.  Computing this address is ok; attempting to dereference it
>> would invoke UB.
> I think that when the result of an expression is a non-valid pointer,
> the behavior is undefined.
> Correct me if I am wrong, but I have also seen comments in GNU code
> like this:
> --
> /* ANSI C violation */
> char * s = p - 1;
> --
> Where p points to the start of a string passed. 's' is never
> dereferenced, only used for comparing purposes (while(p > s) or
> something similar)

Computing a pointer before the beginning of an array invokes UB;
computing a pointer just past the end of an array does not.

This is mentioned in passing in the answer to question 6.17 in the
comp.lang.c FAQ, <http://www.c-faq.com/> (can anyone find a more
explicit reference?).

The standard's rather long-winded explanation of this is in C99
6.5.6p8 (quoting from n1256; there are no change bars on this
paragraph):

    When an expression that has integer type is added to or subtracted
    from a pointer, the result has the type of the pointer operand. If
    the pointer operand points to an element of an array object, and
    the array is large enough, the result points to an element offset
    from the original element such that the difference of the
    subscripts of the resulting and original array elements equals the
    integer expression. In other words, if the expression P points to
    the i-th element of an array object, the expressions (P)+N
    (equivalently, N+(P)) and (P)-N (where N has the value n) point
    to, respectively, the i+n-th and in-th elements of the array
    object, provided they exist. Moreover, if the expression P points
    to the last element of an array object, the expression (P)+1
    points one past the last element of the array object, and if the
    expression Q points one past the last element of an array object,
    the expression (Q)-1 points to the last element of the array
    object. If both the pointer operand and the result point to
    elements of the same array object, or one past the last element of
    the array object, the evaluation shall not produce an overflow;
    otherwise, the behavior is undefined. If the result points one
    past the last element of the array object, it shall not be used as
    the operand of a unary * operator that is evaluated.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 1:05:42 AM

vipps...@gmail.com wrote:
> Peter Nilsson <ai...@acay.com.au> wrote:
> > ... one byte past the end of an array _is_ a valid
> > pointer.
>
> Hehe, sorry mr Keith for doubting :-P.
>
> However, as you said mr Nilsson, he (the OP) is then
> dereferencing it, and invoking undefined behavior.

I never said the OP dereferenced it. The actual code had
been snipped by that point. Here it is...

  int *p = my_array;
  int const *const pend = my_array + sizeof my_array/
                                     sizeof*my_array;
  do *p++ = 42;
  while (pend != p);

At no stage is pend dereferenced; and the loop exits on
p == pend, so the address is not dereferenced by p
either.

--
Peter
0
Reply airia (1802) 2/6/2008 2:01:51 AM

On Feb 6, 4:01 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote:
> > Peter Nilsson <ai...@acay.com.au> wrote:
> > > ... one byte past the end of an array _is_ a valid
> > > pointer.
>
> > Hehe, sorry mr Keith for doubting :-P.
>
> > However, as you said mr Nilsson, he (the OP) is then
> > dereferencing it, and invoking undefined behavior.
>
> I never said the OP dereferenced it. The actual code had
> been snipped by that point. Here it is...
>
>   int *p = my_array;
>   int const *const pend = my_array + sizeof my_array/
>                                      sizeof*my_array;
>   do *p++ = 42;
>   while (pend != p);
>
> At no stage is pend dereferenced; and the loop exits on
> p == pend, so the address is not dereferenced by p
> either.
Ah, I was a bit confused I guess.
However, OP _does_ dereference it. Not in that snipped, but in 4)
> 4) *(&my_array+ 1) decays to the address of the first element in the
> non-existant array after the current one, which is also the "pend"
> address for the array that actually exists.
0
Reply vippstar (1211) 2/6/2008 2:05:05 AM

On Feb 6, 3:05 am, Keith Thompson <ks...@mib.org> wrote:
> vipps...@gmail.com writes:
> > On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote:
> >> vipps...@gmail.com writes:
> >> > On Feb 6, 12:40 am, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote=
:
> >> >> I commonly use pointers to iterate thru an array. For example:
>
> >> >>     int my_array[X];
> >> [snip]
> >> >> 1) my_array is an int[X]
> >> >> 2) &my_array is an int(*)[X]
> >> >> 3) &my_array+1 is the address of the non-existant array located afte=
r
> >> >> the current one.
> >> > And you invoke undefined behavior.
>
> >> [...]
>
> >> No, he doesn't.  &my_array+1 is a valid address, just past the end of
> >> my_array.  Computing this address is ok; attempting to dereference it
> >> would invoke UB.
> > I think that when the result of an expression is a non-valid pointer,
> > the behavior is undefined.
> > Correct me if I am wrong, but I have also seen comments in GNU code
> > like this:
> > --
> > /* ANSI C violation */
> > char * s =3D p - 1;
> > --
> > Where p points to the start of a string passed. 's' is never
> > dereferenced, only used for comparing purposes (while(p > s) or
> > something similar)
>
> Computing a pointer before the beginning of an array invokes UB;
> computing a pointer just past the end of an array does not.
>
> This is mentioned in passing in the answer to question 6.17 in the
> comp.lang.c FAQ, <http://www.c-faq.com/> (can anyone find a more
> explicit reference?).
>
> The standard's rather long-winded explanation of this is in C99
> 6.5.6p8 (quoting from n1256; there are no change bars on this
> paragraph):
>
>     When an expression that has integer type is added to or subtracted
>     from a pointer, the result has the type of the pointer operand. If
>     the pointer operand points to an element of an array object, and
>     the array is large enough, the result points to an element offset
>     from the original element such that the difference of the
>     subscripts of the resulting and original array elements equals the
>     integer expression. In other words, if the expression P points to
>     the i-th element of an array object, the expressions (P)+N
>     (equivalently, N+(P)) and (P)-N (where N has the value n) point
>     to, respectively, the i+n-th and in-th elements of the array
>     object, provided they exist. Moreover, if the expression P points
>     to the last element of an array object, the expression (P)+1
>     points one past the last element of the array object, and if the
>     expression Q points one past the last element of an array object,
>     the expression (Q)-1 points to the last element of the array
>     object. If both the pointer operand and the result point to
>     elements of the same array object, or one past the last element of
>     the array object, the evaluation shall not produce an overflow;
>     otherwise, the behavior is undefined. If the result points one
>     past the last element of the array object, it shall not be used as
>     the operand of a unary * operator that is evaluated.
I have thought about this, and clearly the standard talks about
arrays.
If we have foo[N], then foo + N is a valid pointer that cannot be
dereferenced.
However, in foo =3D &bar; foo+1 is *not* a valid pointer because &bar is
a pointer, not an array.
Therefore, in OPs example, the expression cannot be computed and does
invoke undefined behavior.
Here is an example of what i am trying to say
--
int * foo;
int bar;
int baz[N];
foo =3D baz + N; /* valid */
foo =3D &bar + 1; /* invalid */
foo =3D &bar; /* valid */
foo++; /* invalid */
--
0
Reply vippstar (1211) 2/6/2008 2:10:33 AM

vipps...@gmail.com wrote:
> Peter Nilsson <ai...@acay.com.au> wrote:
> > ...
> > =A0 int *p =3D my_array;
> > =A0 int const *const pend =3D my_array + sizeof my_array/
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0sizeof*my_array;
> > =A0 do *p++ =3D 42;
> > =A0 while (pend !=3D p);
>
> > At no stage is pend dereferenced; and the loop exits on
> > p =3D=3D pend, so the address is not dereferenced by p
> > either.
>
> Ah, I was a bit confused I guess.
> However, OP _does_ dereference it. Not in that snipped,
> but in 4)
>
> > 4) *(&my_array+ 1) decays to the address of the first
> > element in the non-existant array after the current one,
> > which is also the "pend" address for the array that
> > actually exists.

Again, it is not derefenced...

  The type of    my_array      is int[]
  The type of   &my_array      is int (*)[]
  The type of   &my_array + 1  is int (*)[]
  The type of *(&my_array + 1) is int []

The last expression will decay to an int * when used in the
assignment to pend. At no stage is that pointer dereferenced.

--
Peter
0
Reply airia (1802) 2/6/2008 2:15:19 AM

On Feb 6, 4:15 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote:
> > Peter Nilsson <ai...@acay.com.au> wrote:
> > > ...
> > >   int *p = my_array;
> > >   int const *const pend = my_array + sizeof my_array/
> > >                                      sizeof*my_array;
> > >   do *p++ = 42;
> > >   while (pend != p);
>
> > > At no stage is pend dereferenced; and the loop exits on
> > > p == pend, so the address is not dereferenced by p
> > > either.
>
> > Ah, I was a bit confused I guess.
> > However, OP _does_ dereference it. Not in that snipped,
> > but in 4)
>
> > > 4) *(&my_array+ 1) decays to the address of the first
> > > element in the non-existant array after the current one,
> > > which is also the "pend" address for the array that
> > > actually exists.
>
> Again, it is not derefenced...
>
>   The type of    my_array      is int[]
>   The type of   &my_array      is int (*)[]
>   The type of   &my_array + 1  is int (*)[]
>   The type of *(&my_array + 1) is int []
>
> The last expression will decay to an int * when used in the
> assignment to pend. At no stage is that pointer dereferenced.
What i ment is that (&myarray + 1) is dereferenced, which would be
invalid, and if my reply to mr Thompson is correct, then even
computing &my_array+1 is invalid.
Remember; We are no longer talking about arrays but pointers, the
pointer-after-the-last-element rule does not apply.
0
Reply vippstar (1211) 2/6/2008 2:20:39 AM

vipps...@gmail.com wrote:
> Keith Thompson <ks...@mib.org> wrote:
> > ...The standard's rather long-winded explanation of this
> > is in C99 6.5.6p8 ...
>
> I have thought about this, and clearly the standard talks
> about arrays. If we have foo[N], then foo + N is a valid
> pointer that cannot be dereferenced.
> However, in foo = &bar; foo+1 is *not* a valid pointer
> because &bar is a pointer, not an array.

The cited paragraph is specifically about adding an integer
to a pointer. Arrays decaying to pointers is in 6.3.2.1p3.

--
Peter
0
Reply airia (1802) 2/6/2008 2:24:05 AM

On Feb 6, 4:24 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote:
> > Keith Thompson <ks...@mib.org> wrote:
> > > ...The standard's rather long-winded explanation of this
> > > is in C99 6.5.6p8 ...
>
> > I have thought about this, and clearly the standard talks
> > about arrays. If we have foo[N], then foo + N is a valid
> > pointer that cannot be dereferenced.
> > However, in foo = &bar; foo+1 is *not* a valid pointer
> > because &bar is a pointer, not an array.
>
> The cited paragraph is specifically about adding an integer
> to a pointer. Arrays decaying to pointers is in 6.3.2.1p3.
I don't think you understand what I am trying to say.
--
char foo[N]; /* array of N length */
char c;
char *bar = foo + N; /* valid */
bar = &c + 1; /* invalid */
--
I understand the matter with arrays and the pointer over the last
element, but I believe that does not apply for pointers.
Furthermore, I think you don't understand arrays since you claimed
my_array to be int[] while it is int[X], and &my_array to be int (*)[]
while it is int (*)[X].
0
Reply vippstar (1211) 2/6/2008 2:27:43 AM

vippstar@gmail.com wrote:
> On Feb 6, 2:09 am, William Ahern <will...@wilbur.25thandClement.com>
> wrote:
<snip>
> > I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
> > macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
> > be cast when used from a comma expression and the return type of your
> > function is a pointer, otherwise it might be evaluated as an int, which
> > might not be wide enough if (sizeof int != sizeof (void *)).
> I don't think that can happend.

It does on most of my machines.

> >
> >         if (some_test_fails)
> >                 return (errno = ESOMETHING), (void *)NULL;
> As i said, NULL does not need to be casted here.
> You don't need the comma operator either. the expression '(x, y, z)'
> evaluates to the type of z with value z.

Indeed, exactly as 6.5.17 states. But, what is the type of an unadorned `0'?
(Note, as I mentioned earlier, on *BSD NULL is defined as simply `0'.) Is it
a pointer or an integer? In a comma expression, it's an integer, because
there's no other context to suggest otherwise. Why? I assume because the
language is too rigid. 6.5.17 must be satisified before 6.8.6.4(3)--which
specifies how expression types are coerced when used with return.

Try it in your compiler. GCC, for instance, exhibits this behavior.

	cc     test.c   -o test
	test.c: In function ‘foo’:
	test.c:4: warning: return makes pointer from integer without a cast

This is one of the primary reasons I don't use NULL. Maybe its foolhardy
(likely, even), but if you truly grok how type coercion works in C, then the
visual syntactical benefit of using NULL loses much of its value. It's sort
of like seeing the red headed woman in a screen of falling green glyphs.
(Obligatory The Matrix reference.)

> > Likewise, you have to cast it when passing as a vararg.
> You have to cast 0 to (char *)0 too.
> >         execl("/bin/true", "true", (char *)NULL);
> >
> > Usually I just use an unadorned `0' in my code. I do this in part because,
> > like the OP, I don't feel like including <stddef.h> or <stdio.h> or any
> > other header that I don't need.
> That in my opinion is very bad practise for a project.
> > I comment my include statements to describe
> > what I'm [trying] to import; it got really old doing:
> >
> >         #include <stddef.h>       /* NULL */
> >
> > But, to each his own. I don't know very many people who do this. I'm okay
> > being alone ;)
> Other programmers in your project might not be okay with that thought,
> ask them first.
> As for me, I wouldn't. Consider
> --
> char *p;
> /* ... */
> if(p == 0) /* did you really mean p == 0 or *p == 0 ? */
> if(p == NULL) /* clearly ment p == NULL */

(1) Though you snipped the imaginary intermediate code, I strive to make all
of my function definitions fit within an 80x24 terminal window. So the
declaration is usually as easy to spot as it is here.

(2) How does that compare to?

	if(!p) ...

Granted, I do indeed use the former, but my point is that very likely you
(or most other people) would find it less objectionable. But I don't expend
any effort to ease a programmer's cognitive load at _that_ particular level
of examination. That is, at the level where he's either unsure of the type
of that object, or what it's being used for. If somebody is doing as
brain-dead an operation as, say, search+replace, I don't want him anywhere
near my code.

Instead, I use 8-space hard tabs and lots of whitespace. Mostly K&R style.
That makes actually _reading_ (as opposed to _looking_) at my code easier,
IMO. Even there I'm usually in the minority, though.

0
Reply William 2/6/2008 2:29:19 AM

In article <3596395a-3542-405c-a7f1-a8e91ee1d8d8@h11g2000prf.googlegroups.com>,
 <vippstar@gmail.com> wrote:
>char foo[N]; /* array of N length */
>char c;
>char *bar = foo + N; /* valid */
>bar = &c + 1; /* invalid */

>I understand the matter with arrays and the pointer over the last
>element, but I believe that does not apply for pointers.

Incorrect. A pointer to a non-array object type is indicated by the
standard to be the same thing as a pointer to the beginning of array
of length 1.
-- 
  "Is there any thing whereof it may be said, See, this is new? It hath
  been already of old time, which was before us."    -- Ecclesiastes
0
Reply roberson2 (8067) 2/6/2008 2:53:32 AM

On Tue, 05 Feb 2008 22:40:37 GMT, "Tom�s � h�ilidhe" <toe@lavabit.com>
wrote in comp.lang.c:

> 
> I commonly use pointers to iterate thru an array. For example:
> 
>     int my_array[X];
> 
>     int *p = my_array;
>     int const *const pend = my_array + sizeof my_array/sizeof*my_array;

Why not:

   int const *const pend = my_array + X;

....???

Whether the value that defines the size, which you have tokenized here
as 'X', is a macro or an enumeration constant, and whether it is
defined immediately above the definition of the array or in an
included header or file, it is certainly available for initializing a
pointer in the source line following the definition of the array.

And this, of course, is bullet-proof, even if you decide you need an
array twice as large, or only half the size.

It even works for C99 VLS's (extraneous const keywords omitted for
brevity):

void some_func(int X)
{
   int my_array [X];
   int *pend = my_array [X];


>     int const *const pend = *(&my_array+1);

I had to look closely at this to understand what you are doing.  At
first glance, it appeared you were assigning the address of an array
of X ints to a pointer to int.

Obviously at least some other posters thought you were dereferencing
the one-past pointer.  That may point to their lack of understanding
the calculation of * and & in this type of expression.  But even
though I do understand it, it took a moment to think through weather
or not it applies in the unusual syntax.

If you can't use my suggestion above (an I don't see why not, unless
you are using hard coded magic numbers for the array size, and think
you will forget to change it in the pointer initialization after you
change it in the array definition), they why not the, to me, cleaner:

   int *pend = (int *)(my_array + 1);

....which does not even appear to dereference the pointer, and should
not confuse anybody?

-- 
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
0
Reply jackklein (3932) 2/6/2008 3:40:40 AM

William Ahern <william@wilbur.25thandClement.com> writes:
[...]
> I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
> macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
> be cast when used from a comma expression and the return type of your
> function is a pointer, otherwise it might be evaluated as an int, which
> might not be wide enough if (sizeof int != sizeof (void *)).
>
> 	if (some_test_fails)
> 		return (errno = ESOMETHING), (void *)NULL;

Is there something wrong with:

    if (some_test_fails) {
        errno = ESOMETHING;
        return NULL;
    }

?  The comma operator is rarely necessary outside macro definitions.

> Likewise, you have to cast it when passing as a vararg.
>
> 	execl("/bin/true", "true", (char *)NULL);

Yes, you do.  You also have to cast 0:

    execl("/bin/true", "true", (char*)0);

(execl isn't defined by standard C, of course; let's assume that it's
variadic and that its last argument should be a null pointer of type
char*.)

I know that NULL is defined as a null pointer constant.  That's all I
need to know.  That tells me I can use an unadorned NULL in most
contexts, but I need to cast it if it's an argument whose type is not
specified by the called function's prototype (this almost always means
it's a variadic function).

Of course, using 0 rather than NULL is perfectly legal.  I personally
dislike it on stylistic grounds (I find NULL more explicit), but any C
programmer needs to be able to read and understand code written in
either style.

[snip]

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 3:56:06 AM

Peter Nilsson <airia@acay.com.au> writes:
> vipps...@gmail.com wrote:
>> Peter Nilsson <ai...@acay.com.au> wrote:
>> > ...
>> >   int *p = my_array;
>> >   int const *const pend = my_array + sizeof my_array/
>> >                                      sizeof*my_array;
>> >   do *p++ = 42;
>> >   while (pend != p);
>>
>> > At no stage is pend dereferenced; and the loop exits on
>> > p == pend, so the address is not dereferenced by p
>> > either.
>>
>> Ah, I was a bit confused I guess.
>> However, OP _does_ dereference it. Not in that snipped,
>> but in 4)
>>
>> > 4) *(&my_array+ 1) decays to the address of the first
>> > element in the non-existant array after the current one,
>> > which is also the "pend" address for the array that
>> > actually exists.
>
> Again, it is not derefenced...
>
>   The type of    my_array      is int[]
>   The type of   &my_array      is int (*)[]
>   The type of   &my_array + 1  is int (*)[]
>   The type of *(&my_array + 1) is int []
>
> The last expression will decay to an int * when used in the
> assignment to pend. At no stage is that pointer dereferenced.

I'm not convinced (which is not to say that you're wrong).

The declaration was

    int my_array[X];

so (&my_array + 1) is of type int (*)[X] (let's assume X is constant;
VLAs make my head hurt).

Then *(&my_array + 1) is of type int[X].  This is an expression of
array type, and it's the value of an array object that does not exist.
It then immediately decays to a value of type int*, pointing just past
the end of my_array.  But I think that the intermediate expression
*before* the array-to-pointer conversion invokes UB.  (Though I'd be
surprised if any implementation did anything other than what was
intended; after all, there's no need to load the value of the
nonexistent array object.)

Since it's easy enough to compute the length of the array, you can
achieve the desired result with
    my_array + X
or (as others have mentioned in this thread)
    my_array + sizeof my_array / sizeof *my_array
with the length calculation encapsulated in a macro definition if you
like.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 4:09:12 AM

vippstar@gmail.com writes:
> On Feb 6, 4:15 am, Peter Nilsson <ai...@acay.com.au> wrote:
>> vipps...@gmail.com wrote:
>> > Peter Nilsson <ai...@acay.com.au> wrote:
>> > > ...
>> > >   int *p = my_array;
>> > >   int const *const pend = my_array + sizeof my_array/
>> > >                                      sizeof*my_array;
>> > >   do *p++ = 42;
>> > >   while (pend != p);
>>
>> > > At no stage is pend dereferenced; and the loop exits on
>> > > p == pend, so the address is not dereferenced by p
>> > > either.
>>
>> > Ah, I was a bit confused I guess.
>> > However, OP _does_ dereference it. Not in that snipped,
>> > but in 4)
>>
>> > > 4) *(&my_array+ 1) decays to the address of the first
>> > > element in the non-existant array after the current one,
>> > > which is also the "pend" address for the array that
>> > > actually exists.
>>
>> Again, it is not derefenced...
>>
>>   The type of    my_array      is int[]
>>   The type of   &my_array      is int (*)[]
>>   The type of   &my_array + 1  is int (*)[]
>>   The type of *(&my_array + 1) is int []
>>
>> The last expression will decay to an int * when used in the
>> assignment to pend. At no stage is that pointer dereferenced.
> What i ment is that (&myarray + 1) is dereferenced, which would be
> invalid, and if my reply to mr Thompson is correct, then even
> computing &my_array+1 is invalid.
> Remember; We are no longer talking about arrays but pointers, the
> pointer-after-the-last-element rule does not apply.

No, just computing &my_array+1 is valid; only dereferencing it would
be invalid.

Consider this declaration:

    my_type my_obj;

where my_type is some arbitrary type.  Then &my_obj is the address of
my_obj (obviously), and &my_obj + 1 points just past the end of
my_obj; it's the address of the non-existent object of type my_obj
that immediately follows my_obj in memory.

For purposes of pointer arithmetic, an object of type my_type is
treated as a one-element array of type my_type[1].

All the above is equally valid even of my_type happens to be an array
type.  The object my_array (of type int[X]) is treated as a
single-element array of type int[X][1].

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 4:13:24 AM

In article <87sl0620x5.fsf@kvetch.smov.org> you wrote:
> William Ahern <william@wilbur.25thandClement.com> writes:
> [...]
> > I don't use NULL 'cause I don't know to what it's defined. Usually the NULL
> > macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to
> > be cast when used from a comma expression and the return type of your
> > function is a pointer, otherwise it might be evaluated as an int, which
> > might not be wide enough if (sizeof int != sizeof (void *)).
> >
> >       if (some_test_fails)
> >               return (errno = ESOMETHING), (void *)NULL;

> Is there something wrong with:

>     if (some_test_fails) {
>         errno = ESOMETHING;
>         return NULL;
>     }

Twice the vertical space. Though, lately I've been experimenting with

	if (some_test_fails)
		{ errno = ESOMETHING; return NULL; }

At least as often, however, I would jump to an error handling label. This
would be preferred if the function were complex enough:

	if (0 == (p = malloc(n)))
		goto sysfail;

Setting errno in the comma expression, combined with the return statement,
says something about what state I'm returning to the caller in a way that
using a multi-statement block wouldn't do so... succinctly.

> ?  The comma operator is rarely necessary outside macro definitions.
>
> > Likewise, you have to cast it when passing as a vararg.
> >
> >       execl("/bin/true", "true", (char *)NULL);
>
> Yes, you do.  You also have to cast 0:

Yes. And so, how often do you see people casting NULL as opposed to casting
0 in such a context? Maybe it's a case of the tail wagging the dog, but you
have to appreciate the consistency at some level. Since, as I stated
earlier, I don't like to include headers just for the NULL definition, it's
not too far fetched to keep this style. A significant proportion of my
source code has no need to include headers at all.

All things being equal I would prefer to use NULL, if only because it's
common place. But, all things aren't equal.

0
Reply William 2/6/2008 4:37:11 AM

William Ahern:

> I comment my include statements to describe what I'm [trying] to
> import; it got really old doing: 
> 
>      #include <stddef.h>     /* NULL */


I thought I was the only one that did that :-D  It makes it a hell of a lot 
easier to strip out unneeded header files, and also just to keep track of 
why you included it in the first place.

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/6/2008 8:21:15 AM

Tom�s � h�ilidhe:

> Anyway, just wondering what people think of the alternative. Saves me 
> that little rush of pissed-off-ness every time I've to write out the 
> tedious sizeof thing.


A few people have asked me why I just don't write:

    int const *const pend = my_array + X;

The reason for this is:

1) I can't see the declaration or definition for my_array (and I don't 
want to see it). It could be defined in another source file. I don't 
know what macros is used for its length.

2) The macro that decides the array's length might change. For instance, 
today, the code might be:

    int my_array[AMOUNT_VOWELS_IN_ALPHABET];

while tomorrow it might be:

    int my_array[AMOUNT_VOWELS_IN_ALPHABET + 1];

....while next week it might be:

    int my_array[AMOUNT_CONSONANTS_IN_ALPHABET];


But anyway... back to my proposed alternative.


Originally, I had:  (void*)(&my_array+1)

but still that was a bit of a pain. So I switched to:

    *(&my_array+1)

I'd like to hear what people think in regard to there being UB when I 
dereference at the end. Strictly speaking, there probably is, but 
realisticly speaking, I think it's another thing to be added to the list 
of "UB" that we ignore. Like for instance, here's a UB that I ignore:


    union { int s; unsigned us; } x;

    x.s = -27;

    printf("%u",x.us);  /* Let's see what the bit pattern is */


Anyone with me on this one?

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/6/2008 8:32:45 AM

Tomás Ó hÉilidhe wrote:
> I'd like to hear what people think in regard to there being UB when I 
> dereference at the end. Strictly speaking, there probably is, but 
> realisticly speaking, I think it's another thing to be added to the list 
> of "UB" that we ignore. Like for instance, here's a UB that I ignore:
> 
> 
>     union { int s; unsigned us; } x;
> 
>     x.s = -27;
> 
>     printf("%u",x.us);  /* Let's see what the bit pattern is */
Knowing that x.s isn't going to be accessed anymore, an implementation can
optimize the assignment away. I don't think this is very common, probably
because there is code depending on type punning like that, but that would
not be pointless to do. (And, just in case, I'd prefer to keep distinct
objects and use memcpy.)

-- 
Army1987 (Replace "NOSPAM" with "email")
0
Reply army1987 (668) 2/6/2008 9:56:36 AM

vippstar wrote:

> Here is an example of what i am trying to say
> --
> int * foo;
> int bar;
> int baz[N];
> foo = baz + N; /* valid */
> foo = &bar + 1; /* invalid */
> foo = &bar; /* valid */
> foo++; /* invalid */

6.5.6 Additive operators
[...]
7 For the purposes of these operators, a pointer to an object that is not an element of an
  array behaves the same as a pointer to the first element of an array of length one with the
  type of the object as its element type.

-- 
Army1987 (Replace "NOSPAM" with "email")
0
Reply army1987 (668) 2/6/2008 11:24:27 AM

William Ahern wrote:

> In article <87sl0620x5.fsf@kvetch.smov.org> you wrote:
>> William Ahern <william@wilbur.25thandClement.com> writes:

> Yes. And so, how often do you see people casting NULL as opposed to casting
> 0 in such a context?
Yeah, I don't like unadorned 0 as a null pointer, and I prefer NULL, but I
found (type *)0 OK, and (type *)NULL as somewhat redundant.

(And I *hate* '\0' as a null pointer. Code such as ptr == '\0' always makes
me wonder whether that's a typo for *ptr == '\0'.)

-- 
Army1987 (Replace "NOSPAM" with "email")
0
Reply army1987 (668) 2/6/2008 11:42:36 AM

Tom�s � h�ilidhe wrote:
> 
> Tom�s � h�ilidhe:
> 
> > Anyway, just wondering what people think of the alternative. Saves me
> > that little rush of pissed-off-ness every time I've to write out the
> > tedious sizeof thing.
> 
> A few people have asked me why I just don't write:
> 
>     int const *const pend = my_array + X;
> 
> The reason for this is:
> 
> 1) I can't see the declaration or definition for my_array (and I don't
> want to see it). It could be defined in another source file. I don't
> know what macros is used for its length.
> 
> 2) The macro that decides the array's length might change. For instance,
> today, the code might be:
> 
>     int my_array[AMOUNT_VOWELS_IN_ALPHABET];
> 
> while tomorrow it might be:
> 
>     int my_array[AMOUNT_VOWELS_IN_ALPHABET + 1];
> 
> ...while next week it might be:
> 
>     int my_array[AMOUNT_CONSONANTS_IN_ALPHABET];

.... or it might even be:

     int my_array[] = {b,c,d,f,g,h,j,k,l,m,n,p,q,r,s,t,v,w,x,z};

Where's their 'X' now? 
Hah!

I use an NMEMB() macro.

http://www.mindspring.com/~pfilandr/C/lists_and_files/string_sort.c

#define NUMBERS \
{"one","two","three","four","five",\
"six","seven","eight","nine","ten"}

#define NMEMB(A)        (sizeof (A) / sizeof *(A))

    char *numbers[] = NUMBERS;
    char **const after = numbers + NMEMB(numbers);


http://www.mindspring.com/~pfilandr/C/lists_and_files/Lf_sort.c

#define NUMBERS \
{15,14,13,7,20,9,8,12,11,6}

#define NMEMB(A)        (sizeof (A) / sizeof *(A))

    long double numbers[] = NUMBERS;
    long double *const after = numbers + NMEMB(numbers);

-- 
pete
0
Reply pfiland (6613) 2/6/2008 12:08:29 PM

Keith Thompson wrote:
> 
> Peter Nilsson <airia@acay.com.au> writes:
> > vipps...@gmail.com wrote:
> >> Peter Nilsson <ai...@acay.com.au> wrote:
> >> > ...
> >> > �  int *p = my_array;
> >> > �  int const *const pend = my_array + sizeof my_array/
> >> > �  �  �  �  �  �  �  �  �  �  �  �  �  �  �  �  �  �  � sizeof*my_array;
> >> > �  do *p++ = 42;
> >> > �  while (pend != p);
> >>
> >> > At no stage is pend dereferenced; and the loop exits on
> >> > p == pend, so the address is not dereferenced by p
> >> > either.
> >>
> >> Ah, I was a bit confused I guess.
> >> However, OP _does_ dereference it. Not in that snipped,
> >> but in 4)
> >>
> >> > 4) *(&my_array+ 1) decays to the address of the first
> >> > element in the non-existant array after the current one,
> >> > which is also the "pend" address for the array that
> >> > actually exists.
> >
> > Again, it is not derefenced...
> >
> >   The type of    my_array      is int[]
> >   The type of   &my_array      is int (*)[]
> >   The type of   &my_array + 1  is int (*)[]
> >   The type of *(&my_array + 1) is int []
> >
> > The last expression will decay to an int * when used in the
> > assignment to pend. At no stage is that pointer dereferenced.
> 
> I'm not convinced (which is not to say that you're wrong).
> 
> The declaration was
> 
>     int my_array[X];
> 
> so (&my_array + 1) is of type int (*)[X] (let's assume X is constant;
> VLAs make my head hurt).
> 
> Then *(&my_array + 1) is of type int[X].  This is an expression of
> array type, and it's the value of an array object that does not exist.
> It then immediately decays to a value of type int*, pointing just past
> the end of my_array.  But I think that the intermediate expression
> *before* the array-to-pointer conversion invokes UB. 

It seems funny that we think that we have such a good idea 
of what the value of (*(&my_array + 1)) *should* be.

I'm not able to come up with how it might not be UB.
But I'm still thinking about it.

-- 
pete
0
Reply pfiland (6613) 2/6/2008 12:37:47 PM

Army1987 wrote:
> 
> William Ahern wrote:
> 
> > In article <87sl0620x5.fsf@kvetch.smov.org> you wrote:
> >> William Ahern <william@wilbur.25thandClement.com> writes:
> 
> > Yes. And so,
> > how often do you see people casting NULL as opposed to casting
> > 0 in such a context?
> Yeah, I don't like unadorned 0 as a null pointer,
> and I prefer NULL, but I
> found (type *)0 OK, and (type *)NULL as somewhat redundant.
> 
> (And I *hate* '\0' as a null pointer.
> Code such as ptr == '\0' always makes
> me wonder whether that's a typo for *ptr == '\0'.)

It *should* make you wonder.
NULL makes the code easier to read.
That's a strong reason for using it.

-- 
pete
0
Reply pfiland (6613) 2/6/2008 12:49:47 PM

"Tomás Ó hÉilidhe" <toe@lavabit.com> writes:
> Tomás Ó hÉilidhe:
>> Anyway, just wondering what people think of the alternative. Saves me 
>> that little rush of pissed-off-ness every time I've to write out the 
>> tedious sizeof thing.
>
> A few people have asked me why I just don't write:
>
>     int const *const pend = my_array + X;
>
> The reason for this is:
>
> 1) I can't see the declaration or definition for my_array (and I don't 
> want to see it). It could be defined in another source file. I don't 
> know what macros is used for its length.
>
> 2) The macro that decides the array's length might change. For instance, 
> today, the code might be:
[snip]

So use a macro that gives you the length of an array object,
regardless of how it's declared, such as:

#define ARRAY_LEN(a) (sizeof (a) / sizeof *(a))

Then you can write:

    int const *const pend = my_array + ARRAY_LEN(my_array);

and not have to worry about UB (as long as you don't try to
dereference pend, of course).

> But anyway... back to my proposed alternative.
>
>
> Originally, I had:  (void*)(&my_array+1)
>
> but still that was a bit of a pain. So I switched to:
>
>     *(&my_array+1)
>
> I'd like to hear what people think in regard to there being UB when I 
> dereference at the end. Strictly speaking, there probably is, but 
> realisticly speaking, I think it's another thing to be added to the list 
> of "UB" that we ignore.

In my opinion, there's an instance of undefined behavior here, but one
that *probably* isn't likely to cause any real problems unless the
compiler goes out of its way to cause problems.  But the real risk, I
think, is that an optimizing compiler could rearrange the code
*assuming* that there's no UB.

In effect, in every C translation unit you write, you're implicitly
making a promise to the compiler that there is no undefined behavior.
If you break that promise, the compiler may get its revenge -- not out
of malice, but just because it innocently assumes that there is no
undefined behavior.

>                         Like for instance, here's a UB that I ignore:
>
>
>     union { int s; unsigned us; } x;
>
>     x.s = -27;
>
>     printf("%u",x.us);  /* Let's see what the bit pattern is */

Here you might be on somewhat safer ground.  The use of unions for
type-punning is so widespread (even though the standard doesn't
support it except perhaps for character arrays) that compiler writers
will probably go out of their way to avoid breaking it.  But in theory
an optimizing compiler could observe that you store a value to x.s but
never use that value, and eliminate the assignment.

Note that you can achieve the same effect with pointer conversions:

    int s = -27;
    printf("s as unsigned = %u\n", *(unsigned*)&s);

or with memcpy (left as an exercise).

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 5:36:45 PM

pete <pfiland@mindspring.com> writes:
> Army1987 wrote:
>> William Ahern wrote:
>> > In article <87sl0620x5.fsf@kvetch.smov.org> you wrote:
>> >> William Ahern <william@wilbur.25thandClement.com> writes:
>> 
>> > Yes. And so,
>> > how often do you see people casting NULL as opposed to casting
>> > 0 in such a context?
>> Yeah, I don't like unadorned 0 as a null pointer,
>> and I prefer NULL, but I
>> found (type *)0 OK, and (type *)NULL as somewhat redundant.
>> 
>> (And I *hate* '\0' as a null pointer.
>> Code such as ptr == '\0' always makes
>> me wonder whether that's a typo for *ptr == '\0'.)
>
> It *should* make you wonder.
> NULL makes the code easier to read.
> That's a strong reason for using it.

I've also seen code that uses NULL rather than '\]0' for the null
character, such as:

    char empty_string[1];
    empty_string[0] = NULL; /* bad */

Apart from being misleading, it won't work if NULL happens to be
defined as ((void*)0).

(If you're really perverse, you can write:
    empty_string[0] = EXIT_SUCCESS;
or
    empty_string[0] = EOF+1;
both of which are horrendously ugly and not guaranteed to work, but
slightly more likely to work than NULL -- and more likely to fail
silently.)

This is not an argument either way about using NULL where it's
appropriate, of course.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 6:31:41 PM

Keith Thompson <kst-u@mib.org> writes:
[...]
> I've also seen code that uses NULL rather than '\]0' for the null
> character, such as:
[...]

Of course I meant NULL rather than '\0'.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 6:39:24 PM

"Tom�s � h�ilidhe" <toe@lavabit.com> wrote in message
>
> I commonly use pointers to iterate thru an array. For example:
>
>    int my_array[X];
>
>    int *p = my_array;
>    int const *const pend = my_array + sizeof my_array/sizeof*my_array;
>
>    do *p++ = 42;
>    while (pend != p);
>
> (Yes I realise the lack of spaces in the sizeof thing above is
> disgusting, but I've gotten so sick of writing it out that I make it as
> compact as possible)
>
> I can't count how many times I use this construct in my code every day.
> It's a right pain in the ass to always have to write out the long-winded
> intialiser for pend, so I'm considering switching to initialising pend
> as follows:
>
>    int const *const pend = *(&my_array+1);
>
> 1) my_array is an int[X]
> 2) &my_array is an int(*)[X]
> 3) &my_array+1 is the address of the non-existant array located after
> the current one.
> 4) *(&my_array+ 1) decays to the address of the first element in the
> non-existant array after the current one, which is also the "pend"
> address for the array that actually exists.
>
> It's a hell of a lot shorter to write, and also I think it's a little
> less vulnerable to typos because you'll most likely get a type mismatch
> if its written wrongly.
>
> Anyway, just wondering what people think of the alternative. Saves me
> that little rush of pissed-off-ness every time I've to write out the
> tedious sizeof thing.
>
> (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array)
> because then I'd have to worry about including the necessary header
> file... which is also the reason why I don't use NULL.)
>

What's wrong with using

for(i=0;i<N;i++)
  my_array[i] = 42;

to step through arrays? Always use i and N as your variable names, even if 
you set them up specially, because that is the convention.

Pointer arithmetic is confusing. It made some sort of sense in the early 
days when compilers were less sophisticated than now and programmers thought 
more in assembly language, but really it should be squeezed out of modern C.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/6/2008 8:36:51 PM

Malcolm McLean said:

<snip>
 
> Pointer arithmetic is confusing.

What is it about pointer arithmetic that you find confusing?

-- 
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
0
Reply rjh (10789) 2/6/2008 8:42:09 PM

"pete" <pfiland@mindspring.com> wrote in message
> It *should* make you wonder.
> NULL makes the code easier to read.
> That's a strong reason for using it.
>
It makes it harder to read, in my opinion.
"null" should be a keyword and ptr = 0 should be illegal. However that 
raises issues of old code that uses memset or calloc to intialise pointers 
to null.

"NULL" shouts, and gives the null pointer an emphasis it normally should not 
have.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/6/2008 8:44:00 PM

In article <5qmdnbNwn_QqhzfanZ2dneKdnZydnZ2d@bt.com>,
Richard Heathfield  <rjh@see.sig.invalid> wrote:
>Malcolm McLean said:

>> Pointer arithmetic is confusing.

>What is it about pointer arithmetic that you find confusing?

Eliza: Have you always What is it about pointer arithmetic that you
find confusing?
-- 
This is a Usenet signature block. Please do not quote it when replying
to one of my postings.
http://en.wikipedia.org/wiki/Signature_block
0
Reply roberson2 (8067) 2/6/2008 9:01:24 PM

"Malcolm McLean" <regniztar@btinternet.com> writes:
> "pete" <pfiland@mindspring.com> wrote in message
>> It *should* make you wonder.
>> NULL makes the code easier to read.
>> That's a strong reason for using it.
>>
> It makes it harder to read, in my opinion.
> "null" should be a keyword and ptr = 0 should be illegal.

I actually agree that this would have been better, but it would break
too much existing code and therefore will never happen in a language
called "C".  The most that could be done is to add a new _Null
keyword, which is a new special null pointer constant that cannot be
used in a non-pointer context.  You could then have "#define null
_Null" or perhaps "#define nil _Null" in a new standard header.  But I
doubt that there would be enough support for this, especially given
the necessity of preserving the existing forms.

>                                                           However that
> raises issues of old code that uses memset or calloc to intialise
> pointers to null.

Such code is already broken^H^H^H^H^H^H non-portable; adding a "null"
keyword would not affect that.

> "NULL" shouts, and gives the null pointer an emphasis it normally
> should not have.

In C source, all-caps identifiers are conventionally used for macros
(no, the standard library does not consistently follow this
convention).  It doesn't imply shouting as it does in English text.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/6/2008 9:07:09 PM

"Walter Roberson" <roberson@ibd.nrc-cnrc.gc.ca> wrote in message 
> In article <5qmdnbNwn_QqhzfanZ2dneKdnZydnZ2d@bt.com>,
> Richard Heathfield  <rjh@see.sig.invalid> wrote:
>>Malcolm McLean said:
> 
>>> Pointer arithmetic is confusing.
> 
>>What is it about pointer arithmetic that you find confusing?
> 
> Eliza: Have you always What is it about pointer arithmetic that you
> find confusing?
> 
Tell me more about your pointer arithmetic.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/6/2008 9:11:22 PM

In article <5qmdnbNwn_QqhzfanZ2dneKdnZydnZ2d@bt.com>,
Richard Heathfield  <rjh@see.sig.invalid> wrote:
>Malcolm McLean said:

>> Pointer arithmetic is confusing.

>What is it about pointer arithmetic that you find confusing?

He said it's confusing, not that he is confused by it.

Certainly many people do seem to get confused by it.  Other things
being equal, it's usually clearer to express an array traversal in
terms of an index, and modern compilers should generate at least
as good code.

-- Richard
-- 
:wq
0
Reply richard91 (3683) 2/6/2008 11:03:32 PM

On Feb 6, 12:42=A0pm, Richard Heathfield <r...@see.sig.invalid> wrote:
> Malcolm McLean said:
>
> <snip>
>
> > Pointer arithmetic is confusing.
>
> What is it about pointer arithmetic that you find confusing?

That void pointers have no stride, and yet you can do this:

#include <stdio.h>
#include <stdlib.h>

int             main(void)
{
    void            *p[5][5];
    p[0][0] =3D NULL;
    return 0;
}
0
Reply dcorbit (2696) 2/6/2008 11:26:55 PM

Malcolm McLean wrote:

> an emphasis it normally should not have.

Isn't that what you say about *all* capitalized macros?

-- 
pete
0
Reply pfiland (6613) 2/6/2008 11:51:29 PM

"pete" <pfiland@mindspring.com> wrote in message
> Malcolm McLean wrote:
>
>> an emphasis it normally should not have.
>
> Isn't that what you say about *all* capitalized macros?
>
Yes. Macros like clamp(), lerp(), uniform(), and so on are sufficiently 
function-like that it makes sense to use the same typography as functions. 
You can make a legitimate case that caller should be warned about the 
side-effect of passing an increment or += to the macro. Nothing is black and 
white. However Nintendo's N64 graphics pipeline macros, which had to take an 
arguement of the form ptr++ because they sometimes expanded to two or more 
pipeline comands, were made to look like function calls.

This is also the convention set by the standard library.

Structures, however, should shout, because they are big. Hence FILE or JPEG 
might refer to very significant memory objects.

So lower case for portable macros, mixed case for macros that depend on 
something other than the standard library, as with fucntions, caps for 
structure typedefs.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/7/2008 12:08:15 AM

Malcolm McLean wrote:
 
> Pointer arithmetic is confusing. 

I like pointer math.
I also like to modify function parameters 
at every given opportunity.

I think the two are related, 
because a lot of parameters tend to be pointers.

-- 
pete
0
Reply pfiland (6613) 2/7/2008 12:13:12 AM

In article <1_2dnRhAXIFZ1jfanZ2dneKdnZydnZ2d@bt.com>,
Malcolm McLean <regniztar@btinternet.com> wrote:

>Yes. Macros like clamp(), lerp(), uniform(), and so on are sufficiently 
>function-like that it makes sense to use the same typography as functions. 

>Structures, however, should shout, because they are big. Hence FILE or JPEG 
>might refer to very significant memory objects.

A function can involve "very signficant memory objects" (the code),
but that is of no concern to you?
-- 
   "No one has the right to destroy another person's belief by
   demanding empirical evidence."                -- Ann Landers
0
Reply roberson2 (8067) 2/7/2008 12:15:28 AM

user923005 <dcorbit@connx.com> writes:

> On Feb 6, 12:42�pm, Richard Heathfield <r...@see.sig.invalid> wrote:
>> What is it about pointer arithmetic that you find confusing?
>
> That void pointers have no stride, and yet you can do this:
[...]
>     void            *p[5][5];
>     p[0][0] = NULL;
[...]

May we assume that you do understand what is going on here,
though?
-- 
"I should killfile you where you stand, worthless human." --Kaz
0
Reply blp (3953) 2/7/2008 12:25:31 AM

"pete" <pfiland@mindspring.com> wrote in message
> Malcolm McLean wrote:
>
>> Pointer arithmetic is confusing.
>
> I like pointer math.
> I also like to modify function parameters
> at every given opportunity.
>
> I think the two are related,
> because a lot of parameters tend to be pointers.
>
That was part of the justification for it. Stepping through an array with i 
would involve creating an unnecessary temporary, and probably a bit of 
useless arithmetic to dereference, whilst if you just incremented the 
pointer the generated machine code would be tighter.

However computers are faster now and optimisers more aggressive. There's a 
school of thought that parameters should be treated as read-only.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/7/2008 12:29:37 AM

"Walter Roberson" <roberson@ibd.nrc-cnrc.gc.ca> wrote in message
> In article <1_2dnRhAXIFZ1jfanZ2dneKdnZydnZ2d@bt.com>,
> Malcolm McLean <regniztar@btinternet.com> wrote:
>
>>Yes. Macros like clamp(), lerp(), uniform(), and so on are sufficiently
>>function-like that it makes sense to use the same typography as functions.
>
>>Structures, however, should shout, because they are big. Hence FILE or 
>>JPEG
>>might refer to very significant memory objects.
>
> A function can involve "very signficant memory objects" (the code),
> but that is of no concern to you?
>
No. It's platform dependent, and if we manipulate that memory, which is 
extremely rare, and rarer still a good idea, we've left the bounds of 
portable C programming altogether.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/7/2008 12:32:22 AM

In article <zOidnYycS4ZczTfanZ2dnUVZ8tuqnZ2d@bt.com>,
Malcolm McLean <regniztar@btinternet.com> wrote:
>There's a 
>school of thought that parameters should be treated as read-only.

Are you talking about code such as

int main(int argc; char **argv) {
  argc--;
  argv++;
  while (argc-- > 0) puts(*argv++);
  return 0;
}

in which the parameters argv and argc are modified?

Or are you talking about modifying something -pointed to- by a
parameter?

If you are talking about something -pointed to- by a parameter,
and want to rule out modifying things pointed to by parameters,
then you need a language that returns multiple values
distinctly, 
   [x y z] = foo(someparameter);
instead of
   x = foo(someparameter,&y,&z);
Or else you need to always wrap multiple return values in a struct.

If you are indeed talking about not allowing things pointed to
by parameters to be modified, then you would lose the ability for a
routine to pass in its own buffer to a function.
-- 
   "Any sufficiently advanced bug is indistinguishable from a feature."
   -- Rich Kulawiec
0
Reply roberson2 (8067) 2/7/2008 12:40:08 AM

On Feb 6, 4:25=A0pm, Ben Pfaff <b...@cs.stanford.edu> wrote:
> user923005 <dcor...@connx.com> writes:
> > On Feb 6, 12:42=A0pm, Richard Heathfield <r...@see.sig.invalid> wrote:
> >> What is it about pointer arithmetic that you find confusing?
>
> > That void pointers have no stride, and yet you can do this:
> [...]
> > =A0 =A0 void =A0 =A0 =A0 =A0 =A0 =A0*p[5][5];
> > =A0 =A0 p[0][0] =3D NULL;
>
> [...]
>
> May we assume that you do understand what is going on here,
> though?

Sure, but didn't it seem confusing to you, the first time you found
out that void pointers have no stride and yet you can make multi-
dimentional arrays of them?
0
Reply dcorbit (2696) 2/7/2008 12:53:14 AM

In article <YpSdnU9qHO36zDfanZ2dnUVZ8sOonZ2d@bt.com>,
Malcolm McLean <regniztar@btinternet.com> wrote:

>"Walter Roberson" <roberson@ibd.nrc-cnrc.gc.ca> wrote in message
>> In article <1_2dnRhAXIFZ1jfanZ2dneKdnZydnZ2d@bt.com>,
>> Malcolm McLean <regniztar@btinternet.com> wrote:

>>>Yes. Macros like clamp(), lerp(), uniform(), and so on are sufficiently
>>>function-like that it makes sense to use the same typography as functions.

>>>Structures, however, should shout, because they are big. Hence FILE or 
>>>JPEG
>>>might refer to very significant memory objects.

>> A function can involve "very signficant memory objects" (the code),
>> but that is of no concern to you?

>No. It's platform dependent,

Sounds to me like you have not considered Kolmogorov complexity in
this matter. Some functions are inherently complex, and cannot be
written smally except each in some language tailor-made to express
that one function smally.

>and if we manipulate that memory, which is 
>extremely rare, and rarer still a good idea, we've left the bounds of 
>portable C programming altogether.

Invoking a routine is a form of manipulating memory.

If you want to have textual case distinguish object size, then
you need to be consistant and have large functions distinguished by
uppercase, as reminders that they are space-expensive to invoke.

printf() is a good traditional example: an integer-only program
that invokes printf() must be linked with the floating point library
(if there is a seperate floating point library) because printf()
needs floating point linked it case the user specified a floating
point formating element. It was not uncommon in the earlier days
for an integer-only program to be a fairly small number of Kb but
to kick up by several hundred Kb because printf() was referenced.
-- 
'Roberson' is my family name; my given name is 'Walter'.
0
Reply roberson2 (8067) 2/7/2008 12:55:26 AM

user923005 <dcorbit@connx.com> writes:
> On Feb 6, 12:42 pm, Richard Heathfield <r...@see.sig.invalid> wrote:
>> Malcolm McLean said:
>>
>> <snip>
>>
>> > Pointer arithmetic is confusing.
>>
>> What is it about pointer arithmetic that you find confusing?
>
> That void pointers have no stride, and yet you can do this:
>
> #include <stdio.h>
> #include <stdlib.h>
>
> int             main(void)
> {
>     void            *p[5][5];
>     p[0][0] = NULL;
>     return 0;
> }

Sorry, I don't see what's confusing about that, other than the
potentially misleading statement that "void pointers have no stride".

p is a two-dimensional array of elements, where the size of each
element (the stride, if you like) is typically something like 4 or 8
bytes.  The fact that each element happens to be a void* is
irrelevant; we're not treating the elements as pointers, just as
objects.

p is an array 5 of array 5 of pointer to void, which decays to
a pointer to an array 5 of pointer to void.

p[0] is an array 5 of pointer to void, which decays to a pointer to
pointer to void.

p[0][0] is a pointer to void; we assign a value to it.

The behavior, and the logic behind it, would be the same for:

    int p[5][5];
    p[0][0] = 0;

Am I missing something?

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/7/2008 2:16:33 AM

Walter Roberson wrote:
> 
> In article <zOidnYycS4ZczTfanZ2dnUVZ8tuqnZ2d@bt.com>,
> Malcolm McLean <regniztar@btinternet.com> wrote:
> >There's a
> >school of thought that parameters should be treated as read-only.
> 
> Are you talking about code such as
> 
> int main(int argc; char **argv) {
>   argc--;
>   argv++;
>   while (argc-- > 0) puts(*argv++);
>   return 0;
> }
> 
> in which the parameters argv and argc are modified?

He was.
I am familiar with that school of thought.
"I'm on the other side of that issue."

-- 
pete
0
Reply pfiland (6613) 2/7/2008 2:17:29 AM

user923005:

> That void pointers have no stride, and yet you can do this:
> 
> #include <stdio.h>
> #include <stdlib.h>
> 
> int             main(void)
> {
>     void            *p[5][5];
>     p[0][0] = NULL;
>     return 0;
> }


"Have no stride"? As in "have no gait"? As in "don't have a particular way 
of walking"?

Is this your way of saying you can't do pointer arithmetic on them?

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/7/2008 8:27:08 AM

user923005:

>     void            *p[5][5];
>     p[0][0] = NULL;


This is equal to:

    *(  *(p + 0) + 0  )


1) p is a void(*)[5][5], which decays to a void (**)[5]
2) p + 0 is a void (**)[5]
3) *(p + 0) is a void (*)[5], which decays to a void **
4) *(p + 0) + 0 is a void **
5) *( *(p + 0) + 0 ) is a void *


At no point is pointer arithmetic performed on a void*.

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/7/2008 8:43:41 AM

Tom�s � h�ilidhe:

> This is equal to:
> 
>     *(  *(p + 0) + 0  )
> 
> 
> 1) p is a void(*)[5][5], which decays to a void (**)[5]
> 2) p + 0 is a void (**)[5]
> 3) *(p + 0) is a void (*)[5], which decays to a void **
> 4) *(p + 0) + 0 is a void **
> 5) *( *(p + 0) + 0 ) is a void *


Wups a daisy I got that wrong:

1) p is a void *[5][5], which decays to a void *(*)[5]
2) p + 0 is a void *(*)[5]
3) *(p + 0) is a void *[5], which decays to a void **
4) *(p + 0) + 0 is a void **
5) *( *(p + 0) + 0 ) is a void *


Therefore we could have:


    void *p[5][5];

    void *(*a)[5] = p + 0;

    void **b = *(p + 0);

    void **c = *(p + 0) + 0;

    void *d = *((p + 0) + 0);


There's no arithmetic performed on a void*.

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/7/2008 8:59:34 AM

"Walter Roberson" <roberson@ibd.nrc-cnrc.gc.ca> wrote in message
> In article <YpSdnU9qHO36zDfanZ2dnUVZ8sOonZ2d@bt.com>,
>> MNalcolm
>
>>No. It's platform dependent, (Kolmogorov complexity)
>
> Sounds to me like you have not considered Kolmogorov complexity in
> this matter. Some functions are inherently complex, and cannot be
> written smally except each in some language tailor-made to express
> that one function smally.
>
Kolmogorov complexity is the complexity of A given B as the input. In this 
case B is the operating system / platform / whatever. B is deliberately 
designed to reduce the Kolmogorov complexity of as many functions as 
possible. So it's not a good measure. Sometimes printf() is one call to a 
library outside of the program, sometimes an expansion of the format string 
followed by a call for each character, sometimes it decodes glyphs and 
accesses the screen raster.
>
>>and if we manipulate that memory, which is
>>extremely rare, and rarer still a good idea, we've left the bounds of
>>portable C programming altogether.
>
> Invoking a routine is a form of manipulating memory.
>
> If you want to have textual case distinguish object size, then
> you need to be consistant and have large functions distinguished by
> uppercase, as reminders that they are space-expensive to invoke.
>
They take memory space to link and memory space to invoke. You're running 
the two things together here.
We could have a leading / trailing _M system.
_M_Mprintf_M
indicates that it is relatively hefty to link in, but only moderately 
memory-greedy to invoke.

_M_M_M_M_malloc()

however is extremely memory heavy to link in, since it uses a huge 
allocation pool, but the overhead on invocation is fairly tiny.
>
> printf() is a good traditional example: an integer-only program
> that invokes printf() must be linked with the floating point library
> (if there is a seperate floating point library) because printf()
> needs floating point linked it case the user specified a floating
> point formating element. It was not uncommon in the earlier days
> for an integer-only program to be a fairly small number of Kb but
> to kick up by several hundred Kb because printf() was referenced.
>
That is a danger for embedded programs.

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/7/2008 12:21:23 PM

Malcolm McLean wrote:

> "pete" <pfiland@mindspring.com> wrote in message
>> It *should* make you wonder.
>> NULL makes the code easier to read.
>> That's a strong reason for using it.
>>
> It makes it harder to read, in my opinion.
> "null" should be a keyword and ptr = 0 should be illegal. However that 
> raises issues of old code that uses memset or calloc to intialise pointers 
> to null.
What the hell? memset sets the representation of pointers to all bits
zero, and it could be the valid representation of a null pointer even if
ptr = 0 was illegal. (Now, the vice versa applies: ptr = 0 is legal, but
((unsigned char)&ptr)[i] needn't be all zeroes.)
It raises the issues of old and less old code which use 0 as a null
pointer constant.

> "NULL" shouts, and gives the null pointer an emphasis it normally should not 
> have.
???



-- 
Army1987 (Replace "NOSPAM" with "email")
0
Reply army1987 (668) 2/7/2008 1:19:20 PM

On Feb 6, 9:32 pm, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
>     *(&my_array+1)
>
> I'd like to hear what people think in regard to there being UB when I
> dereference at the end. Strictly speaking, there probably is, but
> realisticly speaking, I think it's another thing to be added to the list
> of "UB" that we ignore.

Who is "we" ? I don't ignore any UB.  I'm not keen on
my application suddenly going haywire.

> Like for instance, here's a UB that I ignore:
>
>     union { int s; unsigned us; } x;
>     x.s =3D -27;
>     printf("%u",x.us);  /* Let's see what the bit pattern is */

There is no UB here. Memory is explicitly permitted to be
aliased by the corresponding 'signed' or 'unsigned' version
of the effective type, and there are no trap representations
for unsigned.

0
Reply oldwolf (2278) 2/8/2008 1:55:41 AM

On Feb 7, 3:16 pm, Keith Thompson <ks...@mib.org> wrote:
> user923005 <dcor...@connx.com> writes:
>
> > That void pointers have no stride, and yet you can do this:
>
> >     void            *p[5][5];
> >     p[0][0] = NULL;
> >     return 0;
>
> Sorry, I don't see what's confusing about that, other than the
> potentially misleading statement that "void pointers have no stride".
>
> p is a two-dimensional array of elements, where the size of each
> element (the stride, if you like) is typically something like 4 or 8
> bytes.

By 'stride', I think he means sizeof(T), where T is a
complete type of course. However, this has absolutely
nothing to do with declaring an array of T* . Probably
the OP is confused about the distinction between
arrays and pointers.

0
Reply oldwolf (2278) 2/8/2008 1:59:22 AM

Old Wolf <oldwolf@inspire.net.nz> writes:
> On Feb 6, 9:32 pm, "Tom�s � h�ilidhe" <t...@lavabit.com> wrote:
[...]
> > Like for instance, here's a UB that I ignore:
> >
> >     union { int s; unsigned us; } x;
> >     x.s = -27;
> >     printf("%u",x.us);  /* Let's see what the bit pattern is */
> 
> There is no UB here. Memory is explicitly permitted to be
> aliased by the corresponding 'signed' or 'unsigned' version
> of the effective type, and there are no trap representations
> for unsigned.

It's *almost* explicitly permitted; the most explicit statement is in
a footnote.

C99 6.2.5p9:

    The range of nonnegative values of a signed integer type is a
    subrange of the corresponding unsigned integer type, and the
    representation of the same value in each type is the same.

with a footnote:

    The same representation and alignment requirements are meant to
    imply interchangeability as arguments to functions, return values
    from functions, and members of unions.

It's not as unambiguous a statement as I'd like.

-- 
Keith Thompson <kst-u@mib.org> signature temporarily unavailable
0
Reply kst (247) 2/8/2008 3:55:33 AM

On Feb 7, 9:55 pm, Keith Thompson <k...@cts.com> wrote:
> Old Wolf <oldw...@inspire.net.nz> writes:
> > On Feb 6, 9:32 pm, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> [...]
> > > Like for instance, here's a UB that I ignore:
>
> > >     union { int s; unsigned us; } x;
> > >     x.s =3D -27;
> > >     printf("%u",x.us);  /* Let's see what the bit pattern is */
>
> > There is no UB here. Memory is explicitly permitted to be
> > aliased by the corresponding 'signed' or 'unsigned' version
> > of the effective type, and there are no trap representations
> > for unsigned.
>
> It's *almost* explicitly permitted; the most explicit statement is in
> a footnote.
>
> C99 6.2.5p9:
>
>     The range of nonnegative values of a signed integer type is a
>     subrange of the corresponding unsigned integer type, and the
>     representation of the same value in each type is the same.
>
> with a footnote:
>
>     The same representation and alignment requirements are meant to
>     imply interchangeability as arguments to functions, return values
>     from functions, and members of unions.

Are padding bits (and hence trap representation) really prohibited
in unsigned int? If not, then the following would break type punning
here:

int is       [4 "same-as-sign" bits] [sign bit] [27 value bits]
unsigned is  [4 padding bits]        [           28 value bits]

where four "same-as-sign" bits in an int object are set
or not set together with the sign bit; and in unsigned
the four padding bits are never set. Then if you store
a negative number in the int member of the union, and
access it as unsigned, you get the four padding bits set,
a trap representation.

Yevgen
0
Reply ymuntyan (159) 2/8/2008 4:21:15 AM

On Thu, 7 Feb 2008 13:19:20 UTC, Army1987 <army1987@NOSPAM.it> wrote:

> Malcolm McLean wrote:
> 
> > "pete" <pfiland@mindspring.com> wrote in message
> >> It *should* make you wonder.
> >> NULL makes the code easier to read.
> >> That's a strong reason for using it.
> >>
> > It makes it harder to read, in my opinion.
> > "null" should be a keyword and ptr = 0 should be illegal. However that 
> > raises issues of old code that uses memset or calloc to intialise pointers 
> > to null.
> What the hell? memset sets the representation of pointers to all bits
> zero, and it could be the valid representation of a null pointer even if
> ptr = 0 was illegal. (Now, the vice versa applies: ptr = 0 is legal, but
> ((unsigned char)&ptr)[i] needn't be all zeroes.)
> It raises the issues of old and less old code which use 0 as a null
> pointer constant.

<type> *p = NULL; /* legal */
<type> *p = 0;    /* legal */
<type> *p = '\0'; /* legal */

memset(p, 0, n); /* illegal! when p is misused to overwrite vales not 
type char */ 

There is absolutely no guarantee that all bits 0 are a legal value for
all (except char, including pointer) types. All bits 0 does NOT mean 
that it is a nullpointer. An address (pointer) decays in 2 parts: 
- address bits
- padding bits

So memset on anything, except char arrays, is always a good chance to 
produce illegal padding bits.


> > "NULL" shouts, and gives the null pointer an emphasis it normally should not 
> > have.
> ???
> 
> 
> 


-- 
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
0
Reply os2guy1 (1071) 2/8/2008 9:33:40 AM

Keith Thompson <ks...@mib.org> wrote:
> Peter Nilsson <ai...@acay.com.au> writes:
> > vipps...@gmail.com wrote:
> > > Peter Nilsson <ai...@acay.com.au> wrote:
> > > > ...
> > > > =A0 int *p =3D my_array;
> > > > =A0 int const *const pend =3D my_array + sizeof my_array/
> > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0sizeof*my_array;
> > > > =A0 do *p++ =3D 42;
> > > > =A0 while (pend !=3D p);
> > > >
> > > > At no stage is pend dereferenced; and the loop exits
> > > > on p =3D=3D pend, so the address is not dereferenced by p
> > > > either.
> > >
> > > Ah, I was a bit confused I guess.
> > > However, OP _does_ dereference it. Not in that snipped,
> > > but in 4)
> > >
> > > > 4) *(&my_array+ 1) decays to the address of the first
> > > > element in the non-existant array after the current
> > > > one, which is also the "pend" address for the array
> > > > that actually exists.
> >
> > Again, it is not derefenced...
> >
> > =A0 The type of =A0 =A0my_array =A0 =A0 =A0is int[]
> > =A0 The type of =A0 &my_array =A0 =A0 =A0is int (*)[]
> > =A0 The type of =A0 &my_array + 1 =A0is int (*)[]
> > =A0 The type of *(&my_array + 1) is int []
> >
> > The last expression will decay to an int * when used in
> > the assignment to pend. At no stage is that pointer
> > dereferenced.
>
> I'm not convinced (which is not to say that you're wrong).
>
> The declaration was
>
> =A0 =A0 int my_array[X];
>
> so (&my_array + 1) is of type int (*)[X] (let's assume X
> is constant; VLAs make my head hurt).
>
> Then *(&my_array + 1) is of type int[X].

Yes. That is the only consequence of the unary * indirection
under 6.5.3.2p4. [The second sentence is not applicable
since neither condition applies. The third is not applicable
since &my_array + 1 is valid pointer.]

>=A0This is an expression of array type, and it's the value
> of an array object that does not exist.

What value?

> It then immediately decays to a value of type int*,
> pointing just past the end of my_array.

Yes. That's given by 6.3.2.1p3.

>=A0But I think that
> the intermediate expression *before* the array-to-pointer
>  conversion invokes UB.

How?

> (Though I'd be surprised if any implementation did
> anything other than what was intended; after all, there's
> no need to load the value of the nonexistent array object.)

Where does the standard allow the 'loading' of this non-
existant array? 6.3.2.1p2 specifically excludes an array
lvalue from becoming a value.

--
Peter
0
Reply airia (1802) 2/9/2008 5:14:04 AM

On Fri, 08 Feb 2008 21:14:04 -0800, Peter Nilsson wrote:
>Keith Thompson <ks...@mib.org> wrote:
>> I'm not convinced (which is not to say that you're wrong).
>>
>> The declaration was
>>
>>     int my_array[X];
>>
>> so (&my_array + 1) is of type int (*)[X] (let's assume X is constant;
>> VLAs make my head hurt).
>>
>> Then *(&my_array + 1) is of type int[X].
> 
> Yes. That is the only consequence of the unary * indirection under
> 6.5.3.2p4. [The second sentence is not applicable since neither
> condition applies. The third is not applicable since &my_array + 1 is
> valid pointer.]

The third may be applicable. The footnote attached to it clarifies that 
"invalid value" should be read as "value that is invalid for 
dereferencing", and that otherwise perfectly valid pointer values can be 
invalid for dereferencing. I don't know where it's stated which pointer 
values are valid and which aren't, though.
0
Reply truedfx (1926) 2/9/2008 8:10:24 AM

On Feb 8, 4:55 pm, Keith Thompson <k...@cts.com> wrote:
> Old Wolf <oldw...@inspire.net.nz> writes:
> > On Feb 6, 9:32 pm, "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote:
> [...]
> > > Like for instance, here's a UB that I ignore:
>
> > >     union { int s; unsigned us; } x;
> > >     x.s =3D -27;
> > >     printf("%u",x.us);  /* Let's see what the bit pattern is */
>
> > There is no UB here. Memory is explicitly permitted to be
> > aliased by the corresponding 'signed' or 'unsigned' version
> > of the effective type, and there are no trap representations
> > for unsigned.
>
> It's *almost* explicitly permitted; the most explicit statement is in
> a footnote.
>
> C99 6.2.5p9:
>
>     The range of nonnegative values of a signed integer type is a
>     subrange of the corresponding unsigned integer type, and the
>     representation of the same value in each type is the same.
>
> with a footnote:
>
>     The same representation and alignment requirements are meant to
>     imply interchangeability as arguments to functions, return values
>     from functions, and members of unions.
>
> It's not as unambiguous a statement as I'd like.

On re-reading I think perhaps the behaviour could
be undefined; it's only unsigned char that has no
trap representations; and the sign bit (for example)
in the representation of -27 could be a padding bit
which causes a trap rep in the unsigned type. The
footnot you quote seems only to apply to the case
of non-negative values.

0
Reply oldwolf (2278) 2/9/2008 8:10:47 AM

On Thu, 07 Feb 2008 19:55:33 -0800, Keith Thompson wrote:
> Old Wolf <oldwolf@inspire.net.nz> writes:
>> On Feb 6, 9:32 pm, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote:
> [...]
>> > Like for instance, here's a UB that I ignore:
>> >
>> >     union { int s; unsigned us; } x;
>> >     x.s = -27;
>> >     printf("%u",x.us);  /* Let's see what the bit pattern is */
>> 
>> There is no UB here. Memory is explicitly permitted to be aliased by
>> the corresponding 'signed' or 'unsigned' version of the effective type,
>> and there are no trap representations for unsigned.
> 
> It's *almost* explicitly permitted; [...]

The aliasing rules (6.5p7) explicitly allow access of signed integers as 
unsigned, and vice versa. There may be other reasons why a specific 
attempt to access a signed int as unsigned int fails, such as the 
possibility of trap representations pointed out in later messages, but 
Old Wolf was correct in claiming that the aliasing itself is permitted.
0
Reply truedfx (1926) 2/9/2008 8:36:38 AM

Harald van Dijk <truedfx@gmail.com> writes:

> On Fri, 08 Feb 2008 21:14:04 -0800, Peter Nilsson wrote:
>>Keith Thompson <ks...@mib.org> wrote:
>>> I'm not convinced (which is not to say that you're wrong).
>>>
>>> The declaration was
>>>
>>>     int my_array[X];
>>>
>>> so (&my_array + 1) is of type int (*)[X] (let's assume X is constant;
>>> VLAs make my head hurt).
>>>
>>> Then *(&my_array + 1) is of type int[X].
>> 
>> Yes. That is the only consequence of the unary * indirection under
>> 6.5.3.2p4. [The second sentence is not applicable since neither
>> condition applies. The third is not applicable since &my_array + 1 is
>> valid pointer.]
>
> The third may be applicable. The footnote attached to it clarifies that 
> "invalid value" should be read as "value that is invalid for 
> dereferencing", and that otherwise perfectly valid pointer values can be 
> invalid for dereferencing. I don't know where it's stated which pointer 
> values are valid and which aren't, though.

I thought this was a case where the last sentence of 6.5.6 p8 applies:
"If the result points one past the last element of the array object,
it shall not be used as the operand of a unary * operator that is
evaluated.".  The "is evaluated" seems, to me, to reserved for the &*E
case where the validity of E is not significant.  Surely the * "is
evaluated" in this case even though a decay to a pointer is the
immediate result?

-- 
Ben.
0
Reply ben.usenet (6515) 2/9/2008 11:30:59 AM

Malcolm McLean wrote:
> 
> "pete" <pfiland@mindspring.com> wrote in message
> > Malcolm McLean wrote:
> >
> >> an emphasis it normally should not have.
> >
> > Isn't that what you say about *all* capitalized macros?
> >
> Yes. Macros like clamp(), lerp(), uniform(),
> and so on are sufficiently
> function-like that it makes sense to use the same
> typography as functions.
> You can make a legitimate case that caller should be warned about the
> side-effect of passing an increment or += to the macro.
> Nothing is black and
> white. However Nintendo's N64 graphics pipeline macros,
> which had to take an
> arguement of the form ptr++
> because they sometimes expanded to two or more
> pipeline comands, were made to look like function calls.
> 
> This is also the convention set by the standard library.
> 
> Structures, however, should shout, because they are big.
> Hence FILE or JPEG
> might refer to very significant memory objects.
> 
> So lower case for portable macros,
> mixed case for macros that depend on
> something other than the standard library, as with fucntions, caps for
> structure typedefs.

I prefer Indian Hill style naming conventions.

http://www.psgd.org/paul/docs/cstyle/cstyle11.htm

-- 
pete
0
Reply pfiland (6613) 2/9/2008 12:02:14 PM

Old Wolf:


>> Like for instance, here's a UB that I ignore:
>>
>>     union { int s; unsigned us; } x;
>>     x.s = -27;
>>     printf("%u",x.us);  /* Let's see what the bit pattern is */
> 
> There is no UB here. Memory is explicitly permitted to be
> aliased by the corresponding 'signed' or 'unsigned' version
> of the effective type, and there are no trap representations
> for unsigned.


    I was referring to the rule whereby you can't write to union member A 
and then go on to read from union member B.

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/9/2008 2:19:27 PM

"Tom�s � h�ilidhe" <toe@lavabit.com> wrote in message 
news:Xns9A3F91B6EC983toelavabitcom@194.125.133.14...
> Old Wolf:
>
>
>>> Like for instance, here's a UB that I ignore:
>>>
>>>     union { int s; unsigned us; } x;
>>>     x.s = -27;
>>>     printf("%u",x.us);  /* Let's see what the bit pattern is */
>>
>> There is no UB here. Memory is explicitly permitted to be
>> aliased by the corresponding 'signed' or 'unsigned' version
>> of the effective type, and there are no trap representations
>> for unsigned.
>
>
>    I was referring to the rule whereby you can't write to union member A
> and then go on to read from union member B.

I have always wondered why that rule exists. For instance:
____________________________________________________________
#include <stdio.h>

typedef char static_assert[
  sizeof(long int) == sizeof(int) ? 1 : -1
];

typedef union foo_u {
  long int a;
  int b;
} foo;

int main(void) {
  foo f = { 0 };
  f.a = 1;
  printf("%d\n", f.b);
  return 0;
}


____________________________________________________________


I find it a bit hard for me come up with a scenario in which the output 
would not be: 1

Of course I have to be wrong here because according to the standard, the 
output "could" be: 666.

Right? 

0
Reply cristom (952) 2/9/2008 3:55:12 PM

"Chris Thomasson" <cristom@comcast.net> wrote in message 
> #include <stdio.h>
> 
> typedef char static_assert[
>  sizeof(long int) == sizeof(int) ? 1 : -1
> ];
> 
Is this dodge legal?

-- 
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

0
Reply regniztar (3128) 2/9/2008 4:25:07 PM

"Malcolm McLean" <regniztar@btinternet.com> wrote in message 
news:JL6dnedRoMh1TjDanZ2dnUVZ8qijnZ2d@bt.com...
>
> "Chris Thomasson" <cristom@comcast.net> wrote in message
>> #include <stdio.h>
>>
>> typedef char static_assert[
>>  sizeof(long int) == sizeof(int) ? 1 : -1
>> ];
>>
> Is this dodge legal?

Humm; I think so... 

0
Reply cristom (952) 2/9/2008 4:59:39 PM

On Sat, 09 Feb 2008 11:30:59 +0000, Ben Bacarisse wrote:
>>>Keith Thompson <ks...@mib.org> wrote:
>>>> The declaration was
>>>>
>>>>     int my_array[X];
>>>> [...]
>>>> *(&my_array + 1)
> 
> I thought this was a case where the last sentence of 6.5.6 p8 applies:
> "If the result points one past the last element of the array object, it
> shall not be used as the operand of a unary * operator that is
> evaluated.".  The "is evaluated" seems, to me, to reserved for the &*E
> case where the validity of E is not significant.  Surely the * "is
> evaluated" in this case even though a decay to a pointer is the
> immediate result?

I believe you're right. The only special case where unary * is not 
considered evaluated is when it's the immediate operand of unary &. While 
the array-to-pointer conversion does take the array's address, there is 
no operator used to do this, so the special case doesn't apply.
0
Reply truedfx (1926) 2/9/2008 5:05:58 PM

Chris Thomasson:

> ____________________________________________________________
> #include <stdio.h>
> 
> typedef char static_assert[
>   sizeof(long int) == sizeof(int) ? 1 : -1
>];
>
> typedef union foo_u {
>   long int a;
>   int b;
> } foo;
> 
> int main(void) {
>   foo f = { 0 };
>   f.a = 1;
>   printf("%d\n", f.b);
>   return 0;
> }
> 
> 
> ____________________________________________________________



   You might want another static_assert to ensure that neither long int nor 
int contain padding.

-- 
Tom�s � h�ilidhe
0
Reply toe (740) 2/9/2008 9:07:41 PM

"Tom�s � h�ilidhe" <toe@lavabit.com> wrote in message 
news:Xns9A3FD6EE81E3Ctoelavabitcom@194.125.133.14...
> Chris Thomasson:
>
>> ____________________________________________________________
>> #include <stdio.h>
>>
>> typedef char static_assert[
>>   sizeof(long int) == sizeof(int) ? 1 : -1
>>];
>>
>> typedef union foo_u {
>>   long int a;
>>   int b;
>> } foo;
>>
>> int main(void) {
>>   foo f = { 0 };
>>   f.a = 1;
>>   printf("%d\n", f.b);
>>   return 0;
>> }
>>
>>
>> ____________________________________________________________
>
>
>
>   You might want another static_assert to ensure that neither long int nor
> int contain padding.

I just wanted to ensure that they were indeed equal in size in order to keep 
the union semantics somewhat sane wrt the contrived example at hand. 

0
Reply cristom (952) 2/10/2008 5:29:40 AM

Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
<snip>
> I thought this was a case where the last sentence of
> 6.5.6 p8 applies:
> "If the result points one past the last element of the
> array object, it shall not be used as the operand of a
> unary * operator that is evaluated.".

That's a new one I wasn't aware of! I've asked the
question in comp.std.c.

--
Peter
0
Reply airia (1802) 2/11/2008 3:32:17 AM

Peter Nilsson <airia@acay.com.au> writes:

> Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> <snip>
>> I thought this was a case where the last sentence of
>> 6.5.6 p8 applies:
>> "If the result points one past the last element of the
>> array object, it shall not be used as the operand of a
>> unary * operator that is evaluated.".
>
> That's a new one I wasn't aware of! I've asked the
> question in comp.std.c.

That's a good idea.  I hope a consensus emerges.  The construct seems
safe to me though, from my reading, currently undefined.

If the meaning of "evaluated" were to exclude cases where the
resulting lvalue decays to a pointer (which is reasonable, since no
actual access is implied in such cases) then the construct in question
would be well-defined.

-- 
Ben.
0
Reply ben.usenet (6515) 2/11/2008 10:49:23 AM

"Chris Thomasson" <cristom@comcast.net> wrote:

> "Tom�s � h�ilidhe" <toe@lavabit.com> wrote in message 

> >    I was referring to the rule whereby you can't write to union member A
> > and then go on to read from union member B.
> 
> I have always wondered why that rule exists. For instance:

> typedef union foo_u {
>   long int a;
>   int b;
> } foo;

>   foo f = { 0 };
>   f.a = 1;
>   printf("%d\n", f.b);

Possibly because it is hard to write a rule which reliably forbids the
kind of union hacks which would break, but allows the ones that
wouldn't, while the kind of aliasing hackery which one can do with
unions is also done, and more reliably, with pointer aliasing. Using
your example, you could just as easily have made f a long int, and
called for &(int *)&f. Some would call that ugly, but then, what you're
doing _is_ slightly ugly, and the pointer code expresses more directly
what you're doing.

Richard
0
Reply rlb (4118) 2/12/2008 2:38:13 PM

rlb@hoekstra-uitgeverij.nl (Richard Bos) writes:
> "Chris Thomasson" <cristom@comcast.net> wrote:
>> "Tomás Ó hÉilidhe" <toe@lavabit.com> wrote in message 
>> >    I was referring to the rule whereby you can't write to union member A
>> > and then go on to read from union member B.
>> 
>> I have always wondered why that rule exists. For instance:
>
>> typedef union foo_u {
>>   long int a;
>>   int b;
>> } foo;
>
>>   foo f = { 0 };
>>   f.a = 1;
>>   printf("%d\n", f.b);
>
> Possibly because it is hard to write a rule which reliably forbids the
> kind of union hacks which would break, but allows the ones that
> wouldn't, while the kind of aliasing hackery which one can do with
> unions is also done, and more reliably, with pointer aliasing. Using
> your example, you could just as easily have made f a long int, and
> called for &(int *)&f. Some would call that ugly, but then, what you're
> doing _is_ slightly ugly, and the pointer code expresses more directly
> what you're doing.

I think you meant *(int *)&f.

The problem with pointer aliasing is that it doesn't allow for
alignment.  If f is a long int, then *(int *)&f can fail if f is not
aligned correctly as an int.  (I can even imagine a plausible
implementation where ints require stricter alignment than long ints.)

Unions require all their members to be properly aligned.

Another alternative is to use memcpy() to copy the representation of
an object into another object of the desired type; this also avoids
alignment problems.

-- 
Keith Thompson (The_Other_Keith) <kst-u@mib.org>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Reply kst-u (21469) 2/12/2008 5:15:19 PM

Richard Bos <rlb@hoekstra-uitgeverij.nl> wrote:
> 
> Possibly because it is hard to write a rule which reliably forbids the
> kind of union hacks which would break, but allows the ones that
> wouldn't,

Nonetheless, that's exactly what C99 tried to do -- it removed the
prohibition against reading a member of a union other than the last one
assigned to and relies on the representation of types rules to specify
what's defined and what isn't.  For example, given the union:

	union u {
	   int i;
	   int j;
	   unsigned char a[sizeof (int)];
	};

It's perfectly valid to store a value in i and then retrieve it from j
or to retrieve its object representation from a.

-Larry Jones

Sometimes I think the surest sign that intelligent life exists elsewhere
in the universe is that none of it has tried to contact us. -- Calvin
0
Reply lawrence.jones2 (565) 2/12/2008 6:58:16 PM

On Feb 12, 8:38 am, r...@hoekstra-uitgeverij.nl (Richard Bos) wrote:
> "Chris Thomasson" <cris...@comcast.net> wrote:
> > "Tom=E1s =D3 h=C9ilidhe" <t...@lavabit.com> wrote in message
> > >    I was referring to the rule whereby you can't write to union member=
 A
> > > and then go on to read from union member B.
>
> > I have always wondered why that rule exists. For instance:
> > typedef union foo_u {
> >   long int a;
> >   int b;
> > } foo;
> >   foo f =3D { 0 };
> >   f.a =3D 1;
> >   printf("%d\n", f.b);
>
> Possibly because it is hard to write a rule which reliably forbids the
> kind of union hacks which would break, but allows the ones that
> wouldn't, while the kind of aliasing hackery which one can do with
> unions is also done, and more reliably, with pointer aliasing.

It's not true. Some TC explicitly made type punning using
unions work (unless you step onto a trap representation),
while pointer aliasing is UB. And here's UB in action:

muntyan@munt10:/tmp$ cat alias.c
#include <stdio.h>

int func (void)
{
    int f =3D 9;
    *(short*)&f =3D 2;
    return f;
}

int main (void)
{
    printf ("%d\n", func());
    return 0;
}

muntyan@munt10:/tmp$ gcc -O2 -Wall alias.c
alias.c: In function 'func':
alias.c:6: warning: dereferencing type-punned pointer will break
strict-aliasing rules

muntyan@munt10:/tmp$ ./a.out
9

Yevgen
0
Reply ymuntyan (159) 2/12/2008 7:36:33 PM

"Richard Bos" <rlb@hoekstra-uitgeverij.nl> wrote in message 
news:47b1ae5e.4000757244@news.xs4all.nl...
> "Chris Thomasson" <cristom@comcast.net> wrote:
>
>> "Tom�s � h�ilidhe" <toe@lavabit.com> wrote in message
>
>> >    I was referring to the rule whereby you can't write to union member 
>> > A
>> > and then go on to read from union member B.
>>
>> I have always wondered why that rule exists. For instance:

you forgot to quote the following:

typedef char static_assert[
  sizeof(long int) == sizeof(int) ? 1 : -1
];




>
>> typedef union foo_u {
>>   long int a;
>>   int b;
>> } foo;
>
>>   foo f = { 0 };
>>   f.a = 1;
>>   printf("%d\n", f.b);
>
> Possibly because it is hard to write a rule which reliably forbids the
> kind of union hacks which would break, but allows the ones that
> wouldn't, while the kind of aliasing hackery which one can do with
> unions is also done, and more reliably, with pointer aliasing. Using
> your example, you could just as easily have made f a long int, and
> called for &(int *)&f. Some would call that ugly, but then, what you're
> doing _is_ slightly ugly, and the pointer code expresses more directly
> what you're doing.
>
> Richard 

0
Reply cristom (952) 2/14/2008 7:02:43 AM

On Thu, 07 Feb 2008 08:27:08 GMT, "Tom�s � h�ilidhe" <toe@lavabit.com>
wrote:

> user923005:
> 
> > That void pointers have no stride, and yet you can do this:
> > 
> > #include <stdio.h>
> > #include <stdlib.h>
> > 
> > int             main(void)
> > {
> >     void            *p[5][5];
> >     p[0][0] = NULL;
> >     return 0;
> > }
> 
> 
> "Have no stride"? As in "have no gait"? As in "don't have a particular way 
> of walking"?
> 
> Is this your way of saying you can't do pointer arithmetic on them?

Yes. In computing 'stride' has the somewhat specialized meaning of
'the distance from one array element to the next'. For contiguous
arrays, the only type in C, the stride of a pointer is (always) the
size of the array element(s) it points to. For other languages it can
(sometimes) be more complicated, but that's offtopic.

http://en.wikipedia.org/wiki/Stride_of_an_array (which in spite of the
oft-observed limitations of wikipedia is basically correct).

The type 'void' in C has no size, so a pointer to void has no stride
in standard C (although GNU C gives it one as an extension).

- formerly david.thompson1 || achar(64) || worldnet.att.net
0
Reply dave.thompson2 (767) 2/18/2008 3:18:43 AM

On Wed, 06 Feb 2008 01:01:10 +0000, Ben Bacarisse
<ben.usenet@bsb.me.uk> wrote:

> vippstar@gmail.com writes:
> 
> > On Feb 6, 12:40 am, "Tom�s � h�ilidhe" <t...@lavabit.com> wrote:
> >>     [end_ptr] = my_array + sizeof my_array/sizeof*my_array;
> >>     [versus] = /* decay from */ * (&my_array+1);
[but the dereference in the latter nominally causes UB even if in
practice it will be optimized away and work as desired]

Concur.

Aside: I don't like pend for 'pointer to end' because it's also a
perfectly good word for 'wait', 'suspend', 'delay' and I tend to read
it as that. I prefer p_n or p_lim or something along those lines.

> I can't see anything wrong with:
> 
>   int *pend = (void *)(&my_array + 1);
> 
> from a language point of view, but it is fragile in that it breaks
> when the code moves to a function and my_array becomes a pointer.
> 
sizeof myarray / sizeof *myarray also breaks in that case.

You can put (void*)&array == (void*)array in somewhere to ensure it
really is an array and not a pointer -- but this reclutters the code,
which is what the OP wanted to avoid. Of course you can hide it in a
macro -- but hiding things in macros is a whole other controversy. 

- formerly david.thompson1 || achar(64) || worldnet.att.net
0
Reply dave.thompson2 (767) 2/18/2008 3:18:43 AM

David Thompson <dave.thompson2@verizon.net> writes:

> On Wed, 06 Feb 2008 01:01:10 +0000, Ben Bacarisse
> <ben.usenet@bsb.me.uk> wrote:
<snip>
>> I can't see anything wrong with:
>> 
>>   int *pend = (void *)(&my_array + 1);
>> 
>> from a language point of view, but it is fragile in that it breaks
>> when the code moves to a function and my_array becomes a pointer.
>> 
> sizeof myarray / sizeof *myarray also breaks in that case.
>
> You can put (void*)&array == (void*)array in somewhere to ensure it
> really is an array and not a pointer

Very nice.  I had not though of that.

-- 
Ben.
0
Reply ben.usenet (6515) 2/18/2008 1:53:23 PM

On Fri, 8 Feb 2008 09:33:40 +0000 (UTC), "Herbert Rosenau"
<os2guy@pc-rosenau.de> wrote:
<snip>
> <type> *p = NULL; /* legal */
> <type> *p = 0;    /* legal */
> <type> *p = '\0'; /* legal */
> 
> memset(p, 0, n); /* illegal! when p is misused to overwrite vales not 
> type char */ 
> 
(Presumably this is not the same p just set to a null pointer. <G>)

> There is absolutely no guarantee that all bits 0 are a legal value for
> all (except char, including pointer) types. 

Nit: originally the all-zero-bits guarantee was only for _unsigned_
char. As of C99+TC2 it is for all integer types.

#include "usual/debate/about/whether/C99/is/used/enough"

Still no guarantee for floating-point in base C (although for the
common "IEEE"=60559 option it is) and pointers.

> All bits 0 does NOT mean 
> that it is a nullpointer. An address (pointer) decays in 2 parts: 
> - address bits
> - padding bits
> 
> So memset on anything, except char arrays, is always a good chance to 
> produce illegal padding bits.
> 
I don't think I'd say a _good_ chance; implementations that use
significant padding bits, though allowed, are vanishingly rare, and
similarly for not using or at least allowing an all-zero-bits rep at
least for nullpointer. But it is _some_ chance.

- formerly david.thompson1 || achar(64) || worldnet.att.net
0
Reply dave.thompson2 (767) 2/25/2008 4:28:29 AM

93 Replies
225 Views

(page loaded in 1.363 seconds)

Similiar Articles:


















7/25/2012 3:27:15 PM


Reply: