How the struct and pointer acts - c

While going through a quiz in SoloLearn app, I came across a certain code.
#include <stdio.h>
struct node{
int a, b, c;
};
int main()
{
struct node num = {3, 5, 6};
struct node *ptr = &num;
printf("%d\n", *ptr);
printf("%d\n", *((int*)ptr + 1 + (3-2)));
return 0;
}
I got the answer right as the result printing 3 and 6, but I am not sure it I understood the struct statement and pointer function properly.
The following steps are how I understood how the code works.
Due to struct node, num 3, 5, 6 are set into a, b, c in the num variable.
*ptr points the first address of num due to struct node *ptr = &num; which means that it points to the address of the index = 0 which is 3 in {3, 5, 6}.
Therefore, printf("%d\n", *ptr); prints 3,
printf("%d\n", *((int*)ptr + 1 + (3-2))); is printing *(0+1+1) which is index = 2 of num which equals to 6.
Is this right?

Yes, but allow me to adjust your terminology. In particular, using the phrase "index" seems a little off to me, since we're not talking about an array. I will also break up your point#4 to be more explicit about what happens there.
Due to the initializer, 3, 5, 6 are set into a, b, c in the num variable.
ptr points to the start address of num due to struct node *ptr = &num;, which is the same as the address of num.a.
Therefore, printf("%d\n", *ptr); prints 3.
(int*)ptr yields a pointer to num.a with the proper type.
Adding 2 to ptr means that we add the size of two ints to the address. Note that the typecast is very important, as we would've otherwise added the size of two struct nodes to the address; the effect of adding to a pointer depends on the pointer type.
The resulting address is the same as the address of num.c. At that address, we find the integer value of 6.

Related

Pointers problems with C

I don't understand the difference in the t and p pointers. The t pointer gives the same output when printing t and *t only when using **t I get the value.
What's the difference between them?
The code is:
int main()
{
int B [2][3] = {
{2, 3, 6},
{4, 5, 8}
};
int *p = B;
int (*t)[3] = B;
printf ("%d\n", p);
printf ("%d\n", t);
printf ("%d\n", *p);
printf ("%d\n", *t);
printf ("%d\n", **t);
return 0;
}
Output is:
6422000
6422000
2
6422000
2
Comments have addressed the importance of using the correct format specifiers, but here are a couple of other points to consider:
point 1:
The declaration: int *p = B; should generate a compile time warning. This is because int *p is a simple pointer to int, as such it should only be set to point to the address of (&) a single int. But B does not represent an int.
For illustration, it is instructive to see the variations of warnings for the following 3 incorrect ways of initializing p with B. Each starts off with the phrase:
_"warning: incompatible pointer types initializing `int *` with an..."_:
int *p = B;//...expression of type 'int [2][3]'
int *p = &B;//...expression of type 'int (*)[2][3]'
int *p = &B[0]//...expression of type 'int (*)[3]'
Note the incompatible pointer types at the end of each warning. Each of them is specific, providing the programmer hints how to address the potential problem.
The following initializes *p with the address of a single int and generates no warning
int *p = &B[0][0];//assigning address of p to the location of a single integer value.
point 2:
The following declaration/assignment:
int (*t)[3] = B;
creates t as a pointer to an array of 3 int, and points t to the first instance (row) of 3 int in B where B is defined as:
int B [2][3] = {{2, 3, 6}, {4, 5, 8}};
Because t is defined in this way it is flexible in the way it can be used in that it is pointable to any array of 3 int i.e. it does not matter how many rows B has, or to which row t is pointed. Example, given the following arrays:
int B[5][3] = {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}, {13,14,15}};
int A[3] = {0}; //A simple array of 3 int all set to 0
The following declarations can be made:
int (*t)[3] = NULL; //points to nothing
t = B; //points to location of 1st element of 1st row in B
t = &B[1]; //points to location of 1st element of 2nd row in B
t = &B[4]; //points to location of 1st element of 5th row in B
t = &A; //points to location of 1st element of A
Writing int *p = B; isn't a good idea but anyway, it puts the address of the very first element of B, 2 into p. So, p outputs the address(6422000) and *p outputs 2. All good till here.
What is t? It's a pointer to an array, B. What will happen when you print it, you'll get the address to B, which is always also the address of it's very first element, which happens to be 6422000. So, what will happen when you dereference t? You'll get and array, B in this case, which will then decay into a pointer and give you the memory address. And the memory address of B is 6422000. And **t will dereference the dereferenced array. The deference array is B, which will decay into the pointer, 6422000 in this case and that will be dereferenced again, giving 2.
Basically:
p: Address of the very first element of B, 2.
*p: Dereferenced p, in this case 2.
t: Address to B. Address of an array is also the address of it's very first element, equivalent to p.
*t: Dereferences into B, B will decay into it's very first element's pointer, equivalent to p.
**t: Dereferenced *t, which is 2.
Note: I know, the first element of B is {2, 3, 6}, not 2. I refer to 2 as "the very first element". That's inaccurate but for the purpose of explanation, I was forced to use the terminology.

pointers cant read the correct elements in array

I have a pointers in C, and I can't figure out how it works.
Below is the code:
// ptr points to an array of 2 ints
int (*ptr)[2];
int torf[2][2] = {12, 14, 16};
ptr = torf;
int (*ptr_2)[2];
int torf_2[2][2] = { {12}, {14, 16}};
ptr_2 = torf_2;
printf("%d %d\n", **ptr, **(ptr + 2));
printf("%d %d\n", **ptr_2, **(ptr_2 + 2));
The answer I want should be:
12 16
12 14
But actually I got on my PC:
12 6422000
12 12
Any ideas?
try this:
// ptr points to an array of 2 ints
int(*ptr)[2];
int torf[2][2] = { 12, 14, 16 };
ptr = torf;
int(*ptr_2)[2];
int torf_2[2][2] = { {12}, {14, 16} };
ptr_2 = torf_2;
printf("%d %d\n", **ptr, **(ptr + 1));
printf("%d %d\n", **ptr_2, **(ptr_2+1));
when you use pointers it is like the indexes of the array it begin from 0;
good work
If you add these lines to the end of your program:
printf("%p: %p %p\n", ptr, ptr+1, ptr+2);
printf("%p: %p %p\n", *ptr, *ptr+1, *ptr+2);
printf("%p: %p %p\n", **ptr, **ptr+1, **ptr+2);
You will note that in the first case, the numbers increase by 8, which could either be the size of a pointer or two ints. Same goes for the second, but the third increases by the size of an int; so that is good.
So, to disambiguate 2 ints or 1 address, lets do a s/2/3/g.
Now, we see the first case the increment is now 12 ( = 3 * 4 ).
Your second case ( *ptr + i ) increments by 4, so is the address of successive ints
Your third case is the integer values themselves.
Where did it get confusing? Quick checklist:
When you are trying to workout pointer / indexing problems, use unique values as much as possible.
Pay attention when the compiler warns you. Eventually you will know to ignore "format '%p' expects argument ... ", but it takes time to build the confidence.
There is a handy program, cdecl, which converts C type expressions into something english like.
In virtually all C implementations, int x[2][2], y[4]; have the same layout; that is, C multi-dimensional arrays are just an overlay of a single dimension array, with the arithmetic cleverly hidden. For this reason, definitions like int (*p)[2]; are rare and rarely useful.
If you must go down this road, it is likely better to do something like:
typedef int pair[2];
pair torf[2] = { { 0, 1 }, { 2, 3 }};
pair *ptr = torf;
if nothing else, somebody has a chance to understand it...

What is the difference between int* p and int (*p)[] in the context of arrays.?

Using DevCpp with TDM GCC 4.9.2 on Windows 8. But I don't think the platform matters for this question.
I know that we can use a pointer to point to a single data or an array of data.
I have learned about pointer to arrays but never used it. What advantage does one have over the other?
Sample Code...
#include <stdio.h>
int main()
{
int x[2]={10,20};
int *p1= NULL; //simple pointer
int (*p2)[] = NULL; //pointer to an array, specifically
p1 = x;
p2 = &x; //removing the & gives me a warning of "assignment from incompatible pointer types".
printf("x[1] = %d\n", x[1]);
*(p1+1) = 7;
printf("x[1] = %d\n", x[1]);
(*p2)[1] = 55;
printf("x[1] = %d", x[1]);
return 0;
}
Does p1 or p2 have an advantage over the other?
They are completely different.
int *p; - is the pointer to the int
int (*p)[1]; is a pointer to the array (in this case one element only)
In your trivial example the pointer arithmetic will be the same and generated code will be the same. But they still have different types and you may get warnings when compiled.
The "advantages" you will see when your example will be less trivial:
int (*p)[100];
p++; the pointer will point to the next 100 elements int array.
Pointer to an array means a pointer which accepts address of an array.
let's say array is int arr[5],in which size of int is 4 byte.
p is a pointer to an array that accept the address of an int array.
int arr[5];
int (*p)[5];
p=&arr;//address of an array block
let's say the base address is 1000 .So after increment in p it will lead us to 1020 address,because the size of the array block is 20 bytes.
p points to 1000 address
p++;
//Now p points to 1020 not 1004.
Whereas in case of int *q, q will point to 1004 as usual.

Accessing array of pointer to structure inside a structure

I am trying to assign char array value in a structure and retrieving the data but facing some problem while retrieving.
struct first {
int a;
int b;
};
struct second {
int c;
int d;
struct first * f_t[2];
};
int main()
{
struct second *p;
char a[24] = {1, 0, 0, 0,
2, 0, 0, 0,
3, 0, 0, 0,
4, 0, 0, 0,
5, 0, 0, 0,
6, 0, 0, 0};
p = (struct second *)a;
p->f_t[0] = (struct first *) (a + 8);
p->f_t[1] = (struct first *) (a + 16);
printf("%d %d %d %d %d %d",
p->c, p->d, p->f_t[0]->a, p->f_t[0]->b, p->f_t[1]->a, p->f_t[1]->b);
}
Output:
1 2 1245008 1245016 1245024 6
First two values are coming properly (1 and 2) but the rest seems like some garbage values.
Please help to identify some wrong initialization or some other method for the same.
First of all, never assume the padding in a struct. It works fairly reliably, but is technically undefined behavior.
And much more importantly, you are breaking the strict aliasing rule. This is an assumption by the compiler that you never access the same memory location with pointers of different types (in this case, struct first* and char*). You can get around this with the -fno-strict-aliasing or use unions to do type-punning, as it's usually called. Go here for more info.
The more correct version of this code will be -
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
struct first {
int a;
int b;
};
struct second {
int c;
int d;
struct first * f_t[2];
};
int main()
{
struct second *p;
char a[24] = {1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0};
p = alloca(sizeof(struct second));
memset(p, 0, sizeof(struct second));
memcpy(p, a, sizeof(int)*2);
p->f_t[0]= alloca(sizeof(struct first));
p->f_t[1]= alloca(sizeof(struct first));
memset(p->f_t[1], 0, sizeof(struct first));
memset(p->f_t[0], 0, sizeof(struct first));
memcpy(p->f_t[0], a + 8, sizeof(struct first));
memcpy(p->f_t[1], a + 16, sizeof(struct first));
printf("%d %d %d %d %d %d",p->c,p->d,p->f_t[0]->a,p->f_t[0]->b,p->f_t[1]->a,p->f_t[1]->b);
}
NOTE: Using alloca() is bad practice. Don't use it unless you know what you're doing. It's fine here since it's simply one small struct being allocated on the stack, but in general steer away from it and use malloc() or just use local variables.
This still technically falls under undefined behavior, since we're assuming how the structs are padded, which is implementation specific, but most platforms use self aligned data types and are a lot faster when using properly aligned structures, so it's easy to guess. For more information on this, go here. I still strongly discourage this as this is undefined behavior, and it's very easy to mess up.
Also, the next time onward, make sure to provide a minimal, complete and compilable example.
Well, you have a lot of (potential) problems with strict aliasing rule, possible padding, size of variables, alignment.
But let's assume that your compiler is set to no-strict-aliasing and that all sizes, padding and alignment match.
Then look at these lines:
p->f_t[0]=(Struct first *) (a+8);
p->f_t[1]=(Struct first *) (a+16);
What they will do is to overwrite a[8] and forward with the value of the pointer (aka address). Consequently a is no longer equal to the initialized value.
Try this to see for your self:
// Print all element in a
p->f_t[0]=(Struct first *) (a+8);
// Print all element in a
p->f_t[1]=(Struct first *) (a+16);
// Print all element in a
So your problem is that you try to use a[8] to a[23] for storing
a) two pointers
and
b) two struct first
Of cause that's impossible
Other comments and posts have already addressed the strict aliasing problems that accompany casting one type to another, and have detailed other issues as well.
It is appears from your existing code that you are attempting to reference each member of an array of struct usingpointer variables.
This answer is limited to an illustration of how to do what I think you are trying to do in simple terms: (read in-line comments for explantions)
typedef struct { //using struct instead of Struct
int a;
int b;
}FIRST; //create typedef tag (improved readability throughout code)
typedef struct {
int c;
int d;
FIRST f_t[2]; // changed from *[] to []
}SECOND; // (the rest of your post indicates that is what you meant)
int main()
{
SECOND *p1; //for illustration, create two pointers to reference 2 elements of arr[2]
SECOND *p2;
//Create array of SECOND, populate with unique values in each location
//to provide verification during printf that assignments were made correcly.
SECOND arr[2] = {{1,2,{3,4,5,6}},{7,8,{9,10,11,12}}};
//note values are arranged to follow the definitions of 2 elements of SECOND arr[2].
//Create 2 pointers, 1 for each element of SECOND arr[2]
p1 = &arr[0]; //assigns address of 1st element of SECOND arr[0], to pointer p1
p2 = &arr[1];//assigns address of 1st element of SECOND arr[1], to pointer p2
printf("Elements of p1[0]:\n%d, %d, %d, %d, %d, %d\n", p1[0].c, p1[0].d, p1[0].f_t[0].a,p1[0].f_t[0].b,p1[0].f_t[1].a,p1[0].f_t[1].b );
printf("Elements of p2[0]:\n%d, %d, %d, %d, %d, %d\n", p2[0].c, p2[0].d, p2[0].f_t[0].a,p2[0].f_t[0].b,p2[0].f_t[1].a,p2[0].f_t[1].b );
getchar();//stop execution to view result (needed in my environment)
}

How these type (int (*ptr)[3]) = a; (where a is => int a[][3] = {1,2,3,4,5,6}) of statements work in C? Increment "ptr" gives unexpected result

For the following code:
#include <stdio.h>
int main()
{
int a[][3] = {1, 2, 3, 4, 5, 6};
int (*ptr)[3] = a;
printf("%d %d ", (*ptr)[1], (*ptr)[2]);
++ptr;
printf("%d %d\n", (*ptr)[1], (*ptr)[2]);
return 0;
}
what should it print? I am expecting:
2 3 3 4
but the output is:
2 3 5 6
I am expecting the output as 2 3 3 4
because initially the ptr is pointing to the first row of double dimensional array a[][3]. Therefore (*ptr)[1] of first printf will give us 2, like wise (*ptr)[2] will give us 3. But after the ++ptr line it will start pointing to the second element of the first row of a[][3]. Therefore (*ptr)[1] of second line should now give us 3 and likewise (*ptr)[2] should give us 4.
ptr is of type int (*)[3]:
int (*ptr)[3]
The type of ptr specifies how the pointer airthmetic is going to be performed on ptr.
For the following more general case:
int *p;
// ...
int (*ptr)[N] = p;
ptr += M; // same address as p[M * N]
Incrementing the pointer in M results in an address increment of M * N * sizeof(int) and not just M * sizeof(int) as you may have expected.
This is how pointer arithmetic works, because the type of the elements ptr points to is int [N] and not int.
After the statement ++ptr; is executed, ptr points to the third element of a. The increment is done in steps of three elements of type int instead of a single one, that is, in your case, N = 3.
Let's go through this line by line.
int a[][3] = {1, 2, 3, 4, 5, 6};
a has the type "array of array of 3 int". This line is equivalent to
int a[][3] = {{1, 2, 3}, {4, 5, 6}}; /* alternate */
It is clearer with this alternate line that a is an array of arrays. This will help with understanding the next lines.
int (*ptr)[3] = a;
ptr has the type "pointer to array of 3 int". Because of array-to-pointer decay, ptr will point to the first array in a (int (*ptr)[3] = &a[0]).
printf("%d %d ", (*ptr)[1], (*ptr)[2]);
Dereferencing ptr gives a[0], so this will print
2 3
++ptr;
This seems to be where you are confused. Incrementing a pointer to an element makes the pointer point to where the next element would lie if they were in an array. In this case, the current element pointed to and the next element after that are in an array (the element being an array of 3 ints, not a single int). So incrementing the pointer makes it point to the next array of 3 ints (which happens to be a[1]).
printf("%d %d\n", (*ptr)[1], (*ptr)[2]);
Now that we know that ptr points to a[1], we can see that (*ptr)[1] is equivalent to a[1][1], which is 5; and that (*ptr)[2] is equivalent to a[1][2], which is 6. So now the total output is:
2 3 5 6
(*ptr)[3] is a pointer to an array of three elements. Below is an analysis of your program.
#include<stdio.h>
int main()
{
int a[][3] = {1, 2, 3, 4, 5, 6};
int (*ptr)[3]=a;
/* First note ptr is a pointer to an array of three integers
* If you had written it like
* int (*ptr)[3];
* ptr=&a[0];
* ,it would have been more obvious.
* But here, you have the freedom to use it interchangeably
*/
printf("%d %d ", (*ptr)[1], (*ptr)[2]);
/* Remember ptr is &a[0], so *ptr is dereferencing &a[0] to gets its value
* However, since ptr is a pointer to an array, its value itself is an array
* So you need to give the index like (*ptr)[1] & (*ptr)[2] to get the second and third values
* (Mind the count starts with zero)
* In essence you are doing,
* printf("%d %d ", *((*ptr)+1*4*8),*((*ptr)+2*4*8)); // 4bytes=4*8 bits
* Here '*ptr' will be substituted with a[0],the starting address an 12 byte block ( 3 integers * 4 bytes per integer)
*/
++ptr;
/* Above step can be written as
* ptr=ptr+1;
* This is pointer arithmetic, so '1' above should be considered as 1 block
* Or in layman's terms move pointer to the next 12byte block.
* In other words advance ptr by (12*8) bits so that it now points to &a[1]
*/
printf("%d %d\n", (*ptr)[1], (*ptr)[2]);
/* Follow the same steps for the first print with ptr is &a[1]
*/
return 0;
}

Resources