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

7
  • 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? Commented Apr 1, 2014 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. Commented Apr 1, 2014 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 Commented Apr 1, 2014 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). Commented Apr 1, 2014 at 12:55
  • 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. Commented Apr 1, 2014 at 15:38

3 Answers 3

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 :)

Sign up to request clarification or add additional context in comments.

4 Comments

This answer is not correct wrt to the p please see my comment above and Richard's comment.
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.
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.
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.
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...

1 Comment

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.
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.