数据类型对编译器来说何时意味着超过其存储空间

When does a data type means more than its storage space to the compiler?

本文关键字:存储空间 意味着 何时 编译器 数据类型      更新时间:2023-10-16

假设我有以下变量声明:

float f = 23.4;

现在这意味着两件事(也许更多?

  1. 它告诉编译器在内存中分配 4 个字节的存储空间。
  2. 它告诉编译器将这 4 字节存储中包含的位视为实数。

现在我的问题是f什么时候被编译器视为实数,而不仅仅是 4 个字节的存储空间?我可以想到以下场景:

  1. 当我给它分配一个数字(例如 23.4)时,编译器会将这个数字转换为表示实数的相应位并将其放入 f 中。
  2. 当我给它分配一个int变量时,int变量将被转换(强制转换)为表示实数的位并放入f(即使这意味着数据丢失)。或者当我尝试为其分配不允许的数据类型(例如struct)时,编译器会抱怨。
  3. 当我在算术运算中将其用作操作数时,例如i + f,编译器会知道f是一个floati是一个int,因此不仅会将包含在fi中的位相加,而是会添加这些位所代表的内容。

但是,例如,当我想将f输出到控制台时,编译器与它无关。因此,当我使用printf()时,是我告诉printf() f代表什么(通过指定%f参数)。或者当我想从控制台读取输入时,也是我告诉scanf()我想将从用户那里收到的字节转换为实数(通过指定%f参数)。

那么,是否有其他情况,编译器是负责解释变量内容的人呢?

您最终要问的是编译器如何工作以及它们如何处理类型信息和数据表示。这里的答案很广泛,有多种方法:

某些编译器执行类型擦除,因此当特定变量在内存中时,没有关于该变量是什么类型的信息。编译器如何知道在调用printf时应该如何处理数据?好吧,因为你告诉了它类型。表现出类型擦除的编译器确保对于任何变量,所有类型在编译时都是已知的,因此当将更高级别的程序翻译成机器语言时,例如,将两个变量加在一起时,它知道是执行整数加法还是浮点加法,因为它知道所涉及的变量的类型。它还可以知道它应该如何传递要打印的变量等。

另一方面,某些解释器和托管编译器不显示类型擦除。相反,这些编译器将表示变量类型的代码编码为变量本身的一部分。这允许解释器在运行时检查变量是什么,必要时强制转换它,并决定对其执行哪些操作。


请注意,整数和单精度浮点数在内存中的表示方式非常不同(尽管在大多数语言和体系结构中两者都占用 4 个字节)。要知道如何显示或添加某些内容,编译器需要知道它是什么类型。编译器可以发出在运行时(某些托管程序)上决定的代码,或者解释器可以决定在运行时如何处理它,或者编译器可能需要知道编译时每个变量是什么,从而消除了在运行时在内存中存储和指定类型的需要。


在 C 语言

中,printf 是语言表明它不是完全类型安全的领域之一(它最初让我感到困惑,为什么有人会说 C 不是类型安全的,因为它显然有类型并且似乎很强的类型)。

请注意,在C++中,使用 std::cout ,您可以输出intdoublestd::string等,而无需告诉编译器您要输出的内容。这是因为对于std::cout,C++编译器将在编译时找出变量的类型,并调用适当的<<重载,从而正确地格式化表示变量的内存块,无论是intdoublestd::string,还是其他什么。

然而,对于printf,函数的规范被设计为接受一堆"数据"并打印出来。我相信这种情况是有有趣和历史性的原因的。最明显的一个是可变长度参数不能用多种类型描述,因此传递给 printf 的参数是某种"任何"类型。但是在编译函数体时,编译器知道传递的参数数量可变,但不知道每个参数的类型。

如果你为printf提供了几十个重载,其中包含了不同可能类型的每个组合和排列,直到一定长度,那么你就可以避免暗示程序在运行时是什么类型。

严格

来说,我不认为这是你问题的答案,但听起来你误解了什么。

首先,您的假设中存在一些错误:

  • float变量的声明不会告诉编译器"在内存中分配 4 个字节的存储空间"。它告诉编译器分配一个float,但它是在内存中分配,在寄存器中,完全隐式分配(没有实际存储),还是以某种完全不同的方式分配取决于编译器。对于所有 C 规范的关注,编译器可以使用声卡将float存储在扬声器和麦克风之间的音频延迟循环中。
  • 即使您将float传递给printf(),编译器也非常复杂。您可能已经认为它只是将四个字节复制到堆栈上,但这仅仅是x86 ABI的巧合。即使在 AMD64 上,情况也不是如此,并且 varargs 函数的float参数将在 SSE 寄存器中传递(与在整数 GPR 中传递的int参数相反,因此编译器有很大的不同)。

当然,编译器确实不会告诉printf()获取float来打印它,但责任在于您,但这并不是因为编译器"忽略"了float变量的内容,而仅仅是因为没有传递给函数的运行时类型信息, 因此,printf()需要有关从调用帧中获取哪种值的信息。或者,换句话说,即使编译器知道值的数据类型并相应地调整生成的代码以匹配 ABI 和所有内容,它也没有义务告诉printf()它传递给它的内容,因此您需要告诉printf()它的位置。

从这个角度来看,要回答你的问题,数据类型总是不仅仅意味着编译器的存储大小,但我觉得你想问的实际问题是别的。

请检查 http://en.wikipedia.org/wiki/Double-precision_floating-point_format

它比 4 字节的存储空间多一点。

基于编译器产生正确指令集并了解数据类型的整个CPU浮点运算,

当然,您可以分配 4 个字节的存储空间,将其伪造为实数( cast),但这相当于搬起石头砸自己的脚。