1

I have the following code:

   unsigned char bytes[12] = { 1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12};
   char *i;
   struct i_and_c {int x;char c;} *p; //What's the meaning of *p? Is it a pointer 
                                           //to the struct "i_and_c"???
   i = (char *) (&(bytes[4]));
   p = (struct i_and_c *) (&(bytes[5]));

I have to write the value of the following expressions:

  1. i[2]
  2. p->c

The solution is:

  1. 7
  2. 10

But I just don't know how do we get this values. Can anyone help me please? Thanks =D

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
GniruT
  • 731
  • 1
  • 6
  • 14
  • 6
    It will be better for you if you do this yourself. What will you gain from us doing it for you? Do you want to learn or not? – David Heffernan Apr 01 '14 at 12:48
  • There are two approaches to understanding source. The first is to use a debugger and step through the source lines looking at the values of variables. The other is to attempt to do so using a piece of paper to draw diagrams showing the changes in the variables. It is a lot better to use a debugger because people do not think like computers as well as computers do. – Richard Chambers Apr 01 '14 at 12:52
  • To clarify: `struct i_and_c {int x;char c;}` is the declaration (and definition) of a type as `char` or `int`, so yes `*p` is a pointer of type struct i_and_c – aveuiller Apr 01 '14 at 12:53
  • Your first comment is correct, `*p` is a pointer to to `struct i_and_c` but keep in mind that no actual memory has been allocated for the structure yet (at that point in the code). – Bas Jansen Apr 01 '14 at 12:55
  • There is a dependency on the struct being laid out with 4 byte int immediately followed by 1 byte character with no padding. I would not depend on that for real code. – Richard Chambers Apr 01 '14 at 12:59
  • Note that `(struct i_and_c *) (&(bytes[5]));` violates [strict aliasing rules](http://stackoverflow.com/questions/20922609/why-does-optimisation-kill-this-function/20956250#20956250) and therefore the code has undefined behavior so the behavior is not predictable for that portion of the code. – Shafik Yaghmour Apr 01 '14 at 13:19
  • 1
    Richard Chambers and Shafik Yaghmour are correct; this is a terrible question because the actual behaviour of the program depends entirely on the whim of the compiler writer. Reasoning solely from the C specification it is impossible to know what this program does; it could do anything. The only way to know what this program does is either (1) read and understand the compiler documentation which describes how the compiler writers decided to handle this situation, or (2) try it and find out. – Eric Lippert Apr 01 '14 at 15:38

3 Answers3

2

The values you get obviously come from the bytes array, but why do you get these values?

unsigned char bytes[12] = { 1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12};

Here bytes can be viewed as an unsigned char* pointing to the first element of the array, i.e. 1. Now let's break this pointer arithmetic down, shall we?
First of all let's change the type of i from char* to unsigned char*, because that's what it really is, or should be.

   unsigned char *i;
   i = (unsigned char *) (&(bytes[4]));

The brackets mean access the n-th element, which is the 5th element, because indices start at 0. This means we can rewrite them to a simple addition and dereference.

   unsigned char *i;
   i = (unsigned char *) (&(*(bytes+4)));

Wait, do we really get the address of the value we obtained from dereferencing the pointer? Yes, we do. We apply both the operation and its inverse. What does that mean? We can simply leave both parts out!

   unsigned char *i;
   i = (unsigned char *) (bytes+4);

So now i is a pointer to the 5th element of bytes, because we started with a pointer to the first element and add 4 to it. Now i[2] is the same as *(i+2). If i is a pointer to the 5th element, i+2 is a pointer to the 7th element, subsequently i[2] is the 7th element of bytes, which happens to be 7.

Now that we got that out of the way, let's head on to the next problem, it's slightly trickier, though. Let's change the code so it's clearer what it does.

   struct i_and_c {int x;char c;};
   struct i_and_c *p;
   p = (struct i_and_c *) (&(bytes[5]));

*p is indeed a pointer to a struct of type i_and_c. Let's simplify this the same way as we did above.

   struct i_and_c {int x;char c;};
   struct i_and_c *p;
   p = (struct i_and_c *) (bytes+5);

Alright, so now we have a pointer to a struct i_and_c that starts at the 6th element of the bytes array. WARNING: This is undefined behavior, really, on most machines it will behave like described in the following section, though.
The struct is made up of an int followed by a char. I assumesizeof(int) = 4 and that there is no padding between the int and the char, which both needs to be assumed for your given result to be correct.
This means that the int in the struct will reach from the 6th element of bytes to the 9th element (including it). And the char will come after that, which means it's the 10th element. Now p->c is the same as (*p).c which means you want to get that char. The 10th element of bytes is 10, so that's what you get as a result.

I hope my explanations helped :)

Cu3PO42
  • 1,403
  • 1
  • 11
  • 19
  • This answer is not correct wrt to the `p` please see my [comment above](http://stackoverflow.com/questions/22786360/what-does-this-code-in-c#comment34744106_22786360) and [Richard's comment](http://stackoverflow.com/questions/22786360/what-does-this-code-in-c#comment34743238_22786360). – Shafik Yaghmour Apr 01 '14 at 15:44
  • You're right, I tried to make it as simply as possible, though. So I left that out, I added a note on it, though, for the sake of being correct. – Cu3PO42 Apr 01 '14 at 16:39
  • You should explain or at least add a link about strict aliasing if you don't feel you can explain it properly. Also you should mention that the compiler is free to add padding between members of a struct. Since this is undefined behavior we can not make reliable predictions about the results, a compiler is even free to optimize away UB and there are cases were real bugs were caused by such behavior. – Shafik Yaghmour Apr 01 '14 at 16:43
  • I'm pretty positive I can explain it, but I didn't think the OP would profit from that, so I didn't. It seems to me he is just getting into pointers, so that seems a bit high-level. I agree the 'better way' might be to mention it. Right now I'm in the middle of a meeting though, so I won't be able to fix my answer in like the next 2 hours. Feel free to submit an edit to my post. – Cu3PO42 Apr 01 '14 at 16:48
0

here bytes is a char array, so each element of the array is of 1 byte.

the structure is defined like this,

struct i_and_c{
    int x;
    char c;
} *p;

so yes, *p is a pointer to the defined structure.

Now when you are passing the address of 5th element of the bytes array, e.i, 5 to i, i is now an array of character having the elements from 5 to 12. so, i[2] is the third element of the i array, that is, 7.

Now you are storing address of 6th element of the bytes array into p. p contains an int (4 byte) and a char (1 byte). i.e, first 4 bytes are stored into x and the next one in c. So, p->c stores 10.

Understood??

Hope it helped...

deb_rider
  • 570
  • 2
  • 12
  • Re: *so yes, `*p` is a pointer to the defined structure*, no, the value of `p` is a pointer to the defined structure. `*p` is an lvalue of the structure type, not a pointer to the structure type. – Eric Lippert Apr 01 '14 at 15:35
0
struct i_and_c {int x; char c;} *p;

The above statement defines a new type struct i_and_c and also defines a variable p which is a pointer to an object of type struct i_and_c.

i = (char *) (&(bytes[4]));

The above gets the address of the 5th element (since index of the array starts from zero) of the array bytes and casts it to char * type. i[2] evaluates to *(i + 2) which means the 7th element of the byte array. Therefore, i[2] evaluates to 7. Next, the below statement

p = (struct i_and_c *) (&(bytes[5])); // violates strict aliasing rule

casts the address of the 6th element of the bytes array to type struct i_and_c * type and assigns it to p. p->c is a syntactic sugar for (*p).c. Therefore, it accesses the value at address which is offset sizeof(int) from the address pointed to by p. sizeof(int) on your machine is 4. This means (*p).c accesses the (6 + 4) = 10th element (i.e., element at index 9) of the bytes array which is 10.

Note that the above statement violates the strict aliasing rule and hence invokes undefined behaviour.

Community
  • 1
  • 1
ajay
  • 9,402
  • 8
  • 44
  • 71