float类型的比较
浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。
注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会“四舍五入”或者其他策略
如下代码:
#include <stdio.h> int main() { double f = 3.6; printf("%.50lf\n", f); return 0; }
所以因为精度损失的问题我们一定不可以简单的将两个浮点型使用==来进行比较相同
所以我们在比较两个浮点型相同时应当比较两者相减的误差
如下代码:
//那么两个浮点数该如何比较呢? //应该进行范围精度比较 //伪代码 if((x-y) > -精度 && (x-y) < 精度) { //TODO } //伪代码-简洁版 if(fabs(x-y) < 精度) { //fabs是浮点数求绝对值 使用时要包含头文件math.h //TODO } //精度: 自己设置?后面如果有需要,可以试试,通常是宏定义。 使用系统精度? //暂时推荐 #include<float.h> //使用下面两个精度,需要包含该头文件 DBL_EPSILON //double 最小精度 FLT_EPSILON //float 最小精度
指针和零值的比较
真实转换和强制类型转换
真实的转换会改变数据存储数值如想要把"123456" ->123456 想把字符串类型转换成整形类型的数据时需要自己写算法或者使用函数来完成
强制类型转换是只转换类型内部储存的数据不变
而我们指针的零值NULL其实就是对应指针类型的0而已
我们之所以要使用NULL来代替0是为了程序员的阅读体验
void类型
void类型
首先void类型是不能直接定义变量的(因为变量是为了开辟空间void类型无空间开辟,所以编译器干脆不让通过)
void可以修饰函数或者函数参数修饰函数时表示函数无返回值,修饰函数参数时表示函数无参数(被void修饰的函数参数当你再次传参时编译器会报错或警告)
void*类型指针
void*可以直接定义变量因为指针类型开辟内存大小固定
void*类型的指针不能直接使用(毕竟指针类型决定了指针的跳跃能力,void*类型跳跃能力为0一般在强转后再使用
void*可以接收任意类型的指针可以设置成通用接口.譬如在接收函数参数使用void*类型
return关键字
我们在删除数据时并没有将数据清空而只是将数据无效化了而已
return 用于终结函数并返回函数后面的值
值得注意的是我们return的返回值一般是不能返回栈空间内变量的(栈空间内主要是临时变量)返回后一般被摧毁
return;这样写也是可以的他只做终结函数这一个功能哪怕是void类型修饰的函数也可以使用return;还终结函数
const修饰
修饰函数参数
修饰函数返回类型
修饰普通变量(依旧可以通过指针进行改变但是也告诉了其他程序员这个是不可修饰变量)
修饰数组(定义或说明他是只读变量)
修饰指针(有多情况)
const int *p //p指向的内容不可变
int const *p //p指向的内容不可变
int *const p //p的值不可变
volatile
(这个volatile关键字很少用的)
功能:防止内存变量被优化.(让变量一次次从内存中读取)
如果是仅看字面意思还是很迷惑人的,但是他的功能真的仅仅是防止修饰变量被优化.
那么这个鬼关键字有啥用呢?
(PS:这个有关多线程(你不懂其实我也不懂)的知识)
在多线程的情况下我们的变量值有可能会被改变
比如以下代码
#include<stdio.h> int main() { int flag = 1; while (flag); return 0; }
一个简单的死循环,这时我们的变量flag很大的概率是会被我们的编译器优化的.优化后的我们的编译器就不会一次次的从内存中读取flag的变量值再进行判断了.
而是直接将flag的值放到寄存器中,或者直接不再读取flag的值而是直接进行一次次的循环运算.(对应两种优化方式)
这时我们的flag值如果发生了改变如变成0,可是我们的代码依旧在进行死循环式的运行就会出现bug.