It's possible to make the code work (compile, run without crashing, and produce a coherent, explicable answer), but you need a different structure from the one chosen. For example:
#include <stdio.h>
struct my_structure
{
char *i;
};
#define EXPR(x) #x, x
int main(void)
{
char strings[][10] = { { "Winter" }, { "Bash" }, { "Is" }, { "Here" } };
struct my_structure variables[] = { { strings[0] }, { strings[1] }, { strings[2] }, { strings[3] } };
struct my_structure *p = variables;
printf("%10s: %s\n", EXPR(++p->i));
printf("%10s: %s\n", EXPR(p++->i));
printf("%10s: %d\n", EXPR(*p->i++));
printf("%10s: %d\n", EXPR(*p->i++));
printf("%10s: %d\n", EXPR((*p->i)++));
printf("%10s: %d\n", EXPR(*p++->i));
return 0;
}
Which generates the output:
++p->i: inter
p++->i: inter
*p->i++: 66
*p->i++: 97
(*p->i)++: 115
*p++->i: 116
The macro EXPR
simply allows me not to repeat the expressions in the code, and yet to get both the string form and the value into the call to printf()
.
When things start out, p->i
points to the string "Winter"
.
++p->i: inter
— pre-increments the pointer p->i
so it points to the i
of Winter
.
p++->i: inter
— post-increments the pointer p
(to point to "Bash"
), but the result is the same as before because the increment takes effect after p->i
is used.
*p->i++: 66
— post-increments the pointer p->i
(so it points to the a
in Bash
) and reports the value pointed at before the increment, which is B
(66 in ASCII).
*p->i++: 97
— same expression, but the pointer points at a
(97) before the increment and at the s
after the increment.
(*p->i)++: 115
— post-increments the letter that p->i
points at, reporting s
but changing it to t
.
*p++->i: 116
— post-increments p
so it points to the string "In", while reporting t
(116).
Here's an alternative with more instrumentation:
#include <stdio.h>
struct my_structure
{
char *i;
};
#define EXPR(x) #x, x
int main(void)
{
char strings[][10] = { { "Winter" }, { "Bash" }, { "Is" }, { "Here" } };
struct my_structure variables[] = { { strings[0] }, { strings[1] }, { strings[2] }, { strings[3] } };
struct my_structure *p = variables;
for (size_t i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
printf("strings[%zu] = [%s]\n", i, strings[i]);
for (size_t i = 0; i < sizeof(variables)/sizeof(variables[0]); i++)
printf("variables[%zu].i = [%s]\n", i, variables[i].i);
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %s\n", EXPR(++p->i));
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %s\n", EXPR(p++->i));
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %d\n", EXPR(*p->i++));
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %d\n", EXPR(*p->i++));
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %d\n", EXPR((*p->i)++));
printf("%10s: %s\n", EXPR(p->i));
printf("%10s: %d\n", EXPR(*p++->i));
printf("%10s: %s\n", EXPR(p->i));
for (size_t i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
printf("strings[%zu] = [%s]\n", i, strings[i]);
for (size_t i = 0; i < sizeof(variables)/sizeof(variables[0]); i++)
printf("variables[%zu].i = [%s]\n", i, variables[i].i);
return 0;
}
and its output:
strings[0] = [Winter]
strings[1] = [Bash]
strings[2] = [Is]
strings[3] = [Here]
variables[0].i = [Winter]
variables[1].i = [Bash]
variables[2].i = [Is]
variables[3].i = [Here]
p->i: Winter
++p->i: inter
p->i: inter
p++->i: inter
p->i: Bash
*p->i++: 66
p->i: ash
*p->i++: 97
p->i: sh
(*p->i)++: 115
p->i: th
*p++->i: 116
p->i: Is
strings[0] = [Winter]
strings[1] = [Bath]
strings[2] = [Is]
strings[3] = [Here]
variables[0].i = [inter]
variables[1].i = [th]
variables[2].i = [Is]
variables[3].i = [Here]
Play with variants of this scheme (extra parentheses, for example) to ensure you understand what's going on.