tags:

views:

238

answers:

10

Is it possible to have pointers to data variables? I know I can have, say, pointers to strings e.g. char *str[n] and I can perform a 'for' loop over those pointers to retrieve the strings ... str[i] where i is the index counter.

If I have some data e.g.

char var1;
int  var2;
char var3;

and I wanted to get data from stdin I might use 3 separate calls to scanf()- just an example - to populate these variables.

Can I have 'an array of pointers to data' e.g. void *data[] where data[0] = char var1, data[1] = int var2 and data[2] = char var3, so that I could then use a single call to scanf() in a 'for' loop to populate these variables? (I'm assuming the type would have to be void to cater for the different types in the array)

A: 

Not directly, no. If you're able to use C++ in this situation, the closest you could do would be to wrap each variable in an object (either something like a variant_t or some templated, polymorphic solution). For instance, I believe you can do something like this:

class BaseType
{
public:
    virtual void DoScanf();
};

template<typename TYPE>
class SubType : public BaseType
{
public:
    SubType(const TYPE& data) : m_data(data) {}
    const TYPE& m_data;
    virtual void DoScanf()
    {
        // Your code here
    }

};

int num1;
char char1;
SubType<int> num1Wrapper(num1);
SubType<char> char1Wrapper(char1);
// You can then make a list/vector/array of BaseTypes and iterate over those.
Smashery
I'm actually using C but thanks.
A: 

The problem is that this void * array would be dealing with datatypes of different sizes. For this problem, you'd probably want to use a struct and maintain an array of those instead. Or you could just put your data in as a byte array, but then you'd have to know how to "chop it up" properly.

Dave Markle
Does the size really matter in this case? After all, I can varying length strings in an array of pointers to char.
+2  A: 
RAOF
Actually, what prompted my query (pardon the pun) is that I already have a typedef'd struct called Form which contains an array of pointers to Fields (which contain the prompts, row/col positions, filed lengths etc.) I was looking for something similar to capture the data for each field.
A: 

you could have an array of void*, so *array[0] = var1, etc.

Robert
A: 

To illustrate the problem..

int main(int argc, char* argv[])
{
  char var1 = 'a';
  int  var2 = 42;
  char var3 = 'b';

  void* stuff[3] = {0};
  stuff[0] = &var1;
  stuff[1] = &var2;
  stuff[2] = &var3;

  // Can't really use the array of void*'s in a loop because the
  // types aren't known...
  assert( var1 == (char)(*(char*)stuff[0]));
  assert( var2 == (int)(*(int*)stuff[1]));
  assert( var3 == (char)(*(char*)stuff[2]));

  return 0;
}
Skrymsli
Looks kinda hairy!
A: 

Looks like you are trying to implement a template (C++) equivalent in C. :D exactly that is what i am trying to do, for one of my project. I think mine case is less confusing as I am using only one datatype (some project specific structure), mine array would not be intermixing the datatypes.
Hey, do one thing try using a union of various data-types you intent to use, i think reading this shall not be a problem. As when your read-function using that union reads it, will be able to read it, because of the inherit C-type safety. What i mean here is the follow the same concept which we usually use to check a endianess of a machine.
These are few idea, i am myself working on, shall be able to complete this is a day or two. And only then i can tell you, if this is exactly possible or not. Good luck, if you are Implementing this for some project. Do share your solution, may be here itself, I might also find some answer. :)
I am using the array of void pointers.

Vivek Sharma
+2  A: 

I don't really recommend this, but here's the implementation you describe:

char var1;
int  var2;
char var3;

void *vars[3];
char *types[3];

vars[0]=&var1; types[0]="%c";
vars[1]=&var2; types[1]="%d";
vars[2]=&var3; types[2]="%c";

for (int i=0;i<3;i++)
{
    scanf(types[i],vars[i]);
}

You need the array of types so that scanf knows what it should expect.

However, this procedure is extremely unsafe. By discarding any type-safety, you invite crashes from malformed input. Also, if you misconfigure types[] then you will almost certainly crash, or see unexpected results.

By the time you've set up the arrays, have you really saved any code?

There are plenty of answers here that will allow you to use either a type-safe C++ solution, or as others have recommended, calling scanf() explicitly.

Dave Gamble
Dave, see my comment on RAOF's suggestion. I was hoping to have a similar loop processing approach to the data. Of course if all my data were char, no problem - I could use something like fgets(). Alternatively I could declare 'input' vars as char to ease the type issue and then do conversions internally where required...but this is beginning to look like a sledgehammer to crack a nut!
I would say, in general, if you're getting data from a human, you need to be very careful about what you get. Use fgetc() within a loop to fill an array (but only upto a maximum size). Parse the array with caution.
Dave Gamble
A: 

If you want an array of variables that can be "anything" and you are working in C, then I think you want something like a struct that contains a typeid and a union.

Like this, maybe: (Note, quick example, not compile tested, not a complete program)

struct anything_t {
  union {
    int i;
    double d;
    char short_str[7]; /* 7 because with this and typeid makes 8 */
    char *str; /* must malloc or strdup to use this */
  }; /* pretty sure anonymous union like this works, not compiled */
  char type; /* char because it is small, last because of alignment */
};

char *anything_to_str(char *str, size_t len, const struct anything_t *any)
{
   switch(any->type) {
     case 1: snprintf(str, len, "%d", any->i); break;
     case 2: snprintf(str, len, "%f", any->d); break;
     case 3: snprintf(str, len, "%.7s", any->short_str); break; /* careful, not necessarily 0-terminated */
     case 4: snprintf(str, len, "%s", any->str); break;
     default: abort(); break;
   }
   return str;
}

And I forgot to add the scanf part I intended:

char *scanf_anything(struct anything_t *inputs, size_t count)
{
  int input_i;
  struct anything_t *i;

  for(input_i=0; input_i<count; ++input_i) {
    any = inputs + input_i;
    switch(any->type) {
     case 1: scanf(" %d ", any->i); break;
     case 2: scanf(" %lf ", any->d); break;
     case 3: scanf(" %.6s ", any->short_str); break;
     case 4: scanf(" %a ", any->str); break; /* C99 or GNU but you'd be a fool not to use it */
     default: abort(); break;
    }
  }
}
Zan Lynx
Zan: thx. I'll study it and try to adapt it.
A: 

Formally all the solutions presented here which are using void* have the same problem. Passing a void* as a variadic argument (like scanf) which expects another type of pointer put you in the "undefined behavior" domains (for the same reason, you should cast NULL to the correct type when passed as variadic argument as well).

  • on most common platforms, I see no reason for a compiler to take advantage of that. So it will probably work until a compiler maker find out that there is a test in SPEC where it allows to get a 0.000001% improvement :-)

  • on some exotic platforms, taking advantage of that is the obvious thing to do (that rule has been put for them after all; they are mostly of historical interest only but I'd not bet anything about embedded platforms; I know, using scanf on embedded platforms can be considered as strange)

AProgrammer
A: 

All solutions above are valid but no one has yet mentioned that you can do what you want with a single simple scanf call:

scanf(%c%d%c",var1,var2,var3);
James Anderson
James: I have cursor positioning code between each call to scanf() so your suggestion is not really appropriate to my requirement.