views:

324

answers:

10
+3  Q: 

C Pointer Question

In my embedded c program I have a struct:

struct var{
    unsigned long value;
    unsigned long length;

    + More
}

An array of these structs is used to hold variables. Most of the variables stored are simply stored in 'value' and so the length is set to 1.
However, some of these variables are arrays and Im trying to store the start address in 'value'.

unsigned long lookup[10];
variables[x].length = 10;

Then I'm not quite sure how to store the address...

variables[x].value = lookup;
// lookup is a pointer so I cant put it into value

OR

variables[x].value = (unsigned long)lookup;
// value reads back through sprintf+uart as '536874692'
// which couldnt be a valid memory address!

I might just give up and add a pointer variable in the structure

EDIT:
I wanted to avoid adding the pointer to the struct becasue I would have to go back and rewrite the flash read/write functions to also save the pointer. These are pretty complicated and currently work so I'd rather not touch them!

+5  A: 

You could store the address in value by casting it to an unsigned long, as you demonstrate. (I think the value you're getting is a valid address... in hex, it comes out as 0x20000EC4... looks like your embedded system's data segment starts at 0x20000000, huh?)

However, casting pointers to ints (or longs) is never "clean". Why not add an

unsigned long *starting_address;

to your struct? Then you can avoid the typecasts. If you're worried that this will require more memory, you can use a union that stores either an unsigned long *starting_address or an 'unsigned long value`, which is still cleaner than casting.

Martin B
looks like 0x20000EC4 is valid, Thanks.
Tim
A: 

I don't see the downside of putting a pointer in your structure. Do you want the memory to be in a continuous block? You could just do

struct var
{
   unsigned long *value;
   unsigned long length;

   unsigned long othervalue1;
   unsigned long othervalue2;
};

But don't forget to allocate memory for value separately by using malloc. Or if its a fixed amount of space you want use

unsigned long value[50];
DHamrick
I wanted to avoid adding the pointer to the struct becasue I would have to go back and rewrite my flash read/write functions to also save the pointer. Only value is stored in the flash and all the other struct members (not shown) are populated manually.
Tim
A: 

What happens if you sprintf+uart lookup. What value does that give you?

Unless there was casting or sign extension involved, the actual data is the same. It is just interpreted differently by sprintf.

Your solution to just use a pointer is good. If you really wanted to use an int, you should use intptr_t, which is guaranteed to be big enough to fit a pointer.

Mike Mu
+6  A: 

A cleaner option would be to use union.

 struct var{
     union {
        unsigned long numeric;
        void *ptr;  
     } value;
     unsigned long length;

     + More
 }

You could optionally also include a type enum, as often done with union-using pieces.

EFraim
+1  A: 

Assuming that the system pointer size is the same as that of an unsigned long (no gaurantee, check it with sizeof) you would use the cast. But this is messy and error prone. Consider using a union instead.

struct var{
    unsigned short type;
    union {
        unsigned long i;
        void *p;
    } value;
    unsigned long length;

    + More
}

where you store the type in type.

dmckee
A: 

Why not make the value a one-lenght array, when the length is 1? You wont be using more space anyway.

palindrom
A: 

I'm not sure why you're not defining value as an unsigned long*.

Instead, do something like:

struct var{
  union{
    unsigned long solo_value;
    unsigned long* multi_values;
  }

  unsigned long length;
  + More
};

Also, alignment rules aren't enforced as strongly* on the stack as they are in the heap. Though, if you're using any recent compiler they should be anyway... you're probably smashing something with sprintf. Pull out the value using a debugger or something more reliable.

*Technically malloc() et. al. enforce the 8-byte alignment rules, not the language.

Kevin Montrose
A: 

Your best choice is to use an enum, as already stated:

typedef struct var{
     union {
        unsigned long numeric;
        void *ptr;  
     } value;
     unsigned long length;

     // + More
 } composition_t;

then use the lenght member to discriminate the type of data:

composition_t array[2];
unsigned long lookup[10];

// Just a plain unsigned long value
array[0].value.numeric = 100;
array[0].length= 1;

// An array of 10 long values
array[0].value.ptr= lookup;
array[x].length = 10;
PFM
A: 

Bear in mind that an unsigned long is not guaranteed to hold a pointer, although it's likely to in embedded systems. Do the standard thing and use a union, as other answers suggest. A union will Do The Right Thing in any standard-conforming implementation, not just what you're using right now. Further, it has the advantage of saying what you mean, which is always valuable when somebody who doesn't immediately grok the code (maybe you, a year later) has to deal with it.

David Thornley
A: 

Are you aware of offsetof? http://en.wikipedia.org/wiki/Offsetof

I'm not sure what you're really trying to do, but storing the address of one structure member in the same structure seems unnecessary.

There's also a container_of macro (google it) which is built using offsetof and that might also be useful.

smcameron