3

Here is what I was trying to do:-

struct Test
{
    int i;
    char s;
    float f;
};  
int main()
{
    int sizestruct=sizeof(struct Test);
    printf("size of struct=%d\n", sizestruct);
    int maxedge;
    printf("enter maxedge value");
    scanf("%d", &maxedge);
    struct Test *sptr1, *sptr2;
    sptr1=(struct Test *) 1000;
    sptr2=(struct Test *) maxedge;
    printf("sptr1=%d, sptr2=%d\n",sptr1, sptr2);
    printf("then sptr2-sptr1=%d\n", sptr2-sptr1);
    return 0;
}  

Now, the output I am getting is ok if maxedge=1996 (output is 83). Even for values 1997, 1998 and 1999, its giving the same value. But for values from 2000 to 2007, junk value comes. From values 2008 to 2011, output is 84. But from 2012 to 2019 again, output is junk.

Now consider this struct:

struct Test {
    int i;
    char s;
    float f;
    char s1;
};  

For the above struct, no matter what the value is for maxedge, the output is proper. I am unable to understand why junk value is coming for the above code!!

  • 5
    The code sample is slightly confusing. What are you really trying to accomplish? – zoul Aug 21 '14 at 09:22
  • 1
    What is it that you are trying to do? It is hard to guess from your code. The code, unsurprisingly, behaves exactly as the C standard mandates (in this case, undefined behaviour, so the value printed can be anything), but what did you expect it to do? – Thomas Padron-McCarthy Aug 21 '14 at 09:28
  • I am trying to get the difference of the two pointers, which should yield an integer value, depending on what is size of the structure. I hope I am clear. – ANURAG PRIYADARSHI Aug 21 '14 at 09:28
  • 1
    In that case, [this question](http://stackoverflow.com/questions/3238482/pointer-subtraction-confusion) will probably do? In any case, this only makes sense if you’re trying to poke into dark C corners for fun – in practice you should get as far away from pointer arithmetics as possible. – zoul Aug 21 '14 at 09:30
  • What does "junk" mean in this context? – glglgl Aug 21 '14 at 09:35
  • “Unexpected by those not well-versed in pointer arithmetics,” perhaps? – zoul Aug 21 '14 at 09:38

3 Answers3

6

I have tried your code. It doesn't print garbage, but it prints values not relatable to the others at the first glance. (83, -1431655682, 1431655849, 84)

I am not sure why it behaves in exactly this way, but in general, unexpected behavior can result (as it does in this case) from undefined behaviour.

If the difference of pointers, measured in terms of bytes, is not a multiple of 12 (the size of one struct), quite weirder things can happen, as we see.

Dividing by 12 obviously happens in a way which divides by 4 and then by 3, and the latter is performed in a way so that this here happens.

Let's have a look at the produced assembler output:

    movl    28(%esp), %edx
    movl    36(%esp), %eax
    subl    %eax, %edx
    movl    %edx, %eax

This was the subtraction.

    sarl    $2, %eax
    imull   $-1431655765, %eax, %eax
    movl    %eax, 4(%esp)

Here first we divide by 4 and then do a multiplication with the value -1431655765, which is a signed mis-representation of the unsigned value 2863311531.

If I multiply this value with a number dividable by 3 and only consider the lower 32 bits, I get the number divided by 3. This doesn't work with other numbers, but is probably faster than dividing.

So we see that if certain conditions are not met, such as dividability of the /4'ed number by 3 again, weird things happen. But that's ok, as not following these conditions is undefined behaviour.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 1
    I love this answer. This is one of the best examples I've ever seen of what "undefined behaviour" really can do to you. – Thomas Padron-McCarthy Aug 21 '14 at 10:05
  • very well said! well, I would like to add, from what I learnt from other sources, that if structure size happens to be 2, 4, 8, 16 and so on (2^n format, and we should exclude 2 as that can't be size of a structure), then the output comes proper (read "sensible") for any values of "maxedge". This happens because of two shifts (left or right I dont remember), thus, giving on 4 (2 shifts, at bit level, thus you get this magic number 4) sensible values of a given structure size. I hope I am clear with this. – ANURAG PRIYADARSHI Aug 25 '14 at 11:12
  • @ANURAGPRIYADARSHI Depends. In general, you never should use pointer arithmetics if the pointers' difference in bytes is not a multiple of the structure size. We just have seen why, and who knows what other architectures could make out of the data... – glglgl Aug 25 '14 at 11:53
  • @ANURAGPRIYADARSHI That means, if I have a structure size of 16, but an address difference of, say, 27 bytes, you are outside of the specs. In general, ony try to subtract pointers who point into the same array. – glglgl Aug 25 '14 at 11:54
  • @ANURAGPRIYADARSHI BTW, who says that 2 can't be the size of a structure? `struct a { short a; };` and `struct b { char b[2]; };` both have size 2 on my system. `struct a { char a; };` and `struct b { char b[1]; };` both even have 1 byte each! – glglgl Aug 25 '14 at 11:58
  • sorry!! I am new to structure padding. Thats why got confused. Thanks for mentioning! – ANURAG PRIYADARSHI Aug 25 '14 at 13:26
3

Pointers difference might surprise you in C. It will consider that you're asking for a index within an array, e.g. since array[index] is identical to *(array+index) it naturally turns that when p=&(array[index]) then p - array == index.

Does your result still look garbage ?

Well, maybe then you might consider than for offsets smaller than sizeof(struct Test), you're somewhere between index==i and index==i+1. I doubt this behaviour is specified anywhere, so you may not expect anything.

PypeBros
  • 2,607
  • 24
  • 37
1

It is undefined behaviour to subtract two pointers that are not both pointing to elements of the same array (or one-past-the-end of that array).

Typically the compiler assumes that your code follows the standard, and so it generates code something like subtracting the addresses and dividing by the size of the object. Works fine if your code compiles, may produce garbage if your code doesn't.

M.M
  • 138,810
  • 21
  • 208
  • 365