Несколько баллов:
- Вам не нужен посредник
pd
, чтобы назначить адрес d
для t.val
.Следующее должно работать идеально: t.val = &d;
C позволяет прямое назначение между void *
и другими типами указателей.C ++ этого не делает, поэтому это не будет работать в C ++ без приведения: t.val = (void *) &d;
- Однако это предполагает, что вы будете использовать
t.val
в течение времени жизни d
.Если существует вероятность того, что d
выйдет из области видимости, прежде чем использовать t.val
, вам следует выделить новый блок памяти для хранения значения d
: t.val = malloc( sizeof d );
if ( t.val )
*(double *)t.val = d;
Таким образом, вместо сохранения адресаd
в t.val
, вы сохраняете адрес динамически выделяемого блока памяти и копируете значение d
в этот динамический блок.Поскольку t.val
является указателем на void
, вам сначала нужно использовать выражение приведения (double *)
, чтобы сообщить компилятору, что он должен обращаться с ним как с указателем на double
.Затем вам нужно разыменовать этот указатель с помощью унарного оператора *
, чтобы присвоить значение d
этому новому блоку памяти.Это также означает, что вам нужно будет de выделить этот блок, когда вы закончите с ним: free( t.val );
- В любом случае, вы печатаете значение, на которое
t.val
указывает следующее: printf( "%d %f\n", t.id, *(double *)t.val );
Опять же, мы должны указать компилятору трактовать t.val
как указатель на double
и использовать унарный оператор *
для получения значения, на которое указывает указатель.
РЕДАКТИРОВАТЬ
Вот очень надуманный пример того, что я имею в виду d
"выход за рамки".Давайте предположим, что мы создаем объект t
в main
, затем вызываем одну функцию, которая назначает t.val
, используя локальную переменную в этой функции, а затем вызываем другую функцию, чтобы вывести значение, на которое указывает t.val
:
/**
* Sets the val member of a struct Table object. Since we want to
* modify the contents of the parameter, we need to pass a pointer
* to it.
*/
void assignVal( struct Table *tbl )
{
double d = some_value(); // d only exists for the duration of this function
...
tbl->val = malloc( sizeof d ); // since tbl is a pointer, use ->
if ( tbl->val ) // instead of . to access members
*(double *) tbl->val = d;
else
// error could not allocate memory for tbl->val
...
}
void printVal( struct Table tbl )
{
...
if ( tbl.val )
printf( "%d %f\n", tbl->id, *(double *) tbl.val );
else
// tbl.val was never allocated
...
}
int main( void )
{
struct Table t;
assignVal( &t );
printVal( t );
if ( t.val )
free( t.val );
}
Поскольку мы хотим, чтобы assignVal
изменил содержимое t
, нам нужно передать указатель на него в качестве аргумента.Синтаксис tbl->val
такой же, как и (*tbl).val
- мы разыменовываем tbl
перед попыткой доступа к члену val
.
Поскольку d
перестает существовать, как только выходит assignVal
, нам нужно выделить новый объект для хранения значения d
.Как только мы закончим с этим значением, мы освобождаем t.val
.
. В этом примере assignVal
и printVal
оба предполагают, что t.val
указывает на объект double
.Для более общего кода вы можете добавить еще один элемент, который отслеживает тип объекта, на который указывает val
.
Например:
struct Table {
int id;
int valueType;
void *val;
};
void assignDblVal( struct Table *tbl )
{
double d = ...;
...
t->valueType = DOUBLE; // where DOUBLE is some kind of integer constant
t->val = malloc( sizeof d );
if ( t->val )
*(double *) t->val = d;
...
}
void assignIntVal( struct Table *tbl )
{
int x = ...;
...
tbl->valueType = INT;
tbl->val = malloc( sizeof x );
if ( tbl->val )
*(int *) tbl->val = x;
...
}
void printVal( struct Table tbl )
{
switch( tbl.valueType )
{
case CHAR:
printf( "%d '%c'\n", tbl.id, *(char *) tbl.val );
break;
case INT:
printf( "%d %d\n", tbl.id, *(int *) tbl.val );
break;
...
case DOUBLE:
printf( "%d %f\n", tbl.id, *(double *) tbl.val );
break;
...
}
}
Таким образом, printVal
не должен предполагать, что tbl.val
указывает на двойное число - мы сообщаем ему, на какой тип он указывает, и он использует правильный спецификатор приведения и форматирования для печати.
Помните,это быстрые и грязные примеры.Есть много более умных способов борьбы с полиморфизмом типов в C. Я просто не могу думать об одном прямо сейчас.