Reputation: 971
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
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