C语言复习第一天(下篇)
浮点数的秘密
浮点数在内存中存储的形式:符号位、指数、尾数
类型 | 符号 | 指数 | 尾数 |
---|---|---|---|
float | 1位(第31位) | 8位(第23~30位 | 23位 (第0~22)位 |
double | 1位(第63位) | 11位(第52~62)位 | 52位(第0~51)位 |
float类型和double类型在计算机内存中的表示方法是一致的,但是由于内存大小不同,所以double类型的精度会比float类型的精度高。
浮点数的转换
1、浮点数成二进制
2、用科学计数法表示二进制数
3、计算指数偏移量之后的值
注意计算指数时需要加上偏移量,而偏移量的值与类型有关
对于指数6偏移后的值如下:
float :127+6 -->130
double: 1023+6 -->1029
举例:8.25在内存中如何表示(三步走):
1、8.25 ----->1000.01
2、1000.01 ----->1.00001 * 2^3
3、指数偏移 127+3=130 =》
最后存储:符号位 0
指数位 1000 0010
尾数位 0000 1000 0000 0000 0000 000
合并起来转换成十六进制数为 —>0x41040000
代码举例查看浮点数在二进制中存储的数值:
#include <stdio.h>
int main()
{
float f=8.25; //4 word
unsigned int * pf=(unsigned int *)(&f);
printf("0x%x\n",*pf);
}
代码运行结果
深入思考:int类型和float类型的差异
int类型可表示范围:2^-31 ~ 2^31-1
float类型可表示的范围: -3.4*10^38 ~ 3.4*10^38
tips:为什么int类型和float类型都是四字节类型,然后float可表示的范围却比int类型大的多呢?
答:因为浮点数是不精确的数,它在内存中的表现形式并不是连续的数,
代码举例:
#include <stdio.h>
int main()
{
float f = 3.1415f;
float fl = 123456789;
printf("%0.10f\n", f);
printf("%0.10f\n", fl);
return 0;
}
然而输出的结果却是:
由此可见float类型并不精确。
小结:
1、浮点数类型在内存中表示的方法与整形不同
2、浮点数类型的内存表示法更加复杂
3、float类型和int类型能表示的数的个数是一样的
4、float可表示的数之间不是连续,存在间隙。
tips: 由于内存表示法不一样,浮点型的运算复杂度比整形高。
类型转换
C语言中的数据类型可以进行类型转化
1、强制类型转换
2、隐式类型转换
强制类型转换
强制类型转换的语法:
1、<type>var_name
2、<type>value
强制类型转换的结果:
目标类型能搞容纳目标值-----结果不变
目标类型不能容纳目标值-----结果产生截断
tips 不是所有类型转换都能成功,当不能进行类型转换是,编译器将报错。
代码举例
#include <stdio.h>
struct TS
{
int i;
int j;
};
struct TS ts;
int main()
{
short s = 0x1122;
char c = (char)s; // 0x22
int i = (int)s; // 0x00001122
int j = (int)3.1415; // 3
unsigned int p = (unsigned int)&ts;
long l = (long)ts; // error
ts = (struct TS)l; // error
printf("s = %x\n", s);
printf("c = %x\n", c);
printf("i = %x\n", i);
printf("j = %x\n", j);
printf("p = %x\n", p);
printf("&ts = %p\n", &ts);
return 0;
}
编译结果
提示 :1、自定义结构类型不可强转成基本数据类型
2、基本数据类型也不可强转成自定义结构类型
隐式类型转换
编译器主动进行的类型转换:
低类型类型转换成高类型-----结果不变
高类型转换成低类型-----结果产生截断
隐式类型转换发生的发生点
tips
short类型和char类型结合隐式转换成int 类型
代码举例:
#include <stdio.h>
int main()
{
char c = 'a';
int i = c; // safe
unsigned int j = 0x11223344;
short s = j; // unsafe
printf("c = %c\n", c);
printf("i = %d\n", i);
printf("j = %x\n", j);
printf("s = %x\n", s);
printf("sizeof(c + s) = %d\n", sizeof(c + s));
//注意:short类型和char类型结合隐式转换成int 类型
return 0;
}
小结:
1.强制类型转换
1、强制类型转换由程序员完成
2、强制类型转换不一定成功,编译器会报错
3、类型不区分高低,可能产生截断
2 隐式类型转换
低转高 ---> 安全
高转低 ---> 不安全 ,长生截断
变量的属性
1、C语言变量可以有自己的属性
2、在变量定义前可以加上属性名称
3、每个属性都有自己的特定的含义
auto
1、auto为C语言的默认属性。
2、声明自动变量,由编译器自动生成并分内存。
3、生成的变量保存在==栈区==。
register
1、register变量声明将局部变量存储与计算机寄存器中
2、只是尝试申请,但不一定成功。
3、存放的值,必须是寄存器可以接受的值。
4、不能用 & 去获得寄存器变量的地址。
5、为什么需要这个属性?
因为在寄存器中访问变量比在内存中快的多。
#include <stdio.h>
int main()
{
resigter int j=0;
printf("%d",&j);
return 0;
}
static
1、static 修饰的变量存储在程序静态区
2 、static修饰的变量同样具有作用域限定的作用
1:static定义在全局的变量,作用域就在此文件
2: 函数内staic定义的变量,作用域就在此函数
代码举例:
#include <stdio.h>
int f1()
{
int r = 0;
r++;
return r;
}
int f2()
{
static int r = 0;
r++;
return r;
}
int main()
{
auto int i = 0; // 显示声明 auto 属性,i 为栈变量
static int k = 0; // 局部变量 k 的存储区位于静态区,作用域位于 main 中
register int j = 0; // 向编译器申请将 j 存储于寄存器中
printf("%p\n", &i);
printf("%p\n", &k);
printf("%p\n", &j); // error
for(i=0; i<5; i++)
{
printf("%d\n", f1());
}
for(i=0; i<5; i++)
{
printf("%d\n", f2());
}
return 0;
}
代码分析:
在程序没有关闭之前,static变量不会重复定义,因为存储在静态区会保存上一次的值。
extern 关键字
1、用来声明外部的函数或变量。
1.告诉编译器变量在其他地方已经分配内存
2.告诉编译器函数已经在其他地方定义
3。静态不能extern 会出现错误
2、extern C用来告诉编译器以C的方式编译程序
程序举例:
//g.c
int g_i=15;
int getI()
{
return g_i;
}
//main.c
#include <stdio.h>
extern int getI();
extern int g_i;
int main()
{
printf("g_i=%d\n", g_i);
printf("getI()=%d\n", getI());
return 0;
}