《c语言修炼内功之第二种境界(看代码就是内存)之关键字系列四》

  • 前言:这个系列内容我会深入讲解一下c语言中的重点内容,会把每一个知识点讲的更加底层些,会增强大家的c语言内功,从内存维度看代码你会有不同的理解。
  • 这一节我会更深入的剖析一下语句的知识(当然这里过于基础的就不讲了可以翻看前面的文章),比如if,else,switch等等,还会讲一些比如bool类型,float类型,指针类型与0比较等。这一讲结论比较多,需要特别细致的讲解内容很少,主要记住一些代码规范并能够在以后写代码或者刷题时候应用到其中就可以,我们接下来开始讲解吧。

目录

深入理解ifelse语句执行过程

bool类型?

bool 变量与“零值”进行比较

float 变量与“零值”进行比较

指针变量与“零值”进行比较

0,NULL,'0'的区别

强制类型转化的理解

else 到底与哪个if配对呢?

常见if语句错误及好的编码习惯

switch语句的一些编码规则

总结switch语句


深入理解ifelse语句执行过程

我们先来看一段代码

#include
int main()
{
	int flag = 1;
	if (1==flag)
	{
		printf("hello,csdn\n");
	}
	else
	{
		printf("hello,C\n");
	}
	return 0;
}

我们在刚开始并没有细致的理解if语句到底是逐怎么执行的,刚开始我会这么理解如果flag==1的话就执行if否则的话就执行else。这样理解的却没错但是他真正的过程又是怎么执行的?怎样回答才是正确的呢?

第一步:if语句执行()表达式的结果或者是函数的结果。

第二步:执行if的判断功能(在c语言中是0为假,非0为真)也就是判断()表达式的真假(或者是说bool值)。

第三步:根据判断的真假结果来决定分支功能(也就是具体执行if还是else的语句)。

bool类型?

c语言中到底有没有bool类型呢?

标准回答:c89,c90没有但最新的c99引入_bool类型,以宏定义的方式定义true和false;

我们还要注意的是我们在vs中也能写出BOOL,TRUE,FALSE也就是大写的,这其实是微软自己设计的不推荐大家使用,因为用大写的bool类型只能在vs相关编译器才可以,到其他编译器就不可以了,所以可移植性太差不推荐。

bool 变量与“零值”进行比较

这里直接给结论吧由于一些c语言的版本差异就不在推导了

#include  
#include 
int main() 
{ 
	int pass = 0; //0表示假,C90,我们习惯用int表示bool //bool pass = false; //C99 
	if (pass == 0){ //理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐 
	//TODO 
	}
	if (pass == false){ //不推荐,尽管在C99中也可行 
		//TODO 
	}
	if (pass)
	{ //推荐 //TODO
	}
		//理论上可行,但此时的pass是应该被当做bool看待的,==用来进行整数比较,不推荐 
	 //另外,非0为真,但是非0有多个,这里也不一定是完全正确的 
	if (pass != 1)
	{ //TODO 
	}
	if (pass != true){ //不推荐,尽管在C99中也可行 
					   //TODO 
	}
	if (!pass){ //推荐 
				//TODO 
	}
	return 0; 
}

我们记住下面两个编码习惯:

if (pass)
	{ //推荐 //TODO
	}
if (!pass){ //推荐 
				//TODO 
	}

我们使用bool类型直接判定就可以,不需要任何的操作符和特定值比较,原因是我们if语句在判断表达式中的真或者假也就是逻辑判断结果正好与bool中的true和false相吻合。

float 变量与“零值”进行比较

我们先来引入一下:

#include  
int main() 
{
	double x = 3.6;
	printf("%.50f\n", x); 
	return 0;
}

由这个结果就可以显示我们的浮点数会出现精度损失。

那我们float是怎么与0比较呢?会不会也出现精度损失呢?

#include 
int main() 
{
	double x = 1.0; 
	double y = 0.1;
	printf("%.50f\n", x - 0.9);
	printf("%.50f\n", y); 
	if ((x - 0.9) == y)
	{
		printf("you can see me!\n"); 
	}
	else
	{
		printf("oops\n"); 
	}
	return 0;
}

《c语言修炼内功之第二种境界(看代码就是内存)之关键字系列四》_第1张图片

所以根据第一行结果和第二行结果能看出浮点数会有精度损失,而由于精度损失就会导致我们输出oops,有这些结论可知我们肯定就不可以用==来比较浮点数,那我们该用什么呢?

由于浮点数在存储的时候就会有精度损失,导致各种结果出现细微差别。

又因为我们两个浮点数是否相等主要取决于这两个浮点数的差值是否在合法范围内,所以我们就可以用这种方法来比较浮点数的大小。

fabs(x-y) < 精度 

float 最小精度:FLT_EPSILON 

double 最小精度 :DBL_EPSILON

#include 
#include  //必须包含math.h,要不然无法使用fabs
#include  //必须包含,要不然无法使用系统精度 
int main()
{
	double x = 1.0;
	double y = 0.1;
	printf("%.50f\n", x - 0.9); 
	printf("%.50f\n", y); 
	if (fabs((x - 0.9) - y) < DBL_EPSILON)//原始数据是浮点数,我们就用DBL_EPSILON 
	{
		printf("you can see me!\n");
	}
	else{
		printf("oops\n"); 
	}
	system("pause");
	return 0; 
}

 这里我们还会出现一个问题?

fabs(x) <= DBL_EPSILON这个到底需不需要写等号呢?
答案是不需要的。
举个例子比如:x+ DBL_EPSILON并不等于x,但是x+0它是等于x的,这正好与加了等号产生矛盾。
如果=,就说明x本身,已经能够引起其他和他+-的数据本身的变化了,这个不符合0的概念。

指针变量与“零值”进行比较

这个也是编码规范问题,这个我们在指针那篇文章中野指针讲解了相关内容,也就是我们避免它是野指针就要验证指针的有效性也就是避免野指针。

我们一般要写

if(NULL!=p)
if(NULL==p)

0,NULL,'0'的区别

  • 从数值上三者都是0,但是类型所表达的含义不一样
  • 0就是数字0,可以表示int等等数据类型
  • NULL的类型是(void*)
  • '0'它是由ascii码值转化的

强制类型转化的理解

这里我们可以打一个比方,我跟你说道上那个人长得真吓人满脸胡须,脸相邪恶他一定是一个换人,然后我过几天又跟你说其实我们误会他了,它其实是一个大好人,我那时候看见他帮助警察抓小偷。

从这个例子我们就可以那个人从一个坏人形象——》变成好人形象,但是从始至终一直都是这个人,这个人不变。这也就跟我们的强制类型转化类似不改变内存中的数据,只改变对应类型,在计算机底层中并没有改变,强制类型转化只是在对应的场景充当不同的角色。而真正的转化是彻彻底底的改变,改变内存中的数据。

总结:强制类型转化不改变内存中的数据,只改变对应类型,真正的转化是彻彻底底的改变,改变内存中的数据

else 到底与哪个if配对呢?

#include
int main() 
{
	int x = 0;
	int y = 1;
	if (0 == x)
		if (11 == y)
			printf("hello bit\n"); 
	else
			printf("hello world!\n");
	return 0;
}

答案会是什么呢?

《c语言修炼内功之第二种境界(看代码就是内存)之关键字系列四》_第2张图片

所以得出结论:

当else对应着多个if的时候,else就与最近的if匹配。 

 在今后我们应该注意这样的编码习惯,与你想象不符的。

我们建议在写if语句的时候一定要带上花括号

常见if语句错误及好的编码习惯

if 语句后面的分号:
if 语句的后面并不需要分号,但如果你不小心写了个分号, 编译器并不会提示出错。因为编译器会把这个分号解析成一条空语句
在编写代码是,要使得正常情况的执行代码清晰,确认那些不常发生的异常情况处理
代码不会遮掩正常的执行路径。这样对于代码的可读性和性能都很重要
确保 if else 子句没有弄反。

switch语句的一些编码规则

  • 每个 case 语句的结尾绝对不要忘了加 break,否则将导致多个分支重叠(除非 有意使多个分支重叠)。
  • 最后必须使用 default 分支。即使程序真的不需要 default 处理,也应该保留语句: default : break;
  • case 关键字后面的值有什么要求吗?
记住: case 后面只能是整型或字符型的常量或常量表达式(想想字符型数据在内存里
是怎么存的
  • case 语句的排列顺序

按字母或数字顺序排列各条 case 语句。把正常情况放在前面,而把异常情况放在后面按执行频率排列 case 语句

  • 使用 case 语句的其他注意事项
  1. 简化每种情况对应的操作不要为了使用 case 语句而刻意制造一个变量
  2. default 子句只用于检查真正的默认情况

总结switch语句

  • switch语句结构中case完成判定功能,break完成分支功能,default决定处理异常情况。

case在执行语句时 

  • 一个case执行多条语句时,case多条语句不能定义变量,如果需要可以用{}和函数最后加上break;
  • 多条case执行一条语句时,多条case不用写break;

default可以出现在任何地方,一般推荐放在结尾

case后面只能跟整形常量表达式,不能是变量也不能是const修饰的常变量。

你可能感兴趣的:(c语言深度解剖学习笔记,c语言,开发语言,后端)