Is the behaviour defined

O

Old Wolf

Old said:
#include <stdio.h>
#include <string.h>

int main(void)
{
if ( sizeof(char *) >= sizeof "abc" )
{
char *c;
c = (char *)&c;
strcpy(c,"abc");
puts(&c);

Of course I meant:
puts( (char *)&c );
 
M

Malcolm Dew-Jones

Joe Wright ([email protected]) wrote:
: grid wrote:
: >> int main()
: >> {
: >> char *c;
: >> c = &c;
: >> strcpy(c,"abc");
: >> puts(&c);
: >> retun 0; }
: >
: > It should be return 0;
: > The spell of the return is wrong in the test program above.I did not
: > have it in my test program which I compiled , but added it while
: > composing this mail , of the fear of getting battered by the C language
: > purists ;).

: Let me be the first to warn of impending doom. You define c an object of
: type char*. For sake of argument, the compiler arbitrarily places c at
: address 0100. Now you assign this address to c itself. If your warnings
: are high enough, you will be told about this. c has type char* while &c
: has type char**. The two are incompatible types.

: Assuming you got away with the assingment 'c = &c;' your c doesn't point
: to usable memory.


"c doesn't point to usable memory"

No, c is on the stack and so &c is pointing at one end of the free space
on the stack.

This sort of trick would be seen in code such as an operating system.
The idea is to get the current address of the top of the stack so as to
"malloc" yourself some memory from the stack without using a memory
manager. If you want to use C instead of assembler to manipulate what
goes on inside the computer then you may need to use that sort of trick,
just like when you point your C variables at hard coded video address or
port addresses, etc.

In this case though, the call to strcpy is very problematic because strcpy
will be using that same area of the stack for its call frame. So in the
above there is a possibility of corrupting the stack. I think exactly one
byte can be safely written since we don't know in general which direction
the stack grows, or the endianess of the pointer.

In assembler you would commonly do the equivalent to the above, (take the
address of the memory which is the stack), but you would then move the
stack pointer before using the memory.

That is exactly what the old malloc variant "alloca()" did, but I don't
recall seeing that call available in any modern system.

It certainly isn't good for any normal application to do this trickery.

$0.10
 
M

Michael Wojcik

No, c is on the stack and so &c is pointing at one end of the free space
on the stack.

&c points to the first byte of the contents of c. Nothing more is
guaranteed by the language.

The C language does not require a "stack" in this sense (a contiguous
area of memory from which automatic variables, and other things, can
be allocated). It requires some stack-equivalent mechanism to
support the function-call semantics, but that can be, for example,
chained activation records.

There need not be any "free space" adjacent to any automatic
variable, and the implementation certainly need not allow the program
access to it.
I think exactly one
byte can be safely written since we don't know in general which direction
the stack grows, or the endianess of the pointer.

There are conforming implementations where this is not true.

As Old Wolf noted, C guarantees that we can inspect and alter the
contents of the "c" variable using a character pointer. It does not
guarantee that we can do anything outside the extent of that object
using a pointer to it; in fact, any such access invokes undefined
behavior.
 
M

Malcolm Dew-Jones

Michael Wojcik ([email protected]) wrote:

: >
: > No, c is on the stack and so &c is pointing at one end of the free space
: > on the stack.

: &c points to the first byte of the contents of c. Nothing more is
: guaranteed by the language.

Well I agree, I didn't mean to imply that the language required what I
described, only that the idiom of taking the address of an auto variable
is not some thoughtless bug, but is a real technique used in situations
when the alternative might be assembler language or FORTH or such like.
 
J

Joe Wright

Keith said:
Or just
puts(c);

No. puts(c); will probably segfault (it does here) because c is neither
an array nor a pointer. This seems to "work"..

#include <stdio.h>
#include <string.h>
int main(void) {
char *c;
c = (char*)&c;
strcpy(c, "abc");
puts((char*)&c);
return 0;
}
 
K

Keith Thompson

Joe Wright said:
No. puts(c); will probably segfault (it does here) because c is
neither an array nor a pointer. This seems to "work"..

#include <stdio.h>
#include <string.h>
int main(void) {
char *c;
c = (char*)&c;
strcpy(c, "abc");
puts((char*)&c);
return 0;
}

A quibble: Yes, c is a pointer object, since it's declared as
"char *c;". But you're right, it doesn't contain a valid pointer
value; it contains the bytes 'a', 'b', 'c', and '\0' (plus other
garbage if sizeof(char*)>4). I was thrown off by the fact that c
contains its own address; that doesn't imply that c and &c are the
same value. I should have tried my own code before posting it.

"puts((char*)&c);" is correct (or as correct as anything can be in a
silly program like this one). "puts(c);" is wrong; it attempts to
dereference a garbage pointer.
 
M

Marc Boyer

Le 01-10-2005 said:
Hi,
A collegue of mine is of the opinion that the behaviour of the
following program is defined,but I am a little apprehensive.

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

int main()
{
char *c;
c = &c;
strcpy(c,"abc");
puts(&c);
retun 0;
}

As other have sayed, the two problems are the conversion
(char**) -> (char*) and the sizeof(char*).

Here is a well defined program:

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

int main(){
void* p= &p;
if ( strlen("abc") < sizeof(p) ){
memcpy(p, "abc", strlen("abc")+1 );
puts( (char*) p );
} else {
puts("Pointers too small");
}
return 0;
}

Marc Boyer
 
P

pete

Marc Boyer wrote:
Here is a well defined program:

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

int main(){
void* p= &p;
if ( strlen("abc") < sizeof(p) ){
memcpy(p, "abc", strlen("abc")+1 );
puts( (char*) p );

It's not defined and these issues have
already been gone over in this thread.

p is an indeterminate pointer after the memcpy call.

Accessing the value of p at that point,
as the cast operator does, is undefined.

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

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

int main(void)
{
void *p = &p;

if (strlen("abc") < sizeof(p)) {
strcpy(p, "abc");
puts((char *)&p);
} else {
puts("Pointers too small");
}
return 0;
}
 
M

Marc Boyer

Le 07-10-2005 said:
It's not defined and these issues have
already been gone over in this thread.

p is an indeterminate pointer after the memcpy call.

Yes, sorry. I ve not posted the right version.
Accessing the value of p at that point,
as the cast operator does, is undefined.

Of course.
#include <stdio.h>
#include <string.h>

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

int main(void)
{
void *p = &p;

if (strlen("abc") < sizeof(p)) {
strcpy(p, "abc");
puts((char *)&p);
} else {
puts("Pointers too small");
}
return 0;
}

Yes
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top