14

在 C 中,您可以转换简单的数据类型,如int,float和指向这些的指针。

现在我假设如果你想从指向一种类型的指针转​​换为另一种类型的值(例如 from *floatto int),强制转换和取消引用的顺序无关紧要。即对于一个变量float* pf,你有(int) *pf == *((int*) pf). 有点像数学中的交换律...

然而,情况似乎并非如此。我写了一个测试程序:

#include <stdio.h>
int main(int argc, char *argv[]){
  float f = 3.3;
  float* pf = &f;
  int i1 = (int) (*pf);
  int i2 = *((int*) pf);
  printf("1: %d, 2: %d\n", i1, i2);
  return 0;
}

在我的系统上,输出是

1: 3, 2: 1079194419

因此,转换指针似乎与转换值的工作方式不同。

这是为什么?为什么第二个版本没有做我认为应该做的事?

这是平台相关的,还是我以某种方式调用未定义的行为?

4

6 回答 6

17

下面说在 pf 处获取浮点数并将其转换为整数。这里的强制转换是将浮点数转换为整数的请求。编译器生成代码以将浮点值转换为整数值。(将浮点值转换为整数是“正常”的事情。)

int i1 = (int) (*pf);

下面说首先强制编译器认为 pf 指向一个整数(并忽略 pf 是指向浮点数的指针的事实)然后获取整数值(但它不是整数值)。这是一件奇怪而危险的事情。在这种情况下,强制转换会禁用正确的转换。编译器在内存中执行位的简单副本(产生垃圾)。(而且也可能存在内存对齐问题!)

int i2 = *((int*) pf);

在第二个语句中,您不是在“转换”指针。您正在告诉编译器内存指向什么(在本例中,这是错误的)。

这两个语句在做非常不同的事情!

请记住,c 有时使用相同的语法来描述不同的操作。

==============

请注意,double 是 C 中的默认浮点类型(数学库通常使用双参数)。

于 2011-01-08T23:57:32.610 回答
4

如果您先取消引用,然后再转换为 int,您将获得从 float 转换为 int 的通常(截断)行为。如果先转换为指向 int 的指针,然后取消引用,则该行为不是由标准定义的。通常它表现为将包含浮点数的内存解释为 int。请参阅http://en.wikipedia.org/wiki/IEEE_754-2008了解其工作原理。

于 2011-01-08T14:17:39.427 回答
4

当然可以!强制转换告诉编译器如何查看内存的某个部分。然后,当您访问内存时,它会尝试根据您告诉它的方式来解释那里的数据。使用以下示例更容易理解它。

int main()
{
    char A[] = {0, 0, 0, 1 };
    int p = *((int*)A);
    int i = (int)*A;
    printf("%d %d\n", i, p);
    return 0; 
}

32 位小端机器上的输出将是0 16777216. 这是因为(int*)A告诉编译器将 A 视为指向整数的指针,因此当您取消引用 A 时,它会查看从 A 开始的 4 个字节(as sizeof(int) == 4)。在考虑了字节序之后,4 个字节的内容评估为 16777216。另一方面,*A取消引用 A 以获取0并将(int)*A其强制转换为 0。

于 2011-01-08T14:31:58.283 回答
2

Cast,然后取消引用的意思是“假装我指向另一个东西,然后得到另一个应该被指向的东西”。

取消引用,然后强制转换的意思是“获取我实际指向的东西,然后按照通常的规则将其转换为其他东西”。

“通常的规则”可以更改位,以获得逻辑上表示相同值的另一种类型的值。假装你在指着你实际上并没有指向的东西是不可能的。

于 2011-01-08T14:57:25.090 回答
1

int 在内存中的表示方式与浮点数不同。您所看到的是编译器认为指针指向一个 int,它查看 32 位内存,认为它会找到一个 int,而实际上它正在寻找浮点数的未定义部分,这很大数字。

于 2011-01-08T14:17:18.333 回答
1

根据标准的6.5/7,粗略地说,存储值必须与有效类型或字符类型兼容。因此,我认为该声明int i2 = *((int*) pf)的定义不明确。

于 2011-01-08T19:32:27.453 回答