Decreasing order of address within main

K

karthikbalaguru

Hi,
I was playing around with the address of operator and i
found some pattern in it.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;
printf("address of diff - %u \naddress of j - %u\naddress of i - %u
\n",&diff,&j,&i);
printf("sizeof diff - %d \nsize of j - %d\nsize of i - %d \n",sizeof
(diff),sizeof(j),sizeof(i));
}

Output of First run
-----------------------------
address of diff - 1375424
address of j - 1375436
address of i - 1375448
sizeof diff - 4
size of j - 4
size of i - 4

Output of Second run
------------------------------
address of diff - 1637408
address of j - 1637420
address of i - 1637432
sizeof diff - 4
size of j - 4
size of i - 4

Were able to notice it ?
In both of the above outputs, the Address of 'diff' has the lowest
address to start with and next is 'j' and finally is the 'i'. The
difference between all these three variable address is 12.

Interesting to know the reason for the difference of 12 between
these variables .
Also, how does the variable 'diff' always gets the lowest address ?
Does it mean that the first element within a function will always
get the highest possible address during that particular time and
the subsequent variables will have their addresses less than that ?

Does the standard talk anything on these lines ?

Any ideas ?

Thx in advans,
Karthik Balaguru
 
N

Nobody

I was playing around with the address of operator and i
found some pattern in it.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;
printf("address of diff - %u \naddress of j - %u\naddress of i - %u
\n",&diff,&j,&i);
printf("sizeof diff - %d \nsize of j - %d\nsize of i - %d \n",sizeof
(diff),sizeof(j),sizeof(i));
}
In both of the above outputs, the Address of 'diff' has the lowest
address to start with and next is 'j' and finally is the 'i'. The
difference between all these three variable address is 12.

Interesting to know the reason for the difference of 12 between
these variables .

On my system, the difference is 4. I suspect that your compiler has
some form of buffer-overrun checking enabled, causing it to insert
a "canary" between each variable.
Also, how does the variable 'diff' always gets the lowest address ?
Does it mean that the first element within a function will always
get the highest possible address during that particular time and
the subsequent variables will have their addresses less than that ?

Does the standard talk anything on these lines ?

The standard says absolutely nothing about how the compiler lays out the
stack, or even whether it uses a stack. All of this is an implementation
detail.

If you enable optimisation, the compiler will often store local variables
in registers, or even completely eliminate many local variables
(obviously, it can't do either of these if you take their address).
 
J

jameskuyper

karthikbalaguru said:
Hi,
I was playing around with the address of operator and i
found some pattern in it.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;

"When two pointers are subtracted, both shall point to elements of the
same array object, or one past the last element of the array object;
the result is the difference of the subscripts of the two array
elements." (6.5.6p9)

For purposes of that section of code, 'i' can treated as a one-
dimensional array of int. If 'j' happens to allocated a position
immediately after the possition allocated for 'i', then a pointer to
'j' could also be a pointer one past the end of that array. However,
the standard guarantees nothing about where 'j' and 'i' are stored
relative to each other. Therefore, in general the expression you use
to initialize 'diff' may violate a "shall" which appears outside of a
"Constraints" section - the behavior is undefined.
printf("address of diff - %u \naddress of j - %u\naddress of i - %u
\n",&diff,&j,&i);
printf("sizeof diff - %d \nsize of j - %d\nsize of i - %d \n",sizeof
(diff),sizeof(j),sizeof(i));
} ....
Does the standard talk anything on these lines ?

No, not at all, except to say that the behavior of &i-&j may have
undefined behavior. You shouldn't write code that depends upon any of
the patterns you've noticed, unless it's acceptable for that code to
be highly non-portable.
 
S

Seebs

I was playing around with the address of operator and i
found some pattern in it.
Okay.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;

This is undefined behavior. There is no guarantee that it will produce
meaningful results.
Interesting to know the reason for the difference of 12 between
these variables .

That happens to be where the compiler put them this time. Or maybe
it just wanted to print 12 -- there's no guarantee that there is a
meaningful result from subtracting a pointer into one object from a
pointer into another object.
Also, how does the variable 'diff' always gets the lowest address ?

That happened to be what happened on this machine.
Does it mean that the first element within a function will always
get the highest possible address during that particular time and
the subsequent variables will have their addresses less than that ?
No.

Does the standard talk anything on these lines ?

No.

Except to say that it is undefined behavior to even try to answer the
question.
Any ideas ?

From the standpoint of the C standard, i, j, and diff are three separate
objects which may or may not be in even the same *kind* of physical storage.
It is not necessarily the case that comparing or subtracting those
pointers would be reasonable; there could be systems on which this program
would dump core or crash when it tried to calculate "diff".

The short answer is: Except in extremely unusual circumstances, you can
never need to know, and if you think you know you're almost certainly wrong.
In the absence of your explicit address-taking, it's quite possible that
the variables wouldn't even *have* addresses in the resulting code -- they
might never make it to actual memory storage. There's no reason they should
have to.

-s
 
K

Keith Thompson

karthikbalaguru said:
I was playing around with the address of operator and i
found some pattern in it.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;
printf("address of diff - %u \naddress of j - %u\naddress of i - %u
\n",&diff,&j,&i);
printf("sizeof diff - %d \nsize of j - %d\nsize of i - %d \n",sizeof
(diff),sizeof(j),sizeof(i));
}
[...]

Others have covered the core problems with your program; I'll handle
the tedious nitpicking. :cool:}

The result of subtracting one pointer value from another is of type
ptrdiff_t, not int. Since ptrdiff_t is an integer type, this:

int diff;
diff = &j - &i;

is valid (apart from the fact that the behavior of the subtraction
itself is undefined), but it would probably be better to do this:

ptrdiff_t diff;
diff = &j - &i; /* still UB, but no conversion is needed */

Your printf formats are all wrong. The correct format for printing an
address value (pointer value) is "%p", and it requires an argument of
type void*. (You can probably get away with char*, but it's easier to
use void* consistently.)

In your second printf, you're using "%d" to print values of type
size_t. This is likely to work in practice if int and size_t happen
to be the same size on your implementation, and if the values don't
exceed INT_MAX, but it's definitely not the best way to do it.

Here's a modified version of your program. I've also split the
printfs into multiple calls, one per output line. Using a single
format string for multiple output lines isn't incorrect, but very long
format strings can be split by Usenet software.

#include <stdio.h>
#include <stddef.h>
int main(void)
{
int i = 10;
int j = 20;
ptrdiff_t diff = &j - &i;

printf("address of diff - %p\n", (void*)&diff);
printf("address of j - %p\n", (void*)&j);
printf("address of i - %p\n", (void*)&i);

printf("sizeof diff - %lu\n", (unsigned long)sizeof diff);
printf("sizeof j - %lu\n", (unsigned long)sizeof j);
printf("sizeof i - %lu\n", (unsigned long)sizeof i);

return 0;
}

Note that the behavior of the subtraction is still undefined.
 
K

karthikbalaguru

"When two pointers are subtracted, both shall point to elements of the
same array object, or one past the last element of the array object;
the result is the difference of the subscripts of the two array
elements." (6.5.6p9)

I checked with this with the below code.

check_1
---------
#include<stdio.h>
int main(void)
{
int arr[]={1,2,3,4,5};
int i,*iptr;
iptr=&arr[4]-4;
for(i=0;i<=4;i++)
{
printf("%d \n",*iptr);
iptr++;
}
}
output
------
1
2
3
4
5

check_2
--------
#include<stdio.h>
int main(void)
{
int arr[]={1,2,3,4,5};
int i,*iptr;
iptr=&arr[4]-&arr[0];
for(i=0;i<=4;i++)
{
printf("%d \n",*iptr);
iptr++;
}
}
output
-------
crash


From check_2, i think even if i try subtraction
between the addresses of the elements within the same array,
it is giving undefined behaviour.
But, from check_1, it confirms that subtraction of a constant
number from the address is defined . But not between two
addresses of the same array.

Correct me if the above is wrong.
For purposes of that section of code, 'i' can treated as a one-
dimensional array of int. If 'j' happens to allocated a position
immediately after the possition allocated for 'i', then a pointer to
'j' could also be a pointer one past the end of that array. However,
the standard guarantees nothing about  where 'j' and 'i' are stored
relative to each other. Therefore, in general the expression you use
to initialize 'diff' may violate a "shall" which appears outside of a
"Constraints" section - the behavior is undefined.




No, not at all, except to say that the behavior of &i-&j may have
undefined behavior. You shouldn't write code that depends upon any of
the patterns you've noticed, unless it's acceptable for that code to
be highly non-portable.

Thx in advans,
Karthik Balaguru
 
J

jacob navia

karthikbalaguru a écrit :
Hi,
I was playing around with the address of operator and i
found some pattern in it.

#include<stdio.h>
int main(void)
{
int i=10,j=20;
int diff;
diff = &j - &i;
printf("address of diff - %u \naddress of j - %u\naddress of i - %u
\n",&diff,&j,&i);
printf("sizeof diff - %d \nsize of j - %d\nsize of i - %d \n",sizeof
(diff),sizeof(j),sizeof(i));
}

Output of First run
-----------------------------
address of diff - 1375424
address of j - 1375436
address of i - 1375448
sizeof diff - 4
size of j - 4
size of i - 4

Output of Second run
------------------------------
address of diff - 1637408
address of j - 1637420
address of i - 1637432
sizeof diff - 4
size of j - 4
size of i - 4

Were able to notice it ?
In both of the above outputs, the Address of 'diff' has the lowest
address to start with and next is 'j' and finally is the 'i'. The
difference between all these three variable address is 12.

Interesting to know the reason for the difference of 12 between
these variables .

This is very easy to explain. The compiler is assigning in decreasing
order 3 integers of size 4.
Also, how does the variable 'diff' always gets the lowest address ?

Probably because the compiler assigns them one after the other in their
declaration order. It sees first "i", then "j", then "diff", so it
assigns an address to i, then to j, then to diff. In the lcc-win
compiler each local variable is assigned an address as soon as it
is seen. Other compilers are more sophisticated and try to put
variables that are disjoint in the same address, or try to
align them to fit machine requirements. How the specific address
is settled can be a complicated process.
Does it mean that the first element within a function will always
get the highest possible address during that particular time and
the subsequent variables will have their addresses less than that ?

Maybe. This depends on the compiler and the compilation options.
For instance, if optimization is requested all 3 variables could end
in some register and not be assigned any address at all.

Or some of them would be in registers and others in memory, all
combinations are possible.
Does the standard talk anything on these lines ?

Not really. Most compilers do something similar to what you see,
but some of them (gcc for instance) have much more complicated schemas
specially when they see a floating point variable etc.
 
K

Keith Thompson

karthikbalaguru said:
check_2
--------
#include<stdio.h>
int main(void)
{
int arr[]={1,2,3,4,5};
int i,*iptr;
iptr=&arr[4]-&arr[0];
for(i=0;i<=4;i++)
{
printf("%d \n",*iptr);
iptr++;
}
}
output
-------
crash


From check_2, i think even if i try subtraction
between the addresses of the elements within the same array,
it is giving undefined behaviour.
But, from check_1, it confirms that subtraction of a constant
number from the address is defined . But not between two
addresses of the same array.

Correct me if the above is wrong.

Ok, it's wrong.

My guess is that you're using gcc, and that you decided to ignore the
"warning: assignment makes pointer from integer without a cast"
message.

Don't ignore warning messages. This one is particularly serious; if
it were up to me, it would be a fatal error, not just a warning.

The result of subtracting two pointer values is a signed integer
(specifically a ptrdiff_t, defined in <stddef.h>). You assign the
result of such a subtraction to a pointer object, iptr. The result is
garbage, and anything that happens after that (or before it, or during
it) is essentially meaningless.

What's probably happening is that iptr is being assigned the value 4,
converted from ptrdiff_t to int*, resulting in a pointer to address
0x00000004. You then try to dereference that pointer, which you're
not allowed to do.

But in fact anything can happen, including the compiler rejecting your
program.

IMHO gcc is not doing you any favors by allowing the code to compile.
The solution is to pay more attention to warnings.
 
J

jameskuyper

karthikbalaguru said:
"When two pointers are subtracted, both shall point to elements of the
same array object, or one past the last element of the array object;
the result is the difference of the subscripts of the two array
elements." (6.5.6p9)

I checked with this with the below code.

check_1
---------
#include<stdio.h>
int main(void)
{
int arr[]={1,2,3,4,5};
int i,*iptr;
iptr=&arr[4]-4;

This is subtraction of an integer from a pointer, something quite
different from subtraction of two pointers. More about that later.
for(i=0;i<=4;i++)
{
printf("%d \n",*iptr);
iptr++;
}
}
output
------
1
2
3
4
5

check_2
--------
#include<stdio.h>
int main(void)
{
int arr[]={1,2,3,4,5};
int i,*iptr;
iptr=&arr[4]-&arr[0];

The difference between two pointers has the type ptrdiff_t, which is
an integer type. You've declared iptr to be a pointer to an int.
Therefore, the assignment statement is a constraint violation
(6.5.16.1p1). Any conforming compiler must give you a diagnostic. Did
yours? Did you ignore it? You shouldn't have.

You could eliminate that diagnostic by putting in a cast to convert
the int to a pointer. However, "Except as previously specified, the
result is implementation-defined, might not be correctly aligned,
might not point to an
entity of the referenced type, and might be a trap representation." If
any of those "might be"s come true, the behavior of your program would
still be undefined, particularly if you try to dereference that
pointer.

for(i=0;i<=4;i++)
{
printf("%d \n",*iptr);

Having set iptr to point at some completely arbitrary location, and
not necessarily even a valid one, you then try to use iptr to retrieve
the value of the int object stored in that location. What did you
expect to happen, and why?

Here you take a pointer to an unknown location, not necessarily a
valid one, and then you try to increment it. Still more undefined
behavior (as if any more was needed).
}
}
output
-------
crash


From check_2, i think even if i try subtraction
between the addresses of the elements within the same array,
it is giving undefined behaviour.

No, the subtraction is one of the few things that is correct in that
program.
But, from check_1, it confirms that subtraction of a constant
number from the address is defined.

No, it does not. Test runs are inherently incapable of proving that
the behavior is defined. Undefined behavior includes, as one
possibility, that the code does precisely what you expect it to do,
even though the standard gives you no justified cause to expect it to
do that thing. The only way to be sure whether the behavior is
undefined is to read and understand the code, and compare it with what
the standard says.

In fact, in this case, the subtraction does have defined behavior,
because subtraction of an integer value from a pointer yields a
pointer; if the original pointer points at the forth element of
whatever array it points at, or any higher element, the result of that
subtracting 4 from that pointer is well defined. The result is a
pointer that does, in fact, point at the location you expect.

... But not between two
addresses of the same array.

If you had stored the result in a ptrdiff_t object, and used an
appropriate format specifier to print the value of that object, there
would have been no problem.
 
K

Kaz Kylheku

Does the standard talk anything on these lines ?

The standard says that the addresses of objects declared in a block, when
considered in their lexical order, are always decreasing.

Don't listen to all these other people. Believe me; I'm the one who is right!

And remember, next time someone asks you, just tell them you read it on Usenet.

Whatever you do, don't actually read the standard yourself.

Second-hand information is best.
 
S

Seebs

I checked with this with the below code.

No you didn't.
But, from check_1, it confirms that subtraction of a constant
number from the address is defined.

No, it doesn't.
Correct me if the above is wrong.

You can never confirm that something is "defined" by running code.

See, "defined" doesn't mean "it happened to work that way once". It
means "we are guaranteed that this will always work, or at least that
failure to do so is clearly a bug in the compiler."

Imagine that you live in apartment #323.

You could make the claim: "All apartment numbers are defined to be
palindromes." You write a little program to check it, you plug in 323,
it confirms: The number is a palindrome.

But you haven't actually checked what you'd *need* to check, which is
*every possible number*.

To make the claim that subtraction is "defined", you must not only
run your program on every compiler, for every kind of computer, with every
set of options. You must also do it on every compiler that will exist
in the future, for machines that haven't even been designed yet.

.... Or you could just use the language *definition* to tell you what is or
is not *defined*. Note the relationship there; "defined" is a function of
the language specification, not of any specific real-world implementation.

The reason this matters is that you may someday want to use a different
compiler, and you may encounter a system where the subtraction crashes in
all cases, for instance.

-s
 
R

Richard Tobin

Kaz Kylheku said:
And remember, next time someone asks you, just tell them you read it
on Usenet. Whatever you do, don't actually read the standard
yourself. Second-hand information is best.

So there we have it: anything that isn't specified in the C standard
is off-topic here, and you shouldn't ask about what's in the standard
eiher.

Your question was in fact a perfectly reasonable one, and the answer
(as others have said) is that the standard doesn't specify the order
of addresses, and doesn't even let you subtract addresses unless
they point into the same object, though in practice it will work
on any system where processes have a flat address space.

-- Richard
 
K

Keith Thompson

Kaz Kylheku said:
The standard says that the addresses of objects declared in a block, when
considered in their lexical order, are always decreasing.

Don't listen to all these other people. Believe me; I'm the one who
is right!

And remember, next time someone asks you, just tell them you read it
on Usenet.

Whatever you do, don't actually read the standard yourself.

Second-hand information is best.

Was there any particular reason you felt the need to give a sarcastic
answer to a reasonable question?
 
K

Keith Thompson

Kenneth Brody said:
Well, to be fair, not everyone has access to the "final" version of
the Standard. From what I understand, you need to pay for that,
though "near final" draft versions are available for free. Also, not
everyone understands the "legalese" of the text.
[...]

The C99 standard itself costs money (something like $30 US for a PDF
copy). But
<http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf>
is free; it includes the full C99 standard with all the changes
specified in the three Technical Corrigenda folded in. For most
purposes, I consider it better than the C99 standard itself. (For
some purposes, C99 plus copies of the three Technical Corrigenda might
be better, since n1256 is marginally less official.)
 
J

jameskuyper

Kenneth Brody wrote:
....
Well, to be fair, not everyone has access to the "final" version of the
Standard. From what I understand, you need to pay for that, though "near
final" draft versions are available for free.

More to the point, n1256.pdf is available for free. It's an unofficial
committee draft which contains the final version of the standard, plus
all of the modifications to the final version that were called for by
the three technical corrigenda that have been approved. For my
purposes, that makes n1256.pdf more useful than the final version
would be.
Also, not everyone
understands the "legalese" of the text.

That, on the hand, it a very serious issue, and not easily dealt with.
 
L

luserXtrog

Was there any particular reason you felt the need to give a sarcastic
answer to a reasonable question?

Indeed, sarcasm must always have a rigorous targeted purpose.
One must agonize of over the construction of the rationalizing
paraphernalia. A twisted presentation depends upon clear facts.
 
N

Nick Keighley

[ie. the relationship between the addresses of variables]

The standard says that the addresses of objects declared in a block, when
considered in their lexical order, are always decreasing.

Don't listen to all these other people. Believe me; I'm the one who is right!

And remember, next time someone asks you, just tell them you read it on Usenet.

Whatever you do, don't actually read the standard yourself.

Second-hand information is best.

for the benefit of the OP, Kaz is pulling your leg (is not serious)
 
P

Phil Carmody

So there we have it: anything that isn't specified in the C standard
is off-topic here, and you shouldn't ask about what's in the standard
eiher.

Not sure how that follows from the quoted paragraph.
Your question was in fact a perfectly reasonable one, and the answer
(as others have said) is that the standard doesn't specify the order
of addresses, and doesn't even let you subtract addresses unless
they point into the same object, though in practice it will work
on any system where processes have a flat address space.

Any sufficiently aggressive optimiser won't even bother setting
any variable set to such an undefined value. Nor will it set any
future values dependent on that variable.

I'm not sure if there are any sufficiently aggressive optimisers
out there, but not prepared to sloppily write UB in order to find
out.

Phil
 
J

James Kuyper

Mark said:
Then what do people pay for if they can get the standard for free?

I can think of three possible reasons.

The most common reason is probably just because they're unaware of the
existence or nature of n1256.pdf.

They might pay for the official standard as a way of contributing to the
standardization effort.

The C99 standard and the three TCs have all been carefully reviewed and
officially approved. n1256.pdf has not gone through that same process,
so it might be somewhat less reliable that it would have been if it had.

At the top of every page of n1256 except the first, it says "Septermber
7, 2007", and as of 2009-09-21, according to Lawrence Jones, the only
defects that had been reported between those two dates have been:

1) The typo in "Septermber 7, 2007".

2) "the predefined macro __STDC_MB_MIGHT_NEQ_WC__ should appear in
6.10.8p2 (optional predefined macros) rather than p1 (required
predefined macros)."
 

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,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top