knick
knick

Reputation: 971

Accessing struct field gives address rather than value

I have some embedded c code which uses void pointers and pointer arithmetic to implement a general-purpose linked list. I am using the list to store structures of type stopwatch_t. The problem I am having is that when I pull items out of the list and cast them from void pointers to stopwatch_t pointers, they are not behaving as expected. When I try to access the fields of the struct I am getting addresses rather than values. See the last comment in the code below for more details.

Complete code for the linked list;

h file

typedef uint16_t list_index_t;

typedef struct
{
    void *              p_values;
    uint16_t *      p_links;
    uint16_t            el_size;
    uint16_t            list_size;
    list_index_t    first;
    list_index_t    last;
    bool                    empty;
} list_t;

typedef struct 
{   
    list_t * p_list;
    bool before_first;  
    list_index_t current;
} list_enumerator_t;


#define LIST_INIT(P_LIST, EL_SIZE, LIST_SIZE)                                                      \
        do                                                                                                                                                                                       \
        {                                                                                                                                                                                            \
                static uint8_t values[LIST_SIZE*EL_SIZE];                                                                                                  \
              static uint16_t links[LIST_SIZE];                                                                                                              \
              static list_t list;                                                                               \
                uint32_t err = list_init(&list, values, links, EL_SIZE, LIST_SIZE);                                      \
                APP_ERROR_CHECK(err);                                                                                                                                          \
                (*P_LIST) = &list;                                                                         \
        } while (0);    

c file

#include <string.h>
#include "compiler_abstraction.h"
#include "list.h"

static __INLINE list_index_t get_successor(list_t * p_list, list_index_t current);
static __INLINE void set_successor(list_t * p_list, list_index_t index, list_index_t successor_index);
static __INLINE list_index_t get_predecessor(list_t * p_list, list_index_t target);
static __INLINE void * get_value(list_t * p_list, list_index_t index);

uint32_t list_init( list_t *    p_list, 
                                        void * p_values, 
                                        uint16_t * p_links, 
                                        uint16_t    el_size, 
                                        uint16_t    list_size)
{
    p_list->p_values = p_values;
    p_list->p_links = p_links;
    p_list->el_size = el_size;
    p_list->list_size = list_size;
    p_list->first = 0;
    p_list->last = 0;
    p_list->empty = true;

    // link all nodes
    for (uint16_t i = 0; i < (list_size - 1); i++)
    {
        p_links[i] = i+1;
    }
    p_links[list_size-1] = 0;

    return NRF_SUCCESS;
}

bool list_empty(list_t * p_list)
{
    return (p_list->empty);
}

bool list_full(list_t * p_list)
{
    list_index_t first = p_list->first;
    list_index_t last = p_list->last;
    list_index_t after_last = get_successor(p_list, last);
    return (first == after_last);
}

// Precondition: !list_full(p_list)
list_index_t list_insert(list_t * p_list, void * p_value)
{
    // determine where new value will go
    list_index_t new_index = 
        p_list->empty ? 
            p_list->last : 
            get_successor(p_list, p_list->last);

    // copy value into list
    void * p_new_value = get_value(p_list, new_index);
    memcpy(p_new_value, p_value, p_list->el_size);

    // update links
    p_list->last = new_index;

    p_list->empty = false;

    return new_index;
}

void * list_lookup(list_t * p_list, list_index_t index)
{
    return get_value(p_list, index);
}

// Precondition: !list_empty(p_list)
void list_delete(list_t * p_list, list_index_t del)
{
    // update empty status
    // note: the only valid way to get an empty list is to delete an
    //       item from a list with only one item.
    p_list->empty = p_list->first == p_list->last;

    // delete items from beginning of list
    if (p_list->first == del)
    {
        p_list->first = get_successor(p_list, del); 
    }

    // delete item from end of list
    else if (p_list->last == del)
    {
        p_list->last = get_predecessor(p_list, del);
    }

    // delete item from middle of list
    else
    {
        // remove node from chain
        list_index_t before_del = get_predecessor(p_list, del);
        list_index_t after_del = get_successor(p_list, del);
        set_successor(p_list, before_del, after_del);

        // insert node back into chain after last
        list_index_t after_last = get_successor(p_list, p_list->last);
        set_successor(p_list, del, after_last);
        set_successor(p_list, p_list->last, del);       
    }
}

static __INLINE list_index_t get_predecessor(list_t * p_list, list_index_t target)
{
    // start at first populated index
    list_index_t x = p_list->first;
    list_index_t y = get_successor(p_list, x);
    while (y != target)
    {
        x = y;
        y = get_successor(p_list, y);
    }
    return x;
}

static __INLINE list_index_t get_successor(list_t * p_list, list_index_t current)
{
    return (p_list->p_links)[current];
}

static __INLINE void set_successor(list_t * p_list, list_index_t index, list_index_t successor_index)
{
    (p_list->p_links)[index] = successor_index;
}

static __INLINE void * get_value(list_t * p_list, list_index_t index)
{
    return (void *)
        ((uint32_t)(p_list->p_values) + (index * p_list->el_size));
}

list_enumerator_t list_enumerate(list_t * p_list)
{
    list_enumerator_t enumerator;
    enumerator.before_first = true;
    enumerator.p_list = p_list;
    return enumerator;
}

bool enumerator_move_next(list_enumerator_t * p_enumerator)
{
    bool result;
    list_t * p_list = p_enumerator->p_list;

    if (p_enumerator->before_first)
    {
        p_enumerator->current = p_list->first;
        p_enumerator->before_first = false;
        result = !list_empty(p_list);
    }
    else if (p_enumerator->current == p_list->last)
    {
        result = false;
    }
    else
    {
        p_enumerator->current = get_successor(p_list, p_enumerator->current);
        result = true;
    }
    return result;
}

void * enumerator_current(list_enumerator_t * p_enumerator)
{
    list_t * p_list = p_enumerator->p_list;
    list_index_t index = p_enumerator->current;
    return get_value(p_list, index);
}

Relevant parts of the stopwatch;

static list_t * p_slots;

typedef struct
{
    uint16_t start;
    uint32_t rollovers;
} stopwatch_t;

uint32_t stopwatch_init(void)
{
    stopwatch_t stopwatch;
    LIST_INIT(&p_slots, sizeof stopwatch, MAX_STOPWATCHES);
    ...
    return OK;
}

stopwatch_id_t stopwatch_start(void)
{
    stopwatch_t stopwatch;
    stopwatch.start = timer1_now();
    stopwatch.rollovers = 0;
    ...
    stopwatch_id_t id = list_insert(p_slots, (void *)(&stopwatch));
    ...
    return id;
}

uint64_t stopwatch_get_elapsed_time(stopwatch_id_t id)
{
    ...
    stopwatch_t * p_stopwatch = list_lookup(p_slots, (list_index_t)id);
    uint16_t start_ticks = (p_stopwatch->start);
    uint32_t rollovers = (p_stopwatch->rollovers);
    /* PROBLEM: variables 'start_ticks' & 'rollovers' now contain the addresses */
    /*          of the values I want, rather than the values themselves.        */
    ...
}

This is the first time I've used void pointers. Where did I go wrong?

Upvotes: 0

Views: 80

Answers (1)

knick
knick

Reputation: 971

The problem was nothing to do with the use of pointers or the pointer arithmetic. As it turns out, there was an error in return expression of function stopwatch_get_elapsed_time(..). This error meant that the variables start_ticks and rollovers were not being used. Presumably the compiler has realised that the variables aren't being used and optimized them away. This explains why the variables were showing strange values when watched in the debugger.

Upvotes: 1

Related Questions