function return pointer of int?

H

Harald van Dijk

You are allowed to call foo. You are even allowed to use its return
value in limited ways (such as comparisons). You are not allowed to
store foo's result in an object, or dereference it.

The behaviour dealing with trap representations is only undefined if you
store them in an object ("produced by a side effect that modifies all or
any part of the object by an lvalue expression that does not have
character type"), or read them from an object ("read by an lvalue
expression that does not have character type"). In this code, if you
call foo and discard its result, the return value is never stored in or
read from an object.

Actually, I'm not sure how the return value could be a trap
representation at all, since it's a value, and the concept of trap
representations applies to objects, but 6.3.2.2p5 (conversions of
integers to pointers) is a clear example of when a non-lvalue expression
may explicitly be a trap representation.

I would like to add that when I posted this, I hadn't noticed the cross-
post. What I said applies to C99; C90 is different here. I do not know
whether C++ in its current form follows C90, C99, or has entirely
different rules regarding indeterminate values.
 
J

James Kanze

You are allowed to call foo. You are even allowed to use its
return value in limited ways (such as comparisons).

Are you sure? It's certainly not the case in C++, and I didn't
think it was the case in C; any use of the pointers value
(lvalue to rvalue conversion) is undefined behavior, so
comparisons are out.

If the invalid pointer is an lvalue expression, you can do
anything with it that doesn't require an lvalue to rvalue
conversion. For example, take its address, or in C++, bind it
to a reference. Which means that you can also memcpy and memcmp
it. (In the actual example, of course, the pointer isn't an
lvalue, so this is irrelevant.)
You are not allowed to store foo's result in an object, or
dereference it.
The behaviour dealing with trap representations is only
undefined if you store them in an object ("produced by a side
effect that modifies all or any part of the object by an
lvalue expression that does not have character type"), or read
them from an object ("read by an lvalue expression that does
not have character type"). In this code, if you call foo and
discard its result, the return value is never stored in or
read from an object.

Is this a change in C99? It's not the case in C++, nor was it
the case in C90.
Actually, I'm not sure how the return value could be a trap
representation at all, since it's a value, and the concept of
trap representations applies to objects, but 6.3.2.2p5
(conversions of integers to pointers) is a clear example of
when a non-lvalue expression may explicitly be a trap
representation.

It looks like the attempts to clean up a rather vague concept
accidentally went too far. (In C++, this wouldn't cause a
problem, because you return a temporary object, and of course,
this object can have a trapping representation, in which case,
you trap. Not sure about C in this regard.)
 
H

Harald van Dijk

Is this a change in C99? It's not the case in C++, nor was it the case
in C90.

Yes, this is what 6.2.6.1p5 says:
"Certain object representations need not represent a value of the object
type. If the stored value of an object has such a representation and is
read by an lvalue expression that does not have character type, the
behavior is undefined. If such a representation is produced by a side
effect that modifies all or any part of the object by an lvalue
expression that does not have character type, the behavior is
undefined.[*] Such a representation is called a trap representation."

C99 has dropped C90's wording for reading indeterminate values, and
replaced it with this.

Thanks for a bit of info on C++'s approach to this.
 
P

Phil Carmody

Keith Thompson said:
Puppet_Sock said:
On Nov 5, 4:26 am, (e-mail address removed) wrote: [...]
int *foo(int i) { return &i; }

However even evaluating foo(anything) invokes undefined behavior,
since the object pointed to by the pointer is over its lifetime, and
the pointer becomes indeterminate. (either unspecified value or trap
representation)

It only becomes undefined if you try to deref the
pointer that gets returned, because it's a pointer
to a local variable, which has gone out of scope
by the time foo returns. There is no problem with
taking the address, or passing the address around.
But if you try to manipulate things through that
address, you open the gates of undefined.

(Ok, whose sock-puppet are you?)

You're mistaken. If you have a pointer to an object, and that object
then ceases to exists, then the pointer value becomes indeterminate,
and any attempt to evaluate the pointer invokes undefined behavior.

What about not doing anything with it?

foo(0);

It's not being assigned to anything, compared to anything, computed
with, dereferenced, or basically anything that could be said to be
"evaluating" the pointer?

Phil
 
K

Keith Thompson

Phil Carmody said:
Keith Thompson said:
Puppet_Sock said:
On Nov 5, 4:26 am, (e-mail address removed) wrote: [...]
int *foo(int i) { return &i; }

However even evaluating foo(anything) invokes undefined behavior,
since the object pointed to by the pointer is over its lifetime, and
the pointer becomes indeterminate. (either unspecified value or trap
representation)

It only becomes undefined if you try to deref the
pointer that gets returned, because it's a pointer
to a local variable, which has gone out of scope
by the time foo returns. There is no problem with
taking the address, or passing the address around.
But if you try to manipulate things through that
address, you open the gates of undefined.

(Ok, whose sock-puppet are you?)

You're mistaken. If you have a pointer to an object, and that object
then ceases to exists, then the pointer value becomes indeterminate,
and any attempt to evaluate the pointer invokes undefined behavior.

What about not doing anything with it?

foo(0);

It's not being assigned to anything, compared to anything, computed
with, dereferenced, or basically anything that could be said to be
"evaluating" the pointer?

When an expression statement is executed, the expression is evaluated
and its result is discarded. An implementation might reasonably store
the result in an address register; the act of storing an invalid
address in an address register might cause a trap.

But Harald made a very interesting point. The pointer value returned
by foo is indeterminate, meaning that it's either an unspecified value
or a trap representation. But a trap representation is not a value.
His argument is that there's no representation (at least as far as the
language semantics are concerned) until and unless the value is stored
in an object.

On the other hand, this means that the standard's definition of an
indeterminate *value* as either an unspecified value or a trap
*representation* doesn't make a whole lot of sense.

I'm not sure how this should be resolved.
 
B

baichuan0798

Hi Ian, thank you,

But how can I get the address of t in the main() scope, if I want to
use function?





- ÏÔʾÒýÓõÄÎÄ×Ö -



void *fun(int i)
{
void *p = &i;
p += (1<<5) + 4;

return p;
}
 
B

Barry Schwarz

void *fun(int i)
{
void *p = &i;
p += (1<<5) + 4;

return p;
}

What do you think this accomplishes?

Your first executable statement contains a constraint violation. If
you ignore that (some compilers do as an extension), it invokes
undefined behavior if sizeof(int) < 36. If you have a system with
really large int, then the value returned still becomes indeterminate
as soon as the function returns.
 
J

James Kanze

[...]
What do you think this accomplishes?
Your first executable statement contains a constraint violation.

I'm not sure what you mean by the "first executable statement".
The definition of p (with its initialization) definitely
generates executable code, and is executed. And there's no
problem with this statement; it is legal and well defined, and
must work in any implementation.

The second statement, of coruse, is ill formed. Pointer
arithmetic is illegal on pointers to void. This is a
constraint violation, which requires a diagnostic.
If you ignore that (some compilers do as an extension), it
invokes undefined behavior if sizeof(int) < 36.

If you have invoked the compiler in a non-conformant mode, what
it does is defined by the compler. If a compiler accepts the
second statement above, you can no longer argue about the code
with regards to the language. For that matter, technically,
once the compiler has issued the diagnostic, it has fulfilled
its obligation, and the rest is undefined behavior. (QoI
issues, of course, introduce additional constraints, but as far
as the standard is concerned, a compiler that reformats your
hard disk anytime you try to compile an ill-formed program is
compliant. The compiler could even document that it's
diagnostic message was to turn on the light of the hard disk
drive for an indeterminate time---I don't think that the
standard formally requires the diagnostic to be text.)
 
J

James Kuyper

Keith Thompson wrote:
....
But Harald made a very interesting point. The pointer value returned
by foo is indeterminate, meaning that it's either an unspecified value
or a trap representation. But a trap representation is not a value.
His argument is that there's no representation (at least as far as the
language semantics are concerned) until and unless the value is stored
in an object.

More to the point, the only cases associated with trap representations
where the C standard explicitly says the behavior is undefined are the
creation of a trap representation in an object by any method other than
use of an lvalue of character type, or by attempting to access the value
of an object that already contains a trap representation.
On the other hand, this means that the standard's definition of an
indeterminate *value* as either an unspecified value or a trap
*representation* doesn't make a whole lot of sense.

I think that the fundamental problem was the decision of the C committee
to define an indeterminate "value" as including, as one possibility, a
trap "representation". It should probably have been called an
"indeterminate representation", but that would have required rewriting
every place in the standard that currently uses "indeterminate value",
and the rewrite would be substantially clumsier than the current wording.

In particular, such a change would make it more complicated to describe
what is going wrong in this particular program: returning a pointer from
a function that becomes invalid precisely because of the fact the
function has returned. Maybe we also need the concept of a "trap value",
distinct from the idea of a "trap representation"?
 
B

Ben Bacarisse

James Kanze said:
]
void *fun(int i)
{
void *p = &i;
p += (1<<5) + 4;
return p;
}
What do you think this accomplishes?
Your first executable statement contains a constraint violation.

I'm not sure what you mean by the "first executable statement".
The definition of p (with its initialization) definitely
generates executable code, and is executed. And there's no
problem with this statement; it is legal and well defined, and
must work in any implementation.

The cross-post is confusing matters (as always!).

In C there is a clear distinction between declarations and statements,
both in the formal syntax as well as in the less formal text of the
standard, so "the first statement" refers unambiguously to the
increment of p (and the extra "executable" is redundant). In C++, as
you know, there is a "declaration statement" so the distinction is
lost. Barry is presumably assuming the code is C.
 
J

James Kanze

James Kanze said:
    [...]
void *fun(int i)
{
   void *p = &i;
   p += (1<<5) + 4;
   return p;
}
What do you think this accomplishes?
Your first executable statement contains a constraint violation.
I'm not sure what you mean by the "first executable
statement". The definition of p (with its initialization)
definitely generates executable code, and is executed.  And
there's no problem with this statement; it is legal and well
defined, and must work in any implementation.
The cross-post is confusing matters (as always!).

Especially as it concerns an area in which C and C++ try to be
identical---all too often using different words to (hopefully)
say the same thing.
In C there is a clear distinction between declarations and
statements, both in the formal syntax as well as in the less
formal text of the standard, so "the first statement" refers
unambiguously to the increment of p (and the extra
"executable" is redundant).  In C++, as you know, there is a
"declaration statement" so the distinction is lost.  Barry is
presumably assuming the code is C.

I'd missed that difference. Historically, of course, it made
sense, since you couldn't mix declarations and (other)
statements.

My main point still holds, of course: no C or C++ compiler will
accept pointer arithmetic on an incomplete type (and void is an
incomplete type). The code simply won't compile.
 
J

jameskuyper

James Kanze wrote:
....
My main point still holds, of course: no C or C++ compiler will
accept pointer arithmetic on an incomplete type (and void is an
incomplete type). The code simply won't compile.

More accurately: no fully conforming compiler for either language will
accept such code without first issuing a diagnostic. A number of
popular compilers will indeed accept pointer arithmetic on void*,
which is performed as if it were char*. As long as they also issue the
mandatory diagnostic message, they can do so while remaining fully
conforming.
 
K

Keith Thompson

James Kanze said:
My main point still holds, of course: no C or C++ compiler will
accept pointer arithmetic on an incomplete type (and void is an
incomplete type). The code simply won't compile.

Any conforming C or C++ compiler must issue a diagnostic for any
attempt to peform pointer arithmetic on a pointer to an incomplete
type, since it's a constraint violation. At least in C, it's not
actually required to reject the translation unit; it may legally
compile it successfully, and the resulting program has behavior that
is not defined by the standard (though of course it may be defined by
the implementation).

The only case where a C translation unit *must* be rejected is when it
contains a #error directive that survives preprocessing. I'm not sure
what the corresponding rules are for C++.

In particular, gcc (specifically the C compiler that's part of the gcc
suite) allows arithmetic on void* by default. This is a permissible
extension as long as it issues a diagnostic. g++ doesn't support this
particular extension by default; I don't know whether it has an option
to enable it.
 
M

Michael

Davy said:
Hi all,

I am writing a function, which return the pointer of the int. But it
seems to be wrong. Any suggestion?

int * get_p_t(int t) {
return &t;
}

int main()
{
printf("v1\n");
int t = 5;
int * p_t[2];

p_t[0] = &t; // right
p_t[1] = get_p_t(t); //wrong

return 0;
}

Best regards,
Davy
change
int * get_p_t(int t) {
to
int * get_p_t(int&t) {
 

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
474,167
Messages
2,570,913
Members
47,455
Latest member
Delilah Code

Latest Threads

Top