【问题标题】:Method to pass double to void * argument将双精度传递给 void * 参数的方法
【发布时间】:2020-01-13 16:28:35
【问题描述】:

我的问题是需要通过void * 函数参数正确传递double 变量。我问是因为在我的机器上sizeof(void *)4,而sizeof(double)8,将一个转换为另一个似乎应该会导致问题,但是我的编译器(CLANG,所有警告都打开。)给出没有任何问题的迹象,并且代码似乎工作正常。

请注意,我见过thisthis。他们的标题中有相似的组成词,但不回答这个具体问题。

以下是否会导致严格的别名违规错误?或未定义的行为?

// some calling function
double a = 0.000234423;
func1(&a);

...

void func1(void *var)
{
    double a = *(double *)(var);
}

【问题讨论】:

  • 您的 4 字节指针指向一个 8 字节对象。有什么问题?
  • 你的问题说你想在指针的值中存储一个双精度值,但是你的代码说你想在你的指针中存储一个指向双精度值的指针。哪一个?只有一个有意义,但你仍然应该具体。
  • @MadPhysicist:我看不到任何地方字面上说要存储双 。问题是你如何传递一个双变量,基本答案归结为“按引用传递”,因为“按值传递”不起作用。
  • 只要你不做肮脏的hack就可以了,即将一个值转换为一个指针,然后让回调函数从指针转换回整数。显然,这是各种 thrashy pthread 应用程序中的常见 hack(因为他们担心如果通过 ref 传递原始值会超出范围)。
  • sizeof(void *) is 4 那么?如果 void *char * 开头并指向一个 124,513 字节的 char 数组呢?

标签: c strict-aliasing


【解决方案1】:

指针的大小与它所指向的元素的大小无关!

4 字节指针可以毫无问题地指向 8 字节或 32 字节或任何大小的元素。

我问是因为在我的机器上 sizeof(void *) 是 4,而 sizeof(double) 是 8,将一个转换为另一个似乎应该会导致问题

好吧,如果您确实将 4 字节指针转换为 8 字节双精度,那将是一个主要问题。但是,这不是您的代码在做什么。

这就是代码的作用:

double a = * (double *)(var);
           | \--------------/
           |  This casts a void pointer to a double pointer
           |
           --> This dereferences the double pointer, i.e. reads the value of the pointed
               to element (aka the pointed to double)

由于 void 指针是从 double 的地址创建的,因此将其强制转换回 double 指针是完全合法的,并且读取指向元素的值是完全合法的。换句话说 - 在函数调用期间,您有一个 double pointervoid pointer 的隐式转换,并且在函数内部 void pointer 被转换回到双指针。完全合法的 C 代码。

加上一个额外的步骤,您的代码相当于:

void func1(void *var)
{
    double *pd = (double *)var;
    double a = *pd;
}

这样会更清楚一点。

【讨论】:

  • Re “指针的大小与指向的元素的大小无关!”:这取决于 C 实现。对于不同的类型,C 实现可能有不同大小的指针。
  • @EricPostpischil:C 标准让我感到厌烦的一件事是,它无法识别可以而且确实以零成本提供更强大的语义保证但可能代价高昂的常见实现类别或对其他人不切实际。在绝大多数大多数平台上,所有数据指针类型都具有相同的大小和表示形式,并且空指针表示形式的所有字节都将为零。做出这种假设的代码不能移植到它们不具备的平台上,但程序员不应该为平台做出可移植性让步......
  • ...没有人会对运行他们的代码感兴趣。必须编写代码以在奇怪平台上使用的人应该知道,普通的假设不会成立,但 99% 的代码永远不会在此类平台上运行的程序员不应该对此类问题感到偏执。
【解决方案2】:

正常的解决方案确实是传递double变量的地址,所以一个double*。它的大小不大于void*

【讨论】:

  • 这就是他们正在做的事情。我认为这个问题只是一个误解,认为对象的大小很重要,但它是指向它的指针的大小。这很好。
  • @underscore_d:是的,我意识到措辞可以更明确。添加了“确实”。
  • 指针类型的 size 无关紧要。重要的是从double *void *double * 的转换是经过良好定义的并且可以根据需要工作。
  • @chux-ReinstateMonica:有一个理论上的论点是,在某些系统上,char* 可能需要指向更大机器字内的单个字节,而本机寻址没有此功能。这意味着char* 必须与本机指针加上一个字节偏移量一样大,因此void* 也是如此,而不是double*。实际上,每个人都使用八位字节。
  • @ryyker:使强制转换合法的原因在于它完全反转了调用中的隐式强制转换。但是 C 并没有强制执行。 func1("pi") 也可以编译。
【解决方案3】:

如果接收void* 类型参数的函数需要接收双精度值,则可以定义结构、函数和宏:

struct doubleWrapper { double d[1]; };
struct doubleWrapper doWrapDouble(double d)
{      
  return (struct doubleWrapper){d};
}
#define wrapDouble(x) (doWrapDouble((x)).d)

然后在需要传递double 值的呼叫站点使用该函数:

void functionTakingVoid(int mode, void *param)
{
  if (mode==0)
  {
    double myDouble = *(double*)param;
    ...
  }
}

void passDoubleToFunctionTakingVoid(double myValue)
{
  functionTakingVoid(0, wrapDouble(myValue));
}

在 C99 下,wrapDouble 宏将返回一个指向 double 的指针,该指针的生命周期将通过对封闭的完整表达式的评估而延长,包括由此进行的函数调用。

【讨论】:

  • 我没听说过wrapDouble。顺便说一句,this question 有更多关于更新版本的对话,其中包含有关相同主题的更多详细信息
  • @ryyker:它是一个宏,它调用上面给出的函数,返回一个结构,上面也给出了。
猜你喜欢
  • 2012-02-03
  • 2012-11-19
  • 2018-09-26
  • 1970-01-01
  • 2016-01-15
  • 1970-01-01
  • 1970-01-01
  • 2013-01-11
  • 1970-01-01
相关资源
最近更新 更多