【问题标题】:When is typecasting a pointer allowed?何时允许对指针进行类型转换?
【发布时间】:2019-07-22 00:47:39
【问题描述】:

我正在准备考试,我收到的一个样题如下:

“以下哪个表达式分配给 new_val(该表达式将替换 HERE)将打印 7?”选择所有适用的:

float m = 7.0
int *a = (int *) &m;

int new_val = HERE;
printf("%d\n", new_val);

(a) *(int *) &m

(b) *a

(c) (int) m

(d) 以上都不是。


答案只是(c)

我已经在我自己的机器上运行了这个程序,我发现的结果与答案一致。但是,我想知道有人怎么能得到这个答案?更一般地说,我无法理解有关投射指针的规则。我在 StackOverflow 上阅读了其他几篇文章,这些文章对我没有太大帮助。

我想知道是否有人可以帮助澄清我如何解决与此类似的问题。

【问题讨论】:

  • (c) (或更简单的答案 (c') m)是将浮点值转换(或强制转换)为整数的正确方法。意识到这一个转换:float 值 7.0 的内部表示与int 值 7 的内部表示不同
  • 答案 (a) 和 (b),另一方面,试图重新解释 float 值 7.0 的内部表示,就好像它是 int没有 i> 进行任何浮点到整数的转换。由于 7.0 的 float 表示通常是位模式 0x40e00000,因此答案 (a) 和 (b) 可能会给您类似 1088421888 的信息。
  • OP 提示:尽管您忠实地复制了考试中的问题并将其提交到 Stack Overflow,但该问题存在缺陷。考试的作者没有完全理解C标准,也没有正确答案。 (My answer 解释了原因。)由于这个缺陷,这个问题没有用,应该被否决。

标签: c pointers casting


【解决方案1】:

但是使用

int new_val = (int)m;

使用int* a = ...忽略在上一行中所做的一切

因此,您只是将原始浮点 m 转换回 int。

编辑 尝试以下方法:


#include "stdio.h"

int main(int argc, char** argv) {
    float m = 7.1;
    int new_val = (int)m;
    printf("%d\n", new_val);

}

【讨论】:

    【解决方案2】:

    解决这个问题的方法是查看每个多项选择答案,并找出陈述的含义。考虑变量的类型。

    但是在这种情况下,将 float * 转换为 int * 只是意味着您告诉编译器“您知道这个包含 float 的内存地址?访问该内存就好像它拥有一个 int ”。 ints 和 floats 在内存中的存储方式不同,因此尝试以错误的格式访问它会产生无意义的结果。转换指针不会转换指针指向的数据。

    在选项 c 的情况下,请注意您不是在转换指针 - 您是在将 float 转换为 int。这是一个简单的转换。

    【讨论】:

      【解决方案3】:

      问题有问题。 C 标准允许 (a)、(b) 和 (c) 中的每一个打印“7”,并且还允许它们中的每一个不打印“7”。

      对于 (a),我们有 int new_val = * (int *) &m;。这里,&m 的类型为 float *,因此这会将 float * 转换为 int *。 C 2018 6.3.2.3 7 告诉我们可以进行这种转换:“指向对象类型的指针可以转换为指向不同对象类型的指针。”但它也“如果结果指针未正确对齐引用类型,则行为未定义。”因此,在float 具有比int 更严格的对齐要求的实现中,该行为未由 C 标准定义,因此允许符合 C 的实现打印“7”并且允许不打印“7” .假设实现成功评估转换而没有对齐问题,然后应用*。这将在下一段中介绍。

      在 (b) 中,我们有 int new_val = *a;,其中 a 已初始化为 (int *) &m;。与 (a) 一样,此表达式将 float * 转换为 int *,因此与上述对齐问题发生冲突。但是,再次假设避免了对齐问题,然后它使用指针访问float 对象m,就好像它是一个int。这违反了 C 2018 6.5 7,其中规定“对象的存储值只能由具有以下类型之一的左值表达式访问:”然后继续列出各种选项,其中没有一个允许访问 @987654337 @ 作为int。由于违反了这个“应”要求,C 2018 4 1 告诉我们行为未定义:“如果违反了出现在约束或运行时约束之外的“应”或“不应”要求,则行为未定义。 ”由于 C 标准没有定义行为,因此允许符合要求的实现打印“7”,并且允许不打印“7”。

      在(c)中,我们有int new_val = (int) m;,其中m是一个float,值为7。这是一个定义明确的转换,结果是7,所以printf("%d\n", new_val);通常会打印“ 7”(带有换行符)。但是,早期的问题仍然存在:即使对 new_val 进行了正确定义,早期的声明 int *a = (int *) &m; 也会执行可能导致未定义行为的转换。在我们阅读new_val 的定义之前,这可能会导致未定义的行为。因此,与 (a) 和 (b) 一样,符合要求的实现可以打印“7”,也可以不打印“7”。

      【讨论】:

      • 问题是关于转换指针和转换值之间的区别。没有迹象表明这是一个可运行的程序。 am 的定义只是问题的设置。
      • @WillisBlackburn:这个问题有缺陷。任何询问使用源自float *int * 的设置都必须包含可能具有未定义行为的转换。所以缺陷是问题的根本;无法通过更改设置来消除它。此外,(a) 和 (b) 都需要混叠违规。
      • @EricPostpischil 看,你是对的,好吗?但是你是对的,这对 OP 一点帮助都没有。他正在学习的考试并不指望他能发现给定的细微缺陷。它询问他是否知道转换指针实际上并不会改变指针所指向的东西,而转换值实际上会改变值。
      【解决方案4】:

      因为a = (int *)&m,选项(a)和(b)是相同的。在这两种情况下,您都会获取m 的地址,将其转换为int *,然后取消引用它。

      (a)和(b)不起作用的原因是&m是指向floatfloat *)的指针,而不是指向intint *)的指针。

      考虑两个指针,一个是指向Window 的指针,另一个是指向Joystick 的指针。很明显,你不能把它们混在一起,对吧?您不能将指向Window 的指针传递给需要指向Joystick 的指针的函数。您可以强制转换指针,但这不会使 Window 变为 Joystick

      所以指向 float 的指针和指向 int 的指针是不兼容的,如果你将指向 float 的指针转换为指向 int 的指针并尝试取消引用它,你会得到垃圾。

      但是 (c) 有效,因为在这里您使用实际的 floatm 并要求编译器将该值转换为 int 值。编译器知道如何执行此操作并生成正确的代码。

      您可能想知道,如果编译器知道如何将float 转换为int,为什么不能将指向float 的指针转换为指向int 的指针?事实上确实如此;这就是演员的目的。但它转换的是 pointer 而不是 指针指向的东西

      【讨论】:

      • (int *) &m 中的转换在 C 2018 6.3.2.3 7 中可能具有未定义的行为,因此 C 标准允许在这三种情况中的任何一种情况下打印“7”,并且它还允许一致的实现在这三种情况中的任何一种情况下都不打印“7”。在情况 (a) 和 (b) 中,由于混叠违规,还存在未定义的行为。
      猜你喜欢
      • 1970-01-01
      • 2017-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-12
      • 1970-01-01
      • 2012-12-09
      相关资源
      最近更新 更多