几点:
- 不需要中介
pd将d的地址分配给t.val。以下应该可以很好地工作:
t.val = &d;
C 允许在void * 和其他指针类型之间直接赋值。 C++ 没有,所以这在没有强制转换的 C++ 中不起作用:
t.val = (void *) &d;
- 但是,这假定您将在
d 的生命周期内使用t.val。如果在您使用t.val 之前d 有可能超出范围,那么您应该分配一个新的内存块来存储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“超出范围”的一个非常人为的例子。假设我们在main 中创建了t 对象,然后使用该函数中的局部变量调用一个分配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 相同——我们在尝试访问val 成员之前取消引用tbl。
由于一旦assignVal 退出,d 就不再存在,我们需要分配一个新对象来存储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 中处理类型多态性有很多更聪明的方法。我现在想不出一个。