我找到了Converting double to void* in C,但是我无法运行任何解决方案。我想用空指针创建一个结构来存储不同类型的数据。
struct Table {
int id;
void *val;
};
在主函数中,我分配类似的值
struct Table t;
int i = 1;
double d = 2.5;
double *pd = &d;
void **p = (void**)pd;
void *dp = *p;
t.id = i;
t.val = dp;
但是我无法获得双精度值。
printf("%d %f", t.id, t.val);
谢谢。
答案 0 :(得分:2)
几点:
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
的函数,然后调用另一个函数以打印{{ 1}}指向:
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
指向双精度数-我们告诉它它指向的是哪种类型,并且它使用正确的强制转换和格式说明符进行打印
请记住,这些是肮脏的例子。在C语言中有很多处理类型多态性的聪明方法。我现在想不起来。
答案 1 :(得分:1)
要打印值,请尝试
printf("%d %f", t.id, *(double*)t.val);
t.val
是一个指针,而不是值本身,但是由于它是void*
,因此如果不强制转换为正确的类型(在简单示例中为(double*)
),则无法取消引用它
要存储数据,您不需要三个指针。您只需分配地址即可:
t.id = i;
t.val = &d;
此方法可能存在两个问题。
如果要“存储不同类型的数据”,则在使用(打印)值时必须知道struct Table
中存储的类型,因为必须使用相应的投。 (也许您在结构中需要一个额外的type
字段。)
如果您使用具有自动或静态存储的变量(如示例中的double d = 2.5;
)并将其地址存储在t.val
中,则必须确保该变量不会无效并且您以后不会修改其值。 (也许您需要动态分配内存来创建变量的副本。)
答案 2 :(得分:0)
双精度字的大小为64位,而空指针在32位程序中为32位,在64位程序中为64位。
因此,您可以在64位可执行文件中执行此操作,而不能在32位可执行文件中执行。
如果要在这样的结构中存储不同种类的数据,则可以使用包含每种数据类型的联合。参见https://www.tutorialspoint.com/cprogramming/c_unions.htm。