【问题标题】:How to store a double in a void * in C如何在 C 中的 void * 中存储双精度
【发布时间】:2019-02-11 17:17:45
【问题描述】:

我找到了Converting double to void* in C,但我无法运行任何解决方案。我想创建一个带有 void 指针的结构来存储不同类型的数据。

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

谢谢。

【问题讨论】:

  • 不要double 存储在void * 中。但是,您可以在 void * 类型的对象中存储一个 指向 double 的指针,也就是 double *
  • 您可以设置t.val = &d;t.val = pd;t.val 指向double。请注意,这是将double * 转换为void *,而不是像您的问题标题所暗示的那样将double 存储在void * 中。当您访问它时,请注意确保指针仍然指向有效的东西。要访问double,请将t.val 转换回double * 并取消引用它。例如。将printf("%d %f", t.id, t.val); 更改为printf("%d %f", t.id, *(double *)t.val);

标签: c pointers void


【解决方案1】:

几点:

  1. 不需要中介pdd的地址分配给t.val。以下应该可以很好地工作:
    t.val = &d;
    C 允许在void * 和其他指针类型之间直接赋值。 C++ 没有,所以这在没有强制转换的 C++ 中不起作用:
    t.val = (void *) &d;
  2. 但是,这假定您将在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 );
  3. 无论哪种方式,您都可以打印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

在此示例中,assignValprintVal 都假定 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 中处理类型多态性有很多更聪明的方法。我现在想不出一个。

【讨论】:

  • 非常感谢,如果你能展示一个如何在另一个“超出范围”的函数中使用它的例子,就像第 2 段中所述。如果我会使用它来应付我也可以以同样的方式使用 void 指针(存储指向任何类型的指针)吗?
  • 我不知道我是否理解得很好。第一种方式,当我在主函数中创建一个要构建的项目时,我不能在testfunc() 中使用它,例如?如果是这样,那么我不明白示例中的区别。
【解决方案2】:

打印值试试

printf("%d %f", t.id, *(double*)t.val);

t.val 是一个指针,而不是值本身,但由于它是一个 void*,如果不强制转换为正确的类型(在您的简单示例中为 (double*)),您无法取消引用它。

要存储数据,您不需要三个指针。您可以简单地分配地址:

t.id = i;
t.val = &d;

这种方法有两个可能的问题。

  • 如果您想“存储不同类型的数据”,当您想使用(打印)该值时,您必须知道struct Table 中存储的类型,因为您必须使用正确的转换。 (也许您的结构中需要一个额外的 type 字段。)

  • 1234563 '以后不要修改它的值。 (也许您需要动态内存分配来创建变量的副本。)

【讨论】:

    【解决方案3】:

    double 的大小为 64 位,而 void 指针在 32 位程序中为 32 位,在 64 位程序中为 64 位。

    因此,您可以在 64 位可执行文件中执行此操作,但不能在 32 位可执行文件中执行此操作。

    如果您想在这样的结构中存储不同类型的数据,您可以使用包含每种数据类型的联合。见https://www.tutorialspoint.com/cprogramming/c_unions.htm

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-05
      • 2014-09-09
      • 2011-07-27
      • 2011-08-05
      • 2011-04-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多