数据类型对编译器来说何时意味着超过其存储空间
When does a data type means more than its storage space to the compiler?
假设我有以下变量声明:
float f = 23.4;
现在这意味着两件事(也许更多?
- 它告诉编译器在内存中分配 4 个字节的存储空间。
- 它告诉编译器将这 4 字节存储中包含的位视为实数。
现在我的问题是f
什么时候被编译器视为实数,而不仅仅是 4 个字节的存储空间?我可以想到以下场景:
- 当我给它分配一个数字(例如 23.4)时,编译器会将这个数字转换为表示实数的相应位并将其放入
f
中。 - 当我给它分配一个
int
变量时,int
变量将被转换(强制转换)为表示实数的位并放入f
(即使这意味着数据丢失)。或者当我尝试为其分配不允许的数据类型(例如struct
)时,编译器会抱怨。 - 当我在算术运算中将其用作操作数时,例如
i + f
,编译器会知道f
是一个float
,i
是一个int
,因此不仅会将包含在f
和i
中的位相加,而是会添加这些位所代表的内容。
但是,例如,当我想将f
输出到控制台时,编译器与它无关。因此,当我使用printf()
时,是我告诉printf()
f
代表什么(通过指定%f
参数)。或者当我想从控制台读取输入时,也是我告诉scanf()
我想将从用户那里收到的字节转换为实数(通过指定%f
参数)。
那么,是否有其他情况,编译器是负责解释变量内容的人呢?
您最终要问的是编译器如何工作以及它们如何处理类型信息和数据表示。这里的答案很广泛,有多种方法:
某些编译器执行类型擦除,因此当特定变量在内存中时,没有关于该变量是什么类型的信息。编译器如何知道在调用printf
时应该如何处理数据?好吧,因为你告诉了它类型。表现出类型擦除的编译器确保对于任何变量,所有类型在编译时都是已知的,因此当将更高级别的程序翻译成机器语言时,例如,将两个变量加在一起时,它知道是执行整数加法还是浮点加法,因为它知道所涉及的变量的类型。它还可以知道它应该如何传递要打印的变量等。
另一方面,某些解释器和托管编译器不显示类型擦除。相反,这些编译器将表示变量类型的代码编码为变量本身的一部分。这允许解释器在运行时检查变量是什么,必要时强制转换它,并决定对其执行哪些操作。
请注意,整数和单精度浮点数在内存中的表示方式非常不同(尽管在大多数语言和体系结构中两者都占用 4 个字节)。要知道如何显示或添加某些内容,编译器需要知道它是什么类型。编译器可以发出在运行时(某些托管程序)上决定的代码,或者解释器可以决定在运行时如何处理它,或者编译器可能需要知道编译时每个变量是什么,从而消除了在运行时在内存中存储和指定类型的需要。
在 C 语言
中,printf
是语言表明它不是完全类型安全的领域之一(它最初让我感到困惑,为什么有人会说 C 不是类型安全的,因为它显然有类型并且似乎很强的类型)。
请注意,在C++中,使用 std::cout
,您可以输出int
、double
、std::string
等,而无需告诉编译器您要输出的内容。这是因为对于std::cout
,C++编译器将在编译时找出变量的类型,并调用适当的<<
重载,从而正确地格式化表示变量的内存块,无论是int
、double
、std::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),但这相当于搬起石头砸自己的脚。
- 可用存储空间会消耗更多剩余内存吗?
- 在C++中,如果成员引用在其声明中初始化,为什么需要存储空间?
- 数组不会存储超出第一个空间的范围
- 将数组类型更改为 Int16 会节省存储空间吗?
- 尝试从每个预期数据之间有多个空间的文件中读取和存储数据
- 如何在文件中节省空间地存储和检索 std::vector<int> 值
- C 将字符串按空白空间将字符串拆分,除非将其封闭在引号中并存储在向量中
- 读取文件中的数字数量无限,存储在数组中(无空间)
- Apache 崩溃并显示错误 R6016 没有足够的空间来存储线程数据
- 如何通过空间分开输入并将其存储在C 中的向量中
- C 的空间indexex库:从/到磁盘加载/存储主内存rtree
- 数组的指针,该数组的每个元素的内存空间信息存储在哪里
- Do C和C++标准意味着地址空间中必须只存在一个特殊值来表示空指针的值
- 数据类型对编译器来说何时意味着超过其存储空间
- 如何在不分配更多存储空间的情况下从数组初始化向量
- 如何在C++中创建虚拟存储空间
- C++删除不同函数中的存储空间
- 从技术上讲,对象是否可以占用不连续字节的存储空间
- c++最大数值存储空间
- 什么会消耗更多的存储空间 - boost::d ynamic_bitset<> 还是原始存储?