浅谈(int)a、&a、(int)&a、(int&)a区别

本文深入探讨了C++中浮点数的内部存储机制,详细解释了float类型的32位二进制表示,包括符号位、指数位和尾数位的分配,以及如何将实数转换为C++浮点数格式。
  /*
                           浅谈(int)a、&a、(int)&a、(int&)a区别

*/
#include
using namespace std;

int main()
{
float a = 1.0f;
cout << sizeof(int) << endl;//4
cout << sizeof(float) << endl;//4

int b1 = (int)a;
cout << b1 <<" "<<&a<<" "<<&b1<< endl;//1  0014FD20   0014FD14  十六进制地址

cout << (int)&a << endl;//(int)&把a的地址强制转换成十进制的整型

cout << (int&)a << endl;//(int&)a:把a的引用强制转换为整型,意思是a所在的内存,本来定义的时候为float类型,并初始1.0f。但是现在要按照 int 类型解释这段内存。即:现在要按照int 型进行解释按float类型存储的数据。
//1.0f在内存里存储为:0011 1111 1000 0000 0000 0000 0000 0000 
//现在:1065353216

cout  << (float&)a<<endl;//float类型引用 1

cout << boolalpha << ((int)a == (int&)a) << endl;//fasle  因为1!=1065353216

float b = 0.0f;
cout << (int)b << endl;//0
cout << (int&)b << endl;//0
cout << boolalpha << ((int)b == (int&)b) << endl;//true 0==0

return 0;

}
c语言中FLOAT 是如何表示的
c语言中FLOAT 是如何表示的?尾数,阶码是如何在32位上安排的,即哪几位是
尾数,哪几位是阶码,那一位是符号位。听说与CPU有关,是真的吗?

在C++里,实数(float)是用四个字节即三十二位二进制位来存储的。其中

有1位符号位,8位指数位和23位有效数字位。实际上有效数字位是24位,因为第
一位有效数字总是“1”,不必存储。
有效数字位是一个二进制纯小数。8位指数位中第一位是符号位,这符号位和
一般的符号位不同,它用“1”代表正,用”0“代表负。整个实数的符号位用“
1”代表负,“0”代表正。
在这存储实数的四个字节中,将最高地址字节的最高位编号为31,最低地址
字节的最低位编号为0,则实数各个部分在这32个二进制位中的分布是这样的:3
1位是实数符号位,30位是指数符号位,29—23是指数位,22—0位是有效数字
位。注意第一位有效数字是不出现在内存中的,它总是“1”。

将一个实数转化为C++实数存储格式的步骤为: 
(1)先将这个实数的绝对值化为二进制格式,注意实数的整数部分和小数部

分化为二进制的方法是不同的。
(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第
一个有效数字的右边。
(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。
(4)如果实数是正的,则在第31位放入“0”,否则放入“1”。
(5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右
移得到的或n=0,则第30位放入“0”。
(6)如果n是左移得到的,则将n减去一然后化为二进制,并在左边加“0”
补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在
左边加“0”补足七位,再各位求反,再放入第29到第23位。

将一个计算机里存储的实数格式转化为通常的十进制的格式的方法如下: 
(1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十

四位有效数字。将小数点点在最左边那个“1”的右边。
(2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30
位是“1”时将n增1。
(3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时)
,得到一个二进制表示的实数。
(4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正
号或负号即可。

特别地,实数0用C++的float格式表示是0000000000000000000000000000000

0。

如果还不太明白,这里举几个例子。
一。将23.56化为C++的float格式。
(1)将23.56化为二进制后大约是“10111.1000111101011100001”。
(2)将小数点左移四位,得到“1.01111000111101011100001”。
(3)这已经有了二十四位有效数字,将最左边一位“1”去掉,得到“0111
1000111101011100001”。将它放入第22到第0位。
(4)因为23.56是正数,因此在第31位放入“0”。
(5)由于我们把小数点左移,因此在第30位放入“1”。
(6)因为我们是把小数点左移4位,因此将4减去1得3,化为二进制,并补足
七位得到0000011,放入第29到第23位。
完毕。
如果把最左边定为第31位,最右边定为第0位,那么在C++里,float格式的2
3.56是这样表示的:01000001101111000111101011100001。相应地-23.56就是这
样表示的:11000001101111000111101011100001。

二。将实数0.2356化为C++的float格式。 
(1)将0.2356化为二进制后大约是0.00111100010100000100100000。 
(2)将小数点右移三位得到1.11100010100000100100000。 
(3)从小数点右边数出二十三位有效数字,即11100010100000100100000放

入第22到第0位。
(4)由于0.2356是正的,所以在第31位放入“0”。
(5)由于我们把小数点右移了,所以在第30位放入“0”。
(6)因为小数点被右移了3位,所以将3化为二进制,在左边补“0”补足七
位,得到0000011,各位取反,得到1111100,放入第29到第23位。
完毕。因此0.2356用C++的float格式表示是:00111110011100010100000100
100000。其中最左边一位是第31位,最右边一位是第0位。

三。将实数1.0化为C++的float格式。 
(1)将1.0化为二进制后是1.00000000000000000000000。 
(2)这时不用移动小数点了,这就是我们在转化方法里说的n=0的情况。 
(3)将小数点右边的二十三位有效数字00000000000000000000000放入第22

到第0位。
(4)因为1.0是正的,所以在第31位里放入“0”。
(5)因为n=0,所以在第30位里放入“0”。
(6)因为n=0,所以将0补足七位得到0000000,各位求反得到1111111,放入
第29到第23位。
完毕。所以实数1.0用C++的float格式表示是:0011111110000000000000000
0000000。其中最左边一位是第31位,最右边一位是第0位。

这是IEEE短实数格式,适合X86cpu。 

补充其他:

原码:

如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,其中最高位为符号位:正数为0,负数为1。剩下的n-1位表示概数的绝对值。

例如: X=+101011 , [X]原= 00101011 X=-101011 , [X]原= 10101011
位数不够的用0补全。

PS:正数的原、反、补码都一样:0的原码跟反码都有两个,因为这里0被分为+0和-0。

反码:

知道了什么是原码,那反码就更是张飞吃豆芽——小菜一碟了。知道了原码,那么你只需要具备区分0跟1的能力就可以轻松求出反码,为什么呢?因为反码就是在原码的基础上,符号位不变其他位按位取反(就是0变1,1变0)就可以了。

例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100

补码:

补码也非常的简单就是在反码的基础上按照正常的加法运算加1。

例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100,[X]补=11010101

PS:0的补码是唯一的,如果机器字长为8那么[0]补=00000000。

移码:

移码最简单了,不管正负数,只要将其补码的符号位取反即可。

例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100,[X]补=11010101,[X]移=01010101

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

在二进制科学表示法中,S=M*2^N 主要由三部分构成:符号位+阶码(N)+尾数(M)。对于float型数据,其二进制有32位,其中符号位1位,阶码8位,尾数23位;对于double型数据,其二进制为64位,符号位1位,阶码11位,尾数52位。

            31        30-23       22-0

float 符号位 阶码 尾数

            63        62-52       51-0

double 符号位 阶码 尾数

符号位:0表示正,1表示负

阶码:这里阶码采用移码表示,对于float型数据其规定偏置量为127,阶码有正有负,对于8位二进制,则其表示范围为-128-127,double型规定为1023,其表示范围为-1024-1023。比如对于float型数据,若阶码的真实值为2,则加上127后为129,其阶码表示形式为10000010

尾数:有效数字位,即部分二进制位(小数点后面的二进制位),因为规定M的整数部分恒为1,所以这个1就不进行存储了。

下面举例说明:

float型数据125.5转换为标准浮点格式

125二进制表示形式为1111101,小数部分表示为二进制为 1,则125.5二进制表示为1111101.1,由于规定尾数的整数部分恒为1,则表示为1.1111011*2^6,阶码为6,加上127为133,则表示为10000101,而对于尾数将整数部分1去掉,为1111011,在其后面补0使其位数达到23位,则为11110110000000000000000

则其二进制表示形式为

0 10000101 11110110000000000000000,则在内存中存放方式为:

00000000 低地址

00000000

11111011

01000010 高地址

而反过来若要根据二进制形式求算浮点数如0 10000101 11110110000000000000000

由于符号为为0,则为正数。阶码为133-127=6,尾数为11110110000000000000000,则其真实尾数为1.1111011。所以其大小为

1.1111011*26,将小数点右移6位,得到1111101.1,而1111101的十进制为125,0.1的十进制为1*2(-1)=0.5,所以其大小为125.5。

同理若将float型数据0.5转换为二进制形式

0.5的二进制形式为0.1,由于规定正数部分必须为1,将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为

0 01111110 00000000000000000000000

由上分析可知float型数据最大表示范围为1.11111111111111111111111*2127=3.4*1038

对于double型数据情况类似,只不过其阶码为11位,偏置量为1023,尾数为52位。

<think>好的,用户想了解内联函数和宏定义的区别,特别是详细解释。首先,我需要回忆一下C++中内联函数和宏的基础知识,然后结合用户提供的引用资料来组织答案。 用户给出的引用资料中提到,内联函数在编译时展开,而宏在预处理阶段替换。这应该是一个关键点。另外,内联函数有类型检查,而宏没有,这也是重要区别。还有,宏可能导致副作用,比如参数多次求值,而内联函数作为函数则不会。用户提供的引用[2]和[4]详细讨论了这些区别,需要引用这些资料来支持解释。 接下来,我需要考虑如何结构化这个回答。可能的分点对比会更清晰,比如从处理阶段、类型检查、语法、调试等方面展开。同时,用户可能需要知道为什么C++推荐使用内联函数替代宏,尤其是在类型安全和代码维护方面。 还要注意用户提到的宏不能访问类的私有成员,这时候内联函数更适合,比如类的getter和setter方法。这一点需要提到,并引用相关资料中的例子。 然后,用户可能还想知道两者的适用场景,比如宏在条件编译中的使用,而内联函数适合短小且频繁调用的函数。同时,性能方面两者都能减少开销,但内联函数更安全。 最后,根据用户的要求,生成相关问题,可能涉及内联函数的优缺点、宏的潜在问题、如何选择两者等。确保问题覆盖用户可能进一步探索的方向。 在组织内容时,需要确保每个区别点都有对应的引用标注,比如处理阶段的不同引用[2][4],类型检查引用[2][4],语法限制引用[4],调试引用[2],访问权限引用[2][4],作用域引用[4]。 另外,用户示例中的代码可能不需要,但需要明确说明内联函数和宏的代码示例差异,比如宏的括号问题和内联函数的结构。比如引用[2]中的宏需要加括号而内联函数不需要,引用[5]中的示例说明内联函数如何展开。 最后检查是否覆盖了所有用户提供的引用内容,并正确引用。确保回答结构清晰,分点明确,并且自然融入引用标注。</think>### 内联函数 vs 宏定义 区别详解 #### 1. **处理阶段不同** - **宏定义**:在**预处理阶段**进行**文本替换**,直接替换代码中的宏名称。例如: ```cpp #define MAX(a,b) ((a) > (b) ? (a) : (b)) ``` 调用时会被替换为表达式,可能导致重复计算或副作用[^2][^4]。 - **内联函数**:在**编译阶段**将函数代码**嵌入调用位置**,保留函数结构(如类型检查),例如: ```cpp inline int max(int a, int b) { return a > b ? a : b; } ``` 编译后调用处直接展开为逻辑代码,而非简单文本替换[^2][^5]。 #### 2. **类型检查与语法限制** - **宏定义**:**无类型检查**,仅做文本替换。例如: ```cpp #define SQUARE(x) x*x SQUARE("abc") // 编译通过但逻辑错误 ``` 可能导致隐式错误,且编写时需手动添加括号避免优先级问题[^4]。 - **内联函数**:**有类型检查**,需符合函数语法。例如: ```cpp inline int square(int x) { return x*x; } square("abc"); // 编译报错 ``` 编译器会拒绝类型不匹配的调用,安全性更高[^2][^4]。 #### 3. **调试与维护** - **宏定义**:**不支持调试**(预处理后宏名称消失),且复杂宏难以维护。例如: ```cpp #define PRINT_IF_EVEN(x) if(x%2==0) cout<<x; ``` 若调用时出现逻辑错误,调试困难[^2][^4]。 - **内联函数**:可像普通函数调试,支持断点跟踪,代码可读性更好[^4]。 #### 4. **作用域与访问权限** - **宏定义**:**无作用域限制**,全局生效,可能引发命名冲突。例如: ```cpp #define private public // 破坏封装性 ``` - **内联函数**:遵循类作用域,可访问私有成员。例如: ```cpp class A { private: int value; public: inline int getValue() { return value; } // 合法访问私有成员 }; ``` 适用于封装性要求高的场景[^2][^4]。 #### 5. **性能与适用场景** - **共同点**:均能减少函数调用开销(无栈帧操作)。 - **宏定义**:适用于**简单文本替换**或**条件编译**(如`#ifdef`)[^2][^4]。 - **内联函数**:适用于**短小且频繁调用**的函数(如数学运算、getter/setter),兼顾效率与安全性[^3][^5]。 --- ### 对比表格 | **特性** | **宏定义** | **内联函数** | |------------------|-------------------------------|-------------------------------| | 处理阶段 | 预处理阶段(文本替换) | 编译阶段(代码嵌入) | | 类型检查 | 无 | 有 | | 调试支持 | 不可调试 | 可调试 | | 语法限制 | 需手动处理优先级(加括号) | 符合函数语法 | | 作用域 | 全局生效 | 遵循类/函数作用域 | | 访问私有成员 | 不支持 | 支持 | | 适用场景 | 简单替换、条件编译 | 短小高频函数、封装操作 | --- ### 总结 内联函数通过**类型安全**、**可调试性**和**语法规范**,解决了宏定义的主要缺陷,成为C++中替代宏的推荐方案[^1][^4]。但宏在条件编译等场景仍有不可替代性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值