doubt in USING POINTERS

J

James Kuyper

[email protected] (Mantorok Redgormor) wrote in message news: said:
I put together the following example:

#include <stdio.h>
struct foo { int array[10]; };
static struct foo example(int);
int main(void)
{
printf("%u\n", sizeof example(10).array);

return 0;
}

struct foo example(int a)
{
static struct foo woo;
woo.array[0] = a; /* pointless */
return woo;
}


But now, something I found to be bizarre was the fact that sizeof
computed the size of the array. I only say this is bizzare because I
thought sizeof did not evaluate its operand? In this specific case it
would have to evaluate its operand to get the size, right?

The term "evaluate" means "determine the value of"; the value of
example(10).array is not needed here. All that's needed for "sizeof
example(10).array" is the return type of example(), which is struct
foo, and the type of 'array' (int[10]). An actual call to example() is
not needed, nor are the values stored in array needed.
Furthermore, it would have to compute this size at run-time and not
compile-time, correct?

No, since it does not depend in any way upon the actual value returned
by example().
 
D

Deepak

The "sizeof" is an operator. The argument to the sizeof operator is
always available to compiler at compile time. And the specification
says that the for array type variables, the sizeof should return the
total number of bytes that array can hold.
The behaviour can be verified for any type of the array.
The array can be a part of the structure declaration but this
information is also available at compile time hence if you are using
sizeof anywhere in the program as here the value calculated by sizeof
will be replaced by 40 (assuming int is 4 bytes i.e. 4*10=40).

You can also verify it by compiling the code with an compiler option
that generates assembly listing. You will find the value calculated by
sizeof operator in the assembly listing generated.
DG
_______________________________________________________________________________
Gabriel Dos Reis said:
(e-mail address removed) (Mantorok Redgormor) writes:

| After playing around with this and getting a better understanding I
| see how it all works. But, is this array really an array or does coord
| just decay into a pointer to the arrays first element?

It is an array, albeit a non-lvalue. It can decay to pointer in
appropriate context (e.g. function call argument). You can even take
its sizeof and compute its length.

-- Gaby

This is great. I have never known this. In this specific context an
array on the right-hand side of an assignment operator does not decay
into a pointer to its first element!

I put together the following example:

#include <stdio.h>
struct foo { int array[10]; };
static struct foo example(int);
int main(void)
{
printf("%u\n", sizeof example(10).array);

return 0;
}

struct foo example(int a)
{
static struct foo woo;
woo.array[0] = a; /* pointless */
return woo;
}


But now, something I found to be bizarre was the fact that sizeof
computed the size of the array. I only say this is bizzare because I
thought sizeof did not evaluate its operand? In this specific case it
would have to evaluate its operand to get the size, right?
Furthermore, it would have to compute this size at run-time and not
compile-time, correct?
 
B

Barry Schwarz

The "sizeof" is an operator. The argument to the sizeof operator is
always available to compiler at compile time. And the specification

Except in the case of variable length arrays allowed in C99.


<<Remove the del for email>>
 
G

Gabriel Dos Reis

(e-mail address removed) (Mantorok Redgormor) writes:

| But now, something I found to be bizarre was the fact that sizeof
| computed the size of the array. I only say this is bizzare because I
| thought sizeof did not evaluate its operand?

It does not evaluate its operand, except when it does. In the
specific example you showed, it does not evaluate its operand; what it
does it compute its type, which is know at translation type.

-- Gaby
 
J

j

Gabriel Dos Reis said:
| But if I write:
|
| int *p=example(10).coord;
|
| p is indeterminate here right? (since the instance of
| struct foo isn't with us anymore)

p becomes a dangling pointer, yes.

Initializing or assigning p the address of coord doesn't seem to be the case
on gcc(that is, it doesn't work). This however works with lcc. Is this a bug
with gcc or is lcc in violation of the standard? Shouldn't coord decay into
a pointer to the first element of the array?
| Is example(10).coord[0] any different then?

Yes.

int x = example(10).coord[0];

is well defined;
 
L

lawrence.jones

In comp.std.c j said:
Initializing or assigning p the address of coord doesn't seem to be the case
on gcc(that is, it doesn't work). This however works with lcc. Is this a bug
with gcc or is lcc in violation of the standard? Shouldn't coord decay into
a pointer to the first element of the array?

In C99, yes; in C90, no. (In C90, the array to pointer conversion only
happened for lvalue arrays.)

-Larry Jones

Apparently I was misinformed. -- Calvin
 
C

CBFalconer

In C99, yes; in C90, no. (In C90, the array to pointer conversion
only happened for lvalue arrays.)

Assuming the field coord is defined as an int array, what is wrong
with:

int *p = &(example(10).coord);
 
J

j

CBFalconer said:
Assuming the field coord is defined as an int array, what is wrong
with:

int *p = &(example(10).coord);

Well, & operates on an lvalue. And since coord is considered to be a
non-lvalue -- in this context -- in c90, then you can't use the address-of
operator on it.

Would anyone mind quoting the relevant section in c90 for this by the way?
Or is there some free draft for c90 that I could take a look at? I would
just like to compare the differences between c90 and c99.
 
C

CBFalconer

j said:
Well, & operates on an lvalue. And since coord is considered to be a
non-lvalue -- in this context -- in c90, then you can't use the
address-of operator on it.

On reflection, true. There is no guarantee that the return value
from example() has any sort of address, in fact the reverse is
probably true. On this reasoning C99 should not allow this
either.

So we would have to write:

int *p;
thing t; /* where thing is a suitable struct */
...
t = example(10);
p = &(t.coord);

The basic problem is that earlier someone snipped the definitions
of example() and what it returns. I knew I should have put in
some such :-(

To answer the original question, lcc is faulty if it allows this.
 
G

Gabriel Dos Reis

| | >
| > | But if I write:
| > |
| > | int *p=example(10).coord;
| > |
| > | p is indeterminate here right? (since the instance of
| > | struct foo isn't with us anymore)
| >
| > p becomes a dangling pointer, yes.
|
| Initializing or assigning p the address of coord doesn't seem to be the case
| on gcc(that is, it doesn't work).

If with -std=c99, you still see the same behaviour, then that is a bug
in the GCC/gcc compiler.

-- Gaby
 
K

Keith Thompson

Gabriel Dos Reis said:
If with -std=c99, you still see the same behaviour, then that is a bug
in the GCC/gcc compiler.

I got an internal compiler error in gcc with the following program:

struct foo {
int arr[10];
};

static struct foo func(void)
{
struct foo result;
return result;
}

int main(void)
{
int *ptr = func().arr;
return 0;
}

I've reported it to the gcc bugzilla system; see
<http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12446>.

(I'm not quite sure what gcc *should* do with this.)
 
G

Gabriel Dos Reis

| [...]
| > If with -std=c99, you still see the same behaviour, then that is a bug
| > in the GCC/gcc compiler.
|
| I got an internal compiler error in gcc with the following program:

Odd.
Anyway, an ICE is (almost) always a bug in the compiler.

[...]

| int main(void)
| {
| int *ptr = func().arr;
| return 0;
| }
|
| I've reported it to the gcc bugzilla system; see
| <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12446>.

Thanks!

| (I'm not quite sure what gcc *should* do with this.)

decay func().arr to a pointer, successfully complete the translation,
and generate a sensible executable.

-- Gaby
 
C

Chris Torek

... an ICE is (almost) always a bug in the compiler.

You do not need the "(almost)" part, unless the machine on which
the compiler is being run is not working correctly (in which case
it becomes difficult to trust anything at all :) ).
decay func().arr to a pointer, successfully complete the translation,
and generate a sensible executable.

The problem here is that there is no really sensible answer.

According to others who should know, the expression "func().arr"
is *not* an lvalue in C89/C90, but *is* an lvalue in C99.

Since it is not an operand of the unary "&" or sizeof operators,
the array lvalue will become a pointer rvalue, so in C99 the
assignment appears to be legal but useless, and a warning would be
appropriate.

In C89/C90, however, this is the one unique case in which an array
is an "rvalue", and the whole thing appears to be an error with a
required diagnostic, or perhaps an error with no required diagnostic.
But writing:
int (*p)[N] = &(func().arr);
would definitely require a diagnostic, because unary & can only
be applied to lvalues.

The way I prefer to describe situations involving arrays uses
somewhat different terminology from that in the C standards. In
my notation, we have "objects" and "values", rather than "lvalues"
and "values". In other words, we eliminate the whole concept of
"lvalue" as basically flawed and confusing. (This escapes the
weirdness surrounding "modifiable" vs "non-modifiable" lvalues, in
particular.) But even here we have a problem. The value returned
by a function is definitely a "value", not an "object", yet in this
case the value is a structure.

Normally structures can only exist in "complete object" form. For
instance:

struct S a, b;
...
a = b;

Here "a" and "b" both name entire objects. While "b" is in a "value
context", so that b's value is copied to a, it is obvious "where
this value lives" -- in the object named by b -- and its lifetime
is equally obvious. If b contains an array member named "arr",
writing:

int *ptr = b.arr;

causes b to point to the first element of the array by the usual
application of what I call "The Rule" about arrays and pointers in
C. The lifetime of that array is the lifetime of b; its place in
storage is determined by b's place; and so on.

Function return values, however, have no defined storage location.
Normally this never even comes up: If functions could return
arrays, we would have to resolve the question of what it means to
write:

int g(void)[N];
int *x = g();

and the answer to that would clearly be the same as the answer to
what it means to write:

int *p = func().arr;

but since functions cannot return arrays -- the declaration of g()
above must draw a diagnostic -- the first situation never occurs.
Only in this one case, when a function returns a structure that
contains at least one array as a member, do we have to find an
answer.

Ultimately, I think there are two reasonable answers: forbid this
entirely -- make a claim that an array value, in this one case in
which it occurs, has no address and cannot be used in any way
(including to subscript it) -- or state that the value of an "array
value" is the value of the address of the first element of that
array, just as with an array object, so that the assignment to p
is valid and well-defined, and so that subscripting works in the
usual way. If the second answer is chosen (as I think it is in
C99), we must then define the lifetime of the object to which this
pointer points. I think that lifetime should be "until the next
sequence point", so that the assignment to p, while valid, is
useless and probably deserves a warning.

The folks defining C seem to have chosen both answers (at different
times in different standards). :)
 
L

lawrence.jones

In comp.std.c j said:
Would anyone mind quoting the relevant section in c90 for this by the way?
Or is there some free draft for c90 that I could take a look at? I would
just like to compare the differences between c90 and c99.

Except when it is the operand of the sizeof operator or the
unary & operator, or is a character string literal used to
initialize an array of character type, or is a wide string
literal used to initialize an array with element type compatible
with wchar_t, an lvalue that has type ``array of type'' is
converted to an expression that has type ``pointer to type''
that points to the initial element of the array object and is
not an lvalue.

The key difference between C90 and C99 is that "an lvalue that has type"
changed to "an expression that has type". There were also changes to
the "&" operator, but I don't think they're relevant in this context.

-Larry Jones

Ha! Wild zontars couldn't drag that information out of me! Do your worst!
-- Calvin
 
L

lawrence.jones

In comp.std.c j said:
Well, & operates on an lvalue. And since coord is considered to be a
non-lvalue -- in this context -- in c90, then you can't use the address-of
operator on it.

Interestingly enough, the same is true in C99. Although the implicit
conversion works, the & operator still requires an lvalue.

-Larry Jones

From now on, I'm devoting myself to the cultivation of
interpersonal relationships. -- Calvin
 
L

lawrence.jones

In comp.std.c CBFalconer said:
On reflection, true. There is no guarantee that the return value
from example() has any sort of address, in fact the reverse is
probably true. On this reasoning C99 should not allow this
either.

The theory is that the return value has to be stored somewhere and thus
has a conceptual address, even if it's not in the usual address space,
and the resulting pointer has such a short lifetime that the compiler
can get the right answer without too much work, even if it has to be
special-cased.

-Larry Jones

Years from now when I'm successful and happy, ...and he's in
prison... I hope I'm not too mature to gloat. -- Calvin
 
K

Keith Thompson

Gabriel Dos Reis said:
| [...]
| > If with -std=c99, you still see the same behaviour, then that is a bug
| > in the GCC/gcc compiler.
|
| I got an internal compiler error in gcc with the following program:

Odd.
Anyway, an ICE is (almost) always a bug in the compiler.

[...]

| int main(void)
| {
| int *ptr = func().arr;
| return 0;
| }
|
| I've reported it to the gcc bugzilla system; see
| <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12446>.

Thanks!

| (I'm not quite sure what gcc *should* do with this.)

decay func().arr to a pointer, successfully complete the translation,
and generate a sensible executable.

Which appears to be what it does if you specify "-std=c99"; at least
it doesn't cause an ICE. (When I submitted the bug report, I had
thought for some reason that I had seen the same behavior with and
without "-std=c99"; fortunately my description was correct anyway.)

I still wonder about the semantics of this. I've just tried another
test program that assigns a value to *ptr and then prints it out.
This doesn't, of course, imply that doing so is valid; it could still
be undefined behavior.

The question is, what is the lifetime of the int object that ptr
points to? It's part of the value returned by a function, so I
normally wouldn't expect it to outlive the expression containing the
call, but being able to grab a pointer to it makes it all very
confusing.
 
T

thp

[...]
+ The way I prefer to describe situations involving arrays uses
+ somewhat different terminology from that in the C standards. In
+ my notation, we have "objects" and "values", rather than "lvalues"
+ and "values".

Agreed. C89/90 attempted to define an "lvalue" to be an object-valued
expression but ran afoul of the fact that to maintain sanity we must
accept "*0" as an lvalue. We might slip out of that embarrassment by
positing a "null object" that *0 designates, but what's its type?
Obviously, the matter gets even more difficult when there are
reference types.

Things remain sane if we accept that there are objects and there are
values, and that some expressions (called rvalues) will always
designate a value, while others (called lvalues) will not represent
values but will not necessarily designate objects eithers.
Unfortunately, the distinction doesn't rest on type, since per the C
Standard, both "3" and "int x" are of the same type, namely int.

Tom Payne
 
C

CBFalconer

The theory is that the return value has to be stored somewhere and
thus has a conceptual address, even if it's not in the usual address
space, and the resulting pointer has such a short lifetime that the
compiler can get the right answer without too much work, even if it
has to be special-cased.

Since C, unlike Pascal, allows a return value to be ignored, such
needs to be (in practice) easily done. The easiest method is
returning it in a register, and failing to store that register.
Registers do not have an address in the usual memory space. Thus
I see no reason for allowing taking the address of a returned
value, or a component thereof.

If C99 allows this I consider it an unnecessary burden on code
generators.

(read in c.l.c)
 
G

Gabriel Dos Reis

[...]

| I still wonder about the semantics of this. I've just tried another
| test program that assigns a value to *ptr and then prints it out.
| This doesn't, of course, imply that doing so is valid; it could still
| be undefined behavior.

If it does something like

print_value(set_value(func().arr, 4));

I would expect it to be valid -- I do not know any Standard text that
implies so, though.
In C++, which as the same notion of non-lvalue array, that would be
valid.

| The question is, what is the lifetime of the int object that ptr
| points to?

What I've found quite intriguing in the Standard is I'm under the
impression that the notion of lifetime of temporaries has been
completely hand waved.

| It's part of the value returned by a function, so I
| normally wouldn't expect it to outlive the expression containing the
| call, but being able to grab a pointer to it makes it all very
| confusing.

Yup.

In C++, the issue has been resolved by stating that the lifetime of a
temporary lasts until the end of the full expression it is part of.

-- Gaby
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,218
Latest member
GracieDebo

Latest Threads

Top