Printing a NULL pointer

M

Michael Mair

Tim said:
Interesting idea. Have you ever actually seen it? I'd expect
that normally the distinction would be caught by during type
checking.

People who get this wrong probably have no scruples inserting the
"necessary" type casts to shut up the oh-so-noisy compiler... :-/

[snip]


Cheers
Michael
 
L

Lawrence Kirby

On Sun, 19 Jun 2005 09:43:34 -0700, Tim Rentsch wrote:

....
Yes, you're writing outside the bounds of the array. The array in
question is out which is (in the specified conditions) a 2 element
array. What you are passing as the first argument to sprintf() is a
pointer to the first element of this 2 element array. When the call to
sprintf() writes to out[2] it invokes undefined behaviour.


If you look at the actual language I think you'll find
that it's not as simple as that. So I need to ask you
to cite "chapter and verse", as they say. I've looked
at the actual language quite carefully.


I know what you mean but I believe what I wrote is the intent.

6.5.6p8 says

"... 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."

So given

char (*out)[NIBBLES_PER_CHAR] = malloc( NIBBLES_PER_CHAR*len + 1 );

out[N] is an array of NIBBLES_PER_CHAR characters and if I write

char *p = out[N];

then p is a pointer to the first element of an array of NIBBLES_PER_CHAR
chars. p+NIBBLES_PER_CHAR is a pointer to one past the last element of
this array so *(p+NIBBLES_PER_CHAR) violates the "shall" requirement above
resulting in undefined behaviour.

Granted the original example uses sprintf() rather than + and *, snd it is
less clear how the one last the end rules apply to standard library
functions.

Lawrence
 
T

Tim Rentsch

Lawrence Kirby said:
On Sun, 19 Jun 2005 09:43:34 -0700, Tim Rentsch wrote:

...
Yes, you're writing outside the bounds of the array. The array in
question is out which is (in the specified conditions) a 2 element
array. What you are passing as the first argument to sprintf() is a
pointer to the first element of this 2 element array. When the call to
sprintf() writes to out[2] it invokes undefined behaviour.


If you look at the actual language I think you'll find
that it's not as simple as that. So I need to ask you
to cite "chapter and verse", as they say. I've looked
at the actual language quite carefully.


I know what you mean but I believe what I wrote is the intent.


ITYM you believe you know what I mean.

It isn't obvious even that there is a single intent, let alone
what "the" intent is. And, as Dennis Ritchie said:

"The intentions of the committee are irrelevant;
only their document matters."

6.5.6p8 says

"... 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."

Clarifying context - 6.5.6 p8 earlier says:

"... 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 of the subscripts of the resulting and
original array elements equals the integer expression. ..."

So given

char (*out)[NIBBLES_PER_CHAR] = malloc( NIBBLES_PER_CHAR*len + 1 );

out[N] is an array of NIBBLES_PER_CHAR characters

What you mean is that 'out[N]' has type 'char [NIBBLES_PER_CHAR]'.
That doesn't change the storage that '&out[N][0]' points at. If
we have

char buf[1000];
char (*x)[2] = (char (*)[2])buf;

then the pointer value of 'x[0]' (ie, '&x[0][0]') still points
at an array object of 1000 characters.

and if I write

char *p = out[N];

then p is a pointer to the first element of an array of NIBBLES_PER_CHAR
chars.

You've confusing the term 'array object' with the notion of array
type. The variable 'p' is only a pointer; the type doesn't know
anything about the array object it points into. The array object that
'p' points into is an array starting at the address returned by malloc
and extending for NIBBLES_PER_CHAR*len + 1 characters. There must be
such an array object, because of how malloc works.

p+NIBBLES_PER_CHAR is a pointer to one past the last element of
this array so *(p+NIBBLES_PER_CHAR) violates the "shall" requirement above
resulting in undefined behaviour.

Converting a pointer (ie, assigning the pointer returned by malloc)
doesn't change the underlying storage. If we had written

char *p = ((char (*)[1000000000]) out[N])[0];

the value of 'p' is just the same as previously; the cast doesn't
magically make an array object of a billion characters spring into
being. Similarly if the cast had been to (char(*)[1]) that doesn't
suddenly reduce the amount of space available; 'p' still points into
the same array object as before, namely the array object returned by
malloc.

Granted the original example uses sprintf() rather than + and *, snd it is
less clear how the one last the end rules apply to standard library
functions.

I believe that doesn't affect the result one way or the other. Once
the expression 'out[N]' gets turned into a character pointer the
result should be the same whether sprintf() is used or not.


[Sorry for the belated reply; I've been busy.]
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top