# Thread: traverse struct using pointer calculus

1. ## traverse struct using pointer calculus

Hi All

The original idea was posted http://cboard.cprogramming.com/c-pro...tml#post865527 by matsp

So, to summarize the problem: Suppose you have the following struct
Code:
```struct CONFIG {
char *my_val
char *other_val
}```
And you have a function that returns one of the two strings of this struct as follows:
Code:
```char* function get_config_property(char* prop_name) {
// prop_name contains "my_val" or "other_val"
// -> access propterty using 'prop_name' and return it
}```
Below is the code I wrote to accomplish this (using matsp idea):
Code:
```#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* getProperty(char *name ) ;
char* setProperty(char *name, char *value ) ;

struct CONFIG
{
char *abc ;
} *config ;

struct propentry
{
ptrdiff_t offset;
const char *name;
};

#define PE(a) { offsetof(struct CONFIG, a), #a }
struct propentry proptable[] =
{
PE(abc),
//PE(def)
};

int main(void) {
config = (struct CONFIG*)malloc(sizeof(config)) ;
setProperty("abc", "hello") ;
printf("MAIN TEST1, abc=%d\n", config->abc) ;
printf("MAIN TEST2, abc=%d\n", getProperty("abc")) ;
return 0;
}

char* setProperty(char *name, char *value )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{
if (strcmp(proptable[i].name, name) == 0)
{
char *ptr = (char *)(((char *)config) + proptable[i].offset);
ptr = value;
}
}
return NULL;
}

char* getProperty(char *name )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{
if (strcmp(proptable[i].name, name) == 0)
{
char *ptr = (char *)(((char *)config) + proptable[i].offset);
return ptr;
}
}
return NULL;
}```
This code compiles and executes, but doesn't produce the correct output. I have the feeling that I'm very close to the solution, but I probably have to do something with malloc, to initialize the fields of the struct, but where ?
Furthermore, I don't understand the difference between
Code:
`char *ptr = (char *)(((char *)config) + proptable[i].offset);`
and
Code:
`char *ptr = (char *)((config + proptable[i].offset);`
I would say the first one is incorrect, but when the struct only contains int properties, everything works fine!

thnx a lot
LuCa

2. Originally Posted by jeanluca
This code compiles and executes, but doesn't produce the correct output. I have the feeling that I'm very close to the solution, but I probably have to do something with malloc, to initialize the fields of the struct, but where ?
Yep. You actually have to do this *before* you send the pointer to setProperty UNLESS you want to use the return value of setProperty, which you are not doing (eg, so "config->abc=setProperty()"). This is because passing a pointer is actually passing an address; before it has been initialized, config->abc contains a nonsense address. After a malloc() call, a proper address is assigned. But if you pass the function the uninitialized address and call malloc, the value of *ptr is not changed in the calling function (main).

If you use the return value, then it will be assigned to the pointer in main. Either way is acceptable.

I would say the first one is incorrect, but when the struct only contains int properties, everything works fine!
Because you can perform arithmetic on the value of an int -- this may work, but it is not really "fine" since that's not what you intended (you want to do arithmetic on the address).

3. I tried (main):
Code:
```.....
config->abc = (char*)malloc(sizeof(char)*10) ;
setProperty("abc", "hello") ;
.....```
and I tried (inside setProperty):
Code:
```....
char *ptr = (char *)(((char *)config) + proptable[i].offset);
ptr = (char*)malloc(10 * sizeof(char)) ;
//ptr = value; --> doesn't feel right!
*ptr = *value;
....```
This last example would be preferred, but both modifications still give incorrect output!!!

4. Originally Posted by jeanluca
I tried (main):
Code:
```.....
config->abc = (char*)malloc(sizeof(char)*10) ;
setProperty("abc", "hello") ;```
There is no point in casting with malloc, and that last line is wrong, so:
Code:
```config->abc = malloc(sizeof(char)*10) ;
setProperty(config->abc,"hello");```
It is kind of hard to tell what you are trying to do in setProperty, since "ptr" is not returned or really used for anything. Be aware that this:
Code:
`ptr = value;`
will simply assign a fresh garbage address to ptr. If you want to set ptr at a certain place in name and use it to write into name:
Code:
```ptr = name+offset;
strcpy(ptr,value);```
ptr and name are both char*, so you do not have to do all that casting here either. "name" will be the address, to which you should be able to add an integer offset. I'm pretty sure that's what you're trying to do.

5. setProperty(config->abc,"hello");
This is cheating! I think I've not been clear enough.
The struct can have many propteries. Here I have only one to keep things simple!
So the setProptery function only knows 3 things:
• the name of the struct
• the name of the property (char *name)
• the value to assign to this proptery (char *value)

So, this can easily be solved as follows by replacing setProperty (ignoring getProptery) with:

Code:
```char* setProperty(char *name, char *value )
{
if ( strcmp("abc", name) == 0 ) {
config->abc = value
}
else if ( strcmp("def", name) == 0 ) {
config->def = value ; // as an example!
}
else if ( ... ) { ... }
.....
return NULL;
}```
But I don't prefer this way!
Actually, my struct will eventually contains chars*, ints, floats, etc

6. Originally Posted by jeanluca
This is cheating! I think I've not been clear enough.
*Sigh*. I think you are confused about some fundamental things. This is your prototype for setProperty:
Code:
`char* setProperty(char *name, char *value )`
So, setProperty accepts two char pointers as it's parameters. You *can* do this without error:
Code:
`setProperty("abc", "hello");`
However, this does not pass anything that has anything to do with struct CONFIG *config or any other previous variable; it passes a string literal, "abc" (not config->abc, which at this point would be an unused 10 char allocated memory block). While you *can* do that, it is still a mistake -- it will not lead to the result you were expecting. When I say you *must*
Code:
`setProperty(config->abc, "hello");`
I mean you should be passing the char pointer you just malloc'd:
Code:
`config->abc = malloc(sizeof(char)*10);`
That pointer is config->abc, not "abc".

Be aware that setProperty *does not* know
• the name of the struct

because you did not submit a struct. You submitted a char pointer that is a member of the struct. If you want to submit the struct, your prototype should be:
Code:
`char* setProperty(struct CONFIG *ptr, char *value )`
Now you can access ptr->abc inside setProperty, which means you can malloc() the char* there and set other properties of the struct, as you plan to do.

7. what if you read the property name and its value from file ? Then you cannot do setProperty(config->abc, value) ; but only setProperty(prop_name, value);

Here is a working example, but the struct only has int properties

Code:
```#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int getProperty(char *name ) ;
int setProperty(char *name, char *value ) ;

struct CONFIG
{
int abc ;
int def ;
} *config ;

struct propentry
{
ptrdiff_t offset;
const char *name;
};

#define PE(a) { offsetof(struct CONFIG, a), #a }
struct propentry proptable[] =
{
PE(abc),
PE(def)
};

int main(void) {
config = (struct CONFIG*)malloc(sizeof(config)) ;

setProperty("abc", "20") ;
printf("MAIN TEST1, abc=%d\n", config->abc) ; // test config->abc
setProperty("def", "30") ;
printf("MAIN TEST2, abc=%d\n", getProperty("abc")) ; // returns config->abc
printf("MAIN TEST2, def=%d\n", getProperty("def")) ; // returns config->def
return 0;
}

int setProperty(char *name, char *value )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{

if (strcmp(proptable[i].name, name) == 0)
{
int *ptr = (int *)(((char *)config) + proptable[i].offset);
*ptr = (int)strtol(value, NULL, 10) ;
}
}
return -1;
}

int getProperty(char *name )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{
if (strcmp(proptable[i].name, name) == 0)
{
int *ptr = (int *)(((char *)config) + proptable[i].offset);
//int *ptr = (int *)(config + proptable[i].offset);
return *ptr;
}
}
return -1;
}```

8. Originally Posted by jeanluca
what if you read the property name and its value from file ? Then you cannot do setProperty(config->abc, value) ; but only setProperty(prop_name, value);
I don't know where you got this, and I don't know what else to say to make you realize that this is JUST WRONG:
Code:
```   setProperty("def", "30") ;
printf("MAIN TEST2, abc=%d\n", getProperty("abc")) ; // returns config->abc```
"abc" IS NOT config->abc, and no matter what you believe right now, that second line IS NOT returning config->abc. It is returning the string literal "abc". No doubt you get compiler warnings doing this.

Eventually you will understand what I am trying to tell you. If you do not believe me now, there is no point to me trying to give you advice. Hopefully, someone else at cboard will show up and support my "claims" and then we can trust each other...

9. Whoa, heated.

The original code is right except for a level of indirection. The version with ints should have pointed it out: where you have "int" members, you have "int *ptr" in getProperty; in other words, one pointer more. In the original code you have "char*" members, but "char *ptr" in getProperty. There's a pointer level missing. Do this instead - also split up to make it clearer what happens.
Code:
```char* bytes = (char*)config;
bytes += proptable[i].offset;
char** prop = (char**)bytes;
return *prop;```

10. Originally Posted by CornedBee
The original code is right except for a level of indirection.
So you would say that this:
Code:
```    config = (struct CONFIG*)malloc(sizeof(config)) ;
setProperty("abc", "hello") ;```
means the same thing as
Code:
`setProperty(config->abc, "hello") ;`
and that the FIRST one is more correct ??!!!?

11. No, the second one simply crashes because config is a null pointer unless it's allocated. If it has been allocated, it's uninitialized, so you pass config->abc (a wild pointer) as the name argument, which is in turn passed to strcmp - you'll still crash. If you used calloc, you've got a null pointer instead of a wild pointer. You'll STILL crash.

The first one however is perfectly correct, const-correctness issues aside. offsetof is a poor man's member pointer (which don't exist in C). So the first one will set config->abc to point to the string literal "hello".

Have you actually looked at get/setProperty to see what it does?

Also, setProperty always returns a null pointer. It should just return void.

12. thnx a lot, I have the char* example working!!

In both the examples I allocate config
Code:
`config = (struct CONFIG*)malloc(sizeof(config)) ;`
so why is the second example crashing ?

Is there an alternative to offsetof ?

Furthermore, I have made my struct more complex (real situation) and tried the following

Code:
```#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void* getProperty(char *name ) ;
void setProperty(char *name, char *value ) ;

struct CONFIG
{
char *abc ;
int *def ;
} *config ;

struct propentry
{
ptrdiff_t offset;
const char *name;
};

#define PE(a) { offsetof(struct CONFIG, a), #a }
struct propentry proptable[] =
{
PE(abc),
PE(def)
};

int main(void) {
config = (struct CONFIG*)malloc(sizeof(config)) ;
//config->abc = malloc(10*sizeof(char)) ;
setProperty("abc", "hello") ;
setProperty("def", "20") ;
printf("MAIN TEST1, abc=%s\n", config->abc) ;
printf("MAIN TEST2, abc=%s\n", (char*)getProperty("abc")) ;
printf("MAIN TEST3, def=%d\n", config->def) ;
printf("MAIN TEST4, def=%d\n", (int*)getProperty("def")) ;
return 0;
}

void setProperty(char *name, char *value )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{

if (strcmp(proptable[i].name, name) == 0)
{
char* bytes = (char*)config;
bytes += proptable[i].offset;
void** prop = (void**)bytes;
*prop = (void*)value ;
}
}
}

void* getProperty(char *name )
{
int i;
for(i = 0; i < sizeof(proptable)/sizeof(proptable[0]); i++)
{
if (strcmp(proptable[i].name, name) == 0)
{
char* bytes = (char*)config;
bytes += proptable[i].offset;
void** prop = (void**)bytes;
return *prop ;```
I'm not sure if void* can be used like this, however, the output is almost correct. Any comments ?

cheers
LuCa

13. Right, can I first of all point out that this is probably the WRONG way to connect one string with another one - I suggested a solution to a specifically asked question, but I also suggested that basically, unless you could come up with it yourself, you should be trying to do this.

And I think you do not fully understand the pointers and what you are actually doing with the code that I (probably misguidedly) suggested.

--
Mats

14. Yup, your current attempt to expand this to heterogenous structs makes me agree with matsp.

It's far more complicated to build a universal solution from this, and the result will be incredibly fragile - in other words, not at all typesafe. For that you would have to track the types yourself, and the only way you can do this without the interface being completely unusable is by hiding it behind a scripting language.

15. Ok, its not the correct way of doing things. However I'm interested in whats going on and how far I can get with pointers, so lets consider it an exercise then! You're right about my problem with pointers, and as you can see all my questions here are about or have to do with pointers!!