Указатели на переменные? - PullRequest
0 голосов
/ 17 июля 2009

Можно ли иметь указатели на переменные данных? Я знаю, что могу иметь, скажем, указатели на строки, например char * str [n] и я могу выполнить цикл for для этих указателей, чтобы получить строки ... str [i] где i - счетчик индекса.

Если у меня есть некоторые данные, например,

char var1;
int  var2;
char var3;

и я хотел получить данные из stdin. Я мог бы использовать 3 отдельных вызова функции scanf () - просто пример - для заполнения этих переменных.

Могу ли я иметь «массив указателей на данные», например, void * data [], где data [0] = char var1, data [1] = int var2 и data [2] = char var3, чтобы я мог затем использовать один вызов функции scanf () в цикле for заполнить эти переменные? (Я предполагаю, что тип должен быть пустым, чтобы обслуживать различные типы в массиве)

Ответы [ 10 ]

3 голосов
/ 17 июля 2009

Я не очень рекомендую это, но вот реализация, которую вы описываете:

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

Вам нужен массив типов, чтобы scanf знал, чего он должен ожидать.

Однако эта процедура крайне небезопасна. Отбрасывая безопасность типов, вы вызываете сбои из-за неправильного ввода. Кроме того, если вы неверно сконфигурируете types [], вы почти наверняка вылетите или увидите неожиданные результаты.

Когда вы настроили массивы, действительно ли вы сохранили какой-либо код?

Здесь есть множество ответов, которые позволят вам использовать либо безопасное для типов решение C ++, либо, как другие рекомендовали, явно вызывать scanf ().

2 голосов
/ 17 июля 2009

У вас наверняка может быть такой массив void * data []. Однако вы не сможете прочитать их через scanf, так как вам нужен другой спецификатор формата для разных типов данных.

Если вы хотите сделать это, вы можете перебрать массив

<code>struct dataType
{
  void *data;
  char *format_specifier;
}
или что-то подобное. Тем не менее, я сомневаюсь, что это было бы хорошей идеей - вы, вероятно, захотите также запрашивать каждое значение, поэтому вы добавите еще один запрос char * в эту структуру, и вам, вероятно, понадобятся другие вещи позже.

Я подозреваю, что код, который вы в конечном итоге напишите для этого, потребует гораздо больше усилий, чем простое сканирование n раз, даже для довольно больших n .

0 голосов
/ 17 июля 2009

Все приведенные выше решения действительны, но еще никто не упомянул, что вы можете делать то, что вы хотите, с помощью одного простого вызова scanf:

scanf(%c%d%c",var1,var2,var3); 
0 голосов
/ 17 июля 2009

Формально все представленные здесь решения, использующие void *, имеют одинаковую проблему. Передача void * в качестве аргумента переменной (например, scanf), который ожидает указатель другого типа, помещает вас в домены «неопределенного поведения» (по той же причине вы должны приводить NULL к правильному типу, когда также передаете как аргумент переменной).

  • на большинстве распространенных платформ, я не вижу причин для компилятора воспользоваться этим. Так что это, вероятно, будет работать до тех пор, пока производитель компиляторов не обнаружит, что в SPEC есть тест, позволяющий получить улучшение на 0,000001%: -)

  • на некоторых экзотических платформах, очевидным преимуществом является использование этого преимущества (в конце концов, для них было разработано это правило; они в основном представляют исторический интерес, но я бы не стал ставить на встроенные платформы; Я знаю, использование scanf на встроенных платформах может показаться странным)

0 голосов
/ 17 июля 2009

Если вам нужен массив переменных, которые могут быть «чем угодно», и вы работаете в C, то я думаю, что вам нужно что-то вроде структуры, содержащей typeid и объединение.

Примерно так: (обратите внимание, быстрый пример, не проверенная компиляция, не полная программа)

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

И я забыл добавить часть развертки, которую я намеревался:

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;
    }
  }
}
0 голосов
/ 17 июля 2009

Похоже, вы пытаетесь реализовать шаблон (C ++), эквивалентный в C.: D, именно это я и пытаюсь сделать для одного из моих проектов. Я думаю, что мой случай менее запутанный, так как я использую только один тип данных (некоторая специфичная для проекта структура), мой массив не будет смешивать типы данных.
Эй, сделайте одну вещь, попробуйте использовать объединение различных типов данных, которые вы намереваетесь использовать, я думаю, что читать это не будет проблемой. Например, когда ваша функция чтения, использующая этот союз, прочитает ее, сможет прочитать ее из-за унаследованной безопасности C-типа. Здесь я имею в виду следующее понятие, которое мы обычно используем для проверки порядка работы машины.
Это несколько идей, над которыми я сам работаю, сумею завершить это день или два. И только тогда я могу сказать вам, если это возможно или нет. Удачи, если вы реализуете это для какого-то проекта. Поделитесь своим решением, может быть, здесь сам, я также мог бы найти какой-то ответ. :)
Я использую массив указателей void.

0 голосов
/ 17 июля 2009

Для иллюстрации проблемы ..

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;
}
0 голосов
/ 17 июля 2009

у вас может быть массив void *, поэтому * array [0] = var1 и т. Д.

0 голосов
/ 17 июля 2009

Проблема в том, что этот массив void * будет иметь дело с типами данных разных размеров. Для этой проблемы вы, вероятно, захотите использовать структуру и поддерживать массив из них. Или вы могли бы просто поместить ваши данные в виде байтового массива, но тогда вам нужно было бы знать, как правильно их разделить.

0 голосов
/ 17 июля 2009

Не напрямую, нет. Если вы можете использовать C ++ в этой ситуации, самое близкое, что вы могли бы сделать, - это обернуть каждую переменную в объекте (что-то вроде variable_t или какое-то шаблонное полиморфное решение). Например, я считаю, что вы можете сделать что-то вроде этого:

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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...