【问题标题】:Function returning wrong value, in C, using gcc Intel compiler使用 gcc 英特尔编译器在 C 中返回错误值的函数
【发布时间】:2011-12-13 11:36:23
【问题描述】:

我写这篇文章是因为我真的不知道为什么会这样。我知道如何纠正它/绕过它,但我想知道发生这种情况的原因。我正在使用 C,我的编译器是在 Intel 机器上运行的 gcc 4.4.1 (TDM)。

关于浮点数的假设: -符合 IEEE 754 标准 - 以 Big Endian 方式存储

假设我们有一个函数接收 4 个字节的数组并将它们作为浮点数返回。这就是函数的目标。我们还假设,例如,所有函数都会以“正确的顺序”获取字节,并且由于系统是小端的,它只会交换它们并将它们放入浮点数以返回一个值。为简单起见,我不包括对 NaN 或 INF 的任何检查,因为这不是本问题的目的。

float testFunction(char* arr)
{
    //this will be the float we return
    float ret;

    //let's just get a char pointer to the float so we can alter its byte values
    char* c = (char*)&ret;
    //just swap them so they conform with little endian byte order
    c[0] = arr[3];
    c[1] = arr[2];
    c[2] = arr[1];
    c[3] = arr[0];

    //up to here if you debug and watch ret's value it is correct as it is supposed to be
    return ret;
}

问题在于我在哪里使用该功能......让我们说如下

   float f = testFunction(arr);

那么浮点 f 与您作为参数传递的字节具有完全无关的浮点值。

成功解决这个问题的方法是声明一个接受浮点数作为参数的函数,并在函数内部给它一个值,如下所示:

void testFunction(char* arr,float* f)
{
   char* c = ((char*)f)
   c[0] = arr[3];
   c[1] = arr[2];
   c[2] = arr[1];
   c[3] = arr[0];
 }

但我的问题仍然是,为什么当我尝试返回值时会发生这种情况?我确实理解 float ret 是函数范围内的临时值,但 return 语句应该将其值复制到函数之外。这不是正确的吗? 我错过了什么?我想这一定很明显。

【问题讨论】:

  • 您传入的是什么字节数组以及您期望的float 值是多少?
  • 如果我们认为 float 是 32 位(而 char 是 8 位),那么您所做的是正确的。
  • @Charles:任何字节数组,我希望浮点数符合 IEEE 754 标准。只需使用任何转换器来查看您必须以字节形式给出的内容以及您应该期望的浮点数。一个非常好的在这里:h-schmidt.net/FloatApplet/IEEE754.html
  • 我试过你的测试功能,它对我来说没问题。至少我提供给函数的字节和返回的浮点值是相同的并且具有正确的顺序。因此,请尝试在您的机器上逐字节打印返回值。
  • @Lefteris 所以arr 是BigEndian,对吧?因为如果它已经是LittleEndian,则不需要交换。

标签: c function


【解决方案1】:

实际上,在这两种情况下,浮点数都应该具有“完全不相关”的值,除非您仔细制作该字符数组(例如 memcpy 来自 float)。

您不能只设置字节并希望它能神奇地匹配您平台上的表示。

【讨论】:

  • 什么意思?我假设浮点表示符合以小端格式存储的 IEEE 754 单精度标准。它还能比这更多样化吗? - 第二种情况,通过引用传递似乎总是在我的系统上工作。这就是为什么我有点困惑
  • @Lefteris 让我们这样说:有些地方有 C 而没有 IEEE 754。当你问关于 C 的问题时,最好在开头写一些类似“我需要使这项工作在英特尔的 gcc(VC++/英特尔 C 编译器)上工作”。这样,没有人可以向您抛出“C 标准不谈论这个或那个”。然后您进入“特定平台和特定编译器”的土地。要了解 C/C++ 标准有多“通用”,请阅读此parashift.com/c++-faq-lite/intrinsic-types.html#faq-26.6
  • 是的,你是对的 xanatos。我应该指定这一点。您提供的文章读起来很有趣。会花一些时间阅读它。但问题仍然存在,有人知道为什么这是一种方式而不是另一种方式吗?
【解决方案2】:

我会说些蠢话。但是尝试初始化ret,比如float ret = 0;。

我不确定您是否可以一次初始化一个变量“piecemail”char,并认为它已为 C 标准(和编译器)“初始化”

【讨论】:

    【解决方案3】:

    摇摆。由于类型转换,编译器无法识别 f 变量已更新。对于编译器,fvariable 和您的 c 指针之间没有链接。它不知道这是一个别名。 在 ABI 中,浮点数在寄存器中返回,它应该在执行 return 时从堆栈中生成负载,但是当它看到 f 未初始化时,它什么也不做并返回寄存器的随机内容用于那个。 如果您将float 声明为volatile,它应该符合您的预期。

    如前所述,这是一个疯狂的猜测。

    【讨论】:

    • 这是一个不错的猜测,但不幸的是至少在我的系统中它不起作用。感谢您对此进行一些思考。只是想知道您的两个缩写:WAG - >? ABI -> 应用程序二进制接口?
    • WAG:wild ass guess ABI: Application Binary Interface 操作系统用来在函数之间传递参数的约定,编译器必须遵循。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 2014-06-14
    • 2014-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多