Paul
Paul

Reputation:

Pointers to variables?

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)

Upvotes: 0

Views: 690

Answers (10)

James Anderson
James Anderson

Reputation: 27478

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); 

Upvotes: 0

AProgrammer
AProgrammer

Reputation: 52314

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)

Upvotes: 0

Zan Lynx
Zan Lynx

Reputation: 54345

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;
    }
  }
}

Upvotes: 0

Dave Gamble
Dave Gamble

Reputation: 4174

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.

Upvotes: 3

Vivek Sharma
Vivek Sharma

Reputation: 3814

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.

Upvotes: 0

Smashery
Smashery

Reputation: 59673

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.

Upvotes: 0

Skrymsli
Skrymsli

Reputation: 5313

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;
}

Upvotes: 0

Robert
Robert

Reputation: 3062

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

Upvotes: 0

RAOF
RAOF

Reputation: 1136

You certainly could have such a void *data[] array. You wouldn't be able to read those in via scanf, though, as you need a different format specifier for the different data types.

If you wanted to do this, you could iterate over an array of

struct dataType
{
  void *data;
  char *format_specifier;
}
or somesuch. However, I doubt this would be a good idea - you probably want to also prompt for each value, so you'd add another char *prompt to that struct, and you'll probably need other things later as well.

I suspect the code you'd end up writing to do this would be much more effort than simply scanf-ing n times, even for quite large n.

Upvotes: 2

Dave Markle
Dave Markle

Reputation: 97771

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.

Upvotes: 0

Related Questions