Pointing past valid memory

J

jois.de.vivre

Hello, I was wondering if the following code was ok:

// ----------- start code

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

int main(int argc, char** argv)
{
char buf[10];
memset(buf, 'a', sizeof(buf));

int i=0;
char *ptr = &buf[0];

while (1) {
if (i == sizeof(buf))
break;

char byte = *ptr++; /* *** (1) *** */
printf("%c", byte);
++i;
}
printf("\n");
return 0;
}

// ----------- end code

In the last iteration of the loop, ptr will point past the last byte
of memory allocated for this array. This is a contrived example, but
I just wanted to know whether it's ok to point past the valid memory
for this array as long as I don't read or modify the contents of
memory at that address afterwards. On the surface it seems like this
code won't cause me any problems.

Thanks,

Prashant
 
S

Scott Fluhrer

Hello, I was wondering if the following code was ok:

// ----------- start code

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

int main(int argc, char** argv)
{
char buf[10];
memset(buf, 'a', sizeof(buf));

int i=0;
char *ptr = &buf[0];

while (1) {
if (i == sizeof(buf))
break;

char byte = *ptr++; /* *** (1) *** */
printf("%c", byte);
++i;
}
printf("\n");
return 0;
}

// ----------- end code

In the last iteration of the loop, ptr will point past the last byte
of memory allocated for this array. This is a contrived example, but
I just wanted to know whether it's ok to point past the valid memory
for this array as long as I don't read or modify the contents of
memory at that address afterwards. On the surface it seems like this
code won't cause me any problems.

Yes, that's safe. The Standard specifically allows you to set a pointer to
just past the end of an array (with the provisio that you pointed out; that
you don't actually reference that location).
 
T

tguclu

Hi,

If your concern by saying "point past the valid memory " is ,
Would it cause a problem to point an unallocated location ? Yes it
would. Because line notated by /* *** (1) *** */ , post-increases
ptr and in the final loop ptr will show next adjacent memory
location.But we did't allocate any place for this location and we
don't know what lies in it.

But looking at your implementation , this would cause no problem
because you don't use ptr anywhere else.





Hello, I was wondering if the following code was ok:

// ----------- start code

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

int main(int argc, char** argv)
{
char buf[10];
memset(buf, 'a', sizeof(buf));

int i=0;
char *ptr = &buf[0];
char *ptr = buf ; //this would do the same
 
K

Keith Thompson

tguclu said:
If your concern by saying "point past the valid memory " is ,
Would it cause a problem to point an unallocated location ? Yes it
would. Because line notated by /* *** (1) *** */ , post-increases
ptr and in the final loop ptr will show next adjacent memory
location.But we did't allocate any place for this location and we
don't know what lies in it.

But looking at your implementation , this would cause no problem
because you don't use ptr anywhere else.
[...]

Please don't top-post. Read the following for more information:

http://www.caliburn.nl/topposting.html
http://www.cpax.org.uk/prg/writings/topposting.php

I think you've misunderstood the rules about pointers.

Creating a pointer value that points just past the end of an array is
legal. (The standard explicitly allows this as a special case,
because it's useful for some algorithms.) Attempting to dereference
such a pointer invokes undefined behavior.

Creating a pointer value that points *beyond* the end of an array, or
before its beginning, invokes undefined behavior. Just creating such
a pointer value invokes UB, even if you don't dereference it.
 
A

Army1987

Hello, I was wondering if the following code was ok:

// ----------- start code

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

int main(int argc, char** argv)
{
char buf[10];
memset(buf, 'a', sizeof(buf));

int i=0;
char *ptr = &buf[0];

while (1) {
if (i == sizeof(buf))
break;

char byte = *ptr++; /* *** (1) *** */
printf("%c", byte);
++i;
}
printf("\n");
return 0;
}

// ----------- end code

In the last iteration of the loop, ptr will point past the last byte
of memory allocated for this array. This is a contrived example, but

for (ptr = buf; ptr < buf + sizeof(buf); ptr++)
putchar(*ptr);
is far less contrived, but it is perfectly equivalent.
I just wanted to know whether it's ok to point past the valid memory
for this array as long as I don't read or modify the contents of
memory at that address afterwards. On the surface it seems like this
code won't cause me any problems.

Yes, but only immediately past the last element. For example (buf + 10) is a valid pointer, even if you can't dereference it, but
(buf
+ 11) has undefined behaviour.
 
A

Army1987

tguclu said:
Hello, I was wondering if the following code was ok:

// ----------- start code

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

int main(int argc, char** argv)
{
char buf[10];
memset(buf, 'a', sizeof(buf));

int i=0;
char *ptr = &buf[0];
char *ptr = buf ; //this would do the same
while (1) {
if (i == sizeof(buf))
break;

char byte = *ptr++; /* *** (1) *** */
printf("%c", byte);
++i;
}
printf("\n");
return 0;

}

// ----------- end code

In the last iteration of the loop, ptr will point past the last byte
of memory allocated for this array. This is a contrived example, but
I just wanted to know whether it's ok to point past the valid memory
for this array as long as I don't read or modify the contents of
memory at that address afterwards. On the surface it seems like this
code won't cause me any problems.
Hi,

If your concern by saying "point past the valid memory " is ,
Would it cause a problem to point an unallocated location ? Yes it
would. Because line notated by /* *** (1) *** */ , post-increases
ptr and in the final loop ptr will show next adjacent memory
location.But we did't allocate any place for this location and we
don't know what lies in it.

But looking at your implementation , this would cause no problem
because you don't use ptr anywhere else.
Only because it is just past the end of buf. If it were any
further, it'd cause UB. (Yes, I don't believe on your machine
that'd cause problems if you computed buf + 16 without ever
dereferencing it, but it's something I would not do on a DS9K.)
 
A

Army1987

pete said:
The non bizarre way of writing that, is:

while (i != sizeof(buf)) {

Or
for( ; i < sizeof buf ; ) {

and i = 0 before the loop and ++i at the end of its body can then
be moved to more appropriate places.
 
M

Manish Tomar

Yes, but only immediately past the last element. For example (buf + 10) is a valid pointer, even if you can't dereference it, but
(buf
+ 11) has undefined behaviour.

Why should this cause undefined behaviour if it is not accessed.
Pointer just happens to be another variable storing address instead of
any other data. As long as it stores data within its range (i.e. say
32-bit address) i dont think it should give undefined behaviour as
long as it is not accessed. In that case the following lines should
have undefined behaviour:

int *p = NULL; // points to 0, invalid mem
int *p; // store garbage address which may / may not be correct

(buff + 11) will just return a number. Thats it. Dont u think??

Regards,
Manish
(http://manishtomar.blogspot.com)
 
C

Chris Dollin

Manish said:
Why should this cause undefined behaviour if it is not accessed.

Because the standard says so.
Pointer just happens to be another variable storing address instead of
any other data.

On some machines, actual machines that have existed and have had C
implementations, the restrictions on pointers are stronger than that.

In some implementations, such as those intended for debugging support,
pointers are more than "just addresses", and computing out-of-range
pointers will raise some kind of error.
As long as it stores data within its range (i.e. say
32-bit address) i dont think it should give undefined behaviour as
long as it is not accessed.

Nevertheless, some machine architectures did/do check, and the
C standard allows them to do so.

Note that "has undefined beahviour" doesn't mean "traps". It means
that the implementation is allowed to do /anything/. It may trap.
It may wrap. It may return zeroes. The Standard doesn't place any
constraints on the implementation at all.
In that case the following lines should
have undefined behaviour:

int *p = NULL; // points to 0, invalid mem

It does /not/ "point to 0". It's a /null pointer/; it doesn't
point anywhere.
int *p; // store garbage address which may / may not be correct

It has an indeterminate value; any use of it invokes undefined
behaviour.
(buff + 11) will just return a number. Thats it. Dont u think??

I don't think so -- but an implementation can legally do so if
that's convenient for it.
 
S

Stephen Sprunk

Manish Tomar said:
Why should this cause undefined behaviour if it is not accessed.

Because the Standard says so.
Pointer just happens to be another variable storing address
instead of any other data. As long as it stores data within its
range (i.e. say 32-bit address) i dont think it should give
undefined behaviour as long as it is not accessed.

Perhaps that's how it works on _your_ implementation. There are others
where pointers have special registers and even the creation or assignment of
an invalid pointer causes a trap, whether you dereference it or not. C
allows that behavior.
In that case the following lines should
have undefined behaviour:

int *p = NULL; // points to 0, invalid mem

NULL is a special case specifically allowed by the Standard.
int *p; // store garbage address which may / may not be correct

If you do anything with p other than assign a valid address or NULL to it,
you get UB.
(buff + 11) will just return a number. Thats it. Dont u think??

No, the Standard says that's UB, and so it is.

Just because your implementation treats pointers and integers the same under
the hood (and thus lets you get away with all sorts of things) doesn't mean
that all do. C runs on a lot of systems that aren't so forgiving...

S
 
F

Flash Gordon

Manish Tomar wrote, On 04/07/07 12:40:
Why should this cause undefined behaviour if it is not accessed.

Because the C standard says it does. As a programmer you really do not
need to know more than that.
Pointer just happens to be another variable storing address instead of
any other data. As long as it stores data within its range (i.e. say
32-bit address) i dont think it should give undefined behaviour as
long as it is not accessed. In that case the following lines should
have undefined behaviour:

int *p = NULL; // points to 0, invalid mem

No, it is a null pointer, it might point to some address other than 0 or
it might be something that does not actually point to anywhere at all.
int *p; // store garbage address which may / may not be correct

(buff + 11) will just return a number. Thats it. Dont u think??

Address are not necessarily just numbers, and the standard is written
the way it is to allow for implementations which can generate hardware
traps on invalid pointer arithmetic, or even on just loading an invalid
pointer, and crashes your program.
 
C

christian.bau

Why should this cause undefined behaviour if it is not accessed.
Pointer just happens to be another variable storing address instead of
any other data. As long as it stores data within its range (i.e. say
32-bit address) i dont think it should give undefined behaviour as
long as it is not accessed. In that case the following lines should
have undefined behaviour:

int *p = NULL; // points to 0, invalid mem
int *p; // store garbage address which may / may not be correct

(buff + 11) will just return a number. Thats it. Dont u think??

(buff + 11) will not return a number, because there is no return
statement, and it will not yield a number, because if anything, it
would yield a pointer. Programming requires exactness, don't you
think?

If "int *p = (buff + 11) doesn't invoke undefined behaviour, it must
be defined behaviour, right? So what is that behaviour? Would you say
that for example p != NULL must yield a value of 1 (true), and that p
buff must yield a value of 1? Now what if we replace 11 with a
different numbers, say 12, or 100000, or 4000000000? Do you still
think that p > buffer must yield a value of 1? If not, where would
this change?

"Undefined behaviour" means: The behaviour is not defined by the C
Standard. It doesn't matter whether you can imagine how something
undesired happens or not, what matters is whether the behaviour is
defined or not.

Now take this is a little challenge: Give an reasonable scenario where
a highly optimising compiler produces a very unexpected result if a
pointer expression (p + 11) is evaluated.
 
H

Harald van =?UTF-8?B?RMSzaw==?=

christian.bau said:
Now take this is a little challenge: Give an reasonable scenario where
a highly optimising compiler produces a very unexpected result if a
pointer expression (p + 11) is evaluated.

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

void g(void);

void f(void) {
char *p = malloc(10);
if (p == NULL) return;

p + 11;

for (;;) {
g();
printf("%c\n", p[10]);
}
}

void g(void) {
exit(0);
}

An optimising compiler may assume that because p+11 is evaluated, p+11 must
be a valid address, so p+10 must be dereferencable. As neither g nor printf
have any way of modifying p[10], the read of p[10] can be moved out of the
loop. And when p[10] is then evaluated, the program crashes.

A highly optimising compiler, on the other hand, would drop p entirely. :)
 
M

Manish Tomar

Thanks a lot guys for the response :). I haven't read the standard and
I didnt think from C implementation point of view. Now, I am clear
with it.
 

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

Staff online

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top