Как у меня есть переменная в C, которая может принимать различные типы? - PullRequest
3 голосов
/ 18 ноября 2009

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

Как я могу инициировать переменную, которая может принимать любые типы?

И как я могу преобразовать этот тип обратно в тип, который я хочу? (Напечатанный материал)

Спасибо.

Ответы [ 10 ]

8 голосов
/ 18 ноября 2009

Способ определения переменной, которая может содержать значения более чем одного типа, заключается в использовании объединений:

union uu {
  int i;
  float f;
  char c;
  char *s;
} x;

x.i - целое число, x.s указатель на символ и т. Д., А размер x является максимальным среди размеров типа членов.

К сожалению, единственный способ запомнить тип переменной - сохранить ее где-нибудь еще. Распространенное решение состоит в том, чтобы иметь такую ​​структуру:

 struct ss {
   int type;
    union {
      int i;
      float f;
      char c;
      char *s;
    } val;
  } x;

И сделать что-то вроде:

 #define FLOAT 1
 #define INT   2
 .... 

 x.val.i = 12;
 x.type = INT;
 ....
 if (x.type = INT) printf("%d\n",x.val.i);
 ....

действительно безобразно.

Есть и другая возможность играть с макропроцессором, чтобы сделать его немного более приятным для глаз, но суть в том, что вы должны знать заранее тип значения, хранящегося в объединении, и получить доступ к соответствующему полю.

7 голосов
/ 18 ноября 2009

Это не тривиально, но вот самый простой способ сделать это:

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

Во-первых, у вас фактически будет структура данных некоторой структуры, подобной struct { void * data; enum Type tag }

и определите enum Type { int, char*, ... etc }

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

Тег Type хранит данные, чтобы ваш код, используя их, мог знать, что возвращается из вашей структуры данных.

Если вам не нужно сохранять тип, и вы можете привести его обратно к правильному типу, когда извлекаете его из структуры данных, тогда вы можете опустить тег Type и просто сохранить void *

4 голосов
/ 18 ноября 2009

В C нет простого способа сделать это. Вы можете использовать void * и передавать указатели на типы, но концепция шаблонов, обобщений или вариантов не существует.

Чтобы использовать void *, вам нужно обращаться со всем как с указателем, так что он должен размещать его в куче. Затем вам нужно будет привести эти указатели к void * при отправке в эту структуру данных, а затем вернуть обратно на другую сторону. Это может быть сложно, потому что вы должны помнить, с каких типов нужно было начинать.

Если вы программируете в Windows, вы можете использовать Варианты для этого, но с этим связаны некоторые издержки.

2 голосов
/ 18 ноября 2009

У вас есть два варианта:

void* foo;

, который может указывать на данные любого типа, или:

union my_types {
   int* i;
   char* s;
   double* d;
   bool* b;
   void* other;
}

, который дает вам "автоматическое" приведение указателя (в том смысле, что вы можете ссылаться на переменную типа "my_types", как если бы это был любой из перечисленных выше типов) См. эту ссылку для получения дополнительной информации о профсоюзах.

Ни то, ни другое - отличный вариант - рассмотрите C ++ для того, что вы пытаетесь сделать.

2 голосов
/ 18 ноября 2009

Вы, вероятно, хотите void *.

1 голос
/ 18 ноября 2009

Ваше решение должно будет использовать указатели, чтобы аннулировать. Указатели на void могут содержать адрес любого типа объекта (не функции) и могут быть преобразованы обратно без потери информации.

Лучше всего использовать «родительскую структуру», чтобы вы знали тип указателя объекта на:

enum MyType { INTEGER, DOUBLE_STAR };
struct anytype {
    enum MyType mytype;
    size_t mysize;
    void *myaddress;
};

, а затем

struct anytype any_1, any_2;
int num_chars;
double array[100];

any_1.mytype = INTEGER;
any_1.myaddress = &num_chars;
any_1.mysize = sizeof num_chars;

any_2.mytype = DOUBLE_STAR;
any_2.myaddress = array;
any_2.size = sizeof array;

и, чтобы работать с этим

foo(any_1);
foo(any_2);

, где foo определяется как

void foo(struct anytype thing) {
    if (thing.mytype == INTEGER) {
        int *x = thing.myaddress;
        printf("the integer is %d\n", *x);
    }
    if (thing.mytype == DOUBLE_STAR) {
        size_t k;
        double *x = thing.myaddress;
        double sum = 0;
        for (k = 0; k < thing.mysize; k++) {
            sum += thing.myaddress[k];
        }
        printf("sum of array: %f\n", sum);
    }
}

КОД НЕ ПРОВЕРЕН

1 голос
/ 18 ноября 2009

Используйте void *?

Вы можете указать все, что хотите, конечно, вы должны выполнить проверку самостоятельно.

0 голосов
/ 18 ноября 2009

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

0 голосов
/ 18 ноября 2009

enum _myType { CHAR, INT, FLOAT, ... }myType;
struct MyNewType
{
   void * val;
   myType type;
}

Затем вы можете передать элементы типа MyNewType в ваши функции. Проверьте тип и выполните правильное литье.

0 голосов
/ 18 ноября 2009

Вы можете использовать указатель void *, чтобы принять указатель практически на что угодно. Возвращение к нужному типу - сложная часть: вы должны где-то хранить информацию о типе указателя.

Одна вещь, которую вы можете сделать, это использовать struct для хранения указателя void * и информации о типе, и добавить это в свою структуру данных. Основной проблемой является сама информация о типе; C не включает никакого отражения типов, поэтому вам, вероятно, придется создать перечисление типов или сохранить строку, описывающую тип. Затем вам придется заставить пользователя struct выполнить приведение обратно от void * к исходному типу, запросив информацию о типе.

Не идеальная ситуация. Возможно, гораздо лучшим решением было бы просто перейти на C ++ или даже C # или Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...