Thread: traverse struct using pointer calculus

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    72

    traverse struct using pointer calculus

    Hi All

    The original idea was posted struct-property name is in char* 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!

    Any comments ?

    thnx a lot
    LuCa

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jeanluca View Post
    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).
    Last edited by MK27; 06-02-2009 at 09:44 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    May 2009
    Posts
    72
    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. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jeanluca View Post
    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.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    May 2009
    Posts
    72
    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. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jeanluca View Post
    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.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User
    Join Date
    May 2009
    Posts
    72
    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. #8
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jeanluca View Post
    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...
    Last edited by MK27; 06-02-2009 at 12:48 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    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;
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by CornedBee View Post
    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 ??!!!?
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    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.
    Last edited by CornedBee; 06-02-2009 at 01:15 PM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Registered User
    Join Date
    May 2009
    Posts
    72
    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. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    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
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    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.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    Registered User
    Join Date
    May 2009
    Posts
    72
    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!!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Global Variables
    By Taka in forum C Programming
    Replies: 34
    Last Post: 11-02-2007, 03:25 AM
  2. Looking for a way to store listbox data
    By Welder in forum C Programming
    Replies: 20
    Last Post: 11-01-2007, 11:48 PM
  3. "dereferencing pointer to incomplete type"
    By incognito54 in forum C Programming
    Replies: 2
    Last Post: 11-01-2005, 09:50 AM
  4. Binary Search Trees Part III
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 10-02-2004, 03:00 PM
  5. Passing pointers between functions
    By heygirls_uk in forum C Programming
    Replies: 5
    Last Post: 01-09-2004, 06:58 PM