发布时间:2023-08-28 11:00
1.6 signed、unsigned 关键字
规则:单纯的char 类型应该只用于字符值的存储和使用;有符号和无符号的 ”char“ 型变量只能用于数值的存储和使用。
char 有三种不同的类型:单纯 char 、signed char 及 unsigned char 。signed char 和 unsigned char 类型是用来声明数值的; 单纯 char 类型是真正的字符类型, 是用来声明字符的。单纯 char 类型有编译器环境决定,不能依赖。对于单纯 char 型,唯一允许的操作是赋值和相同运算符(=、==、!=)。
3个问题:
1、-0 和 +0 在内存里分别是怎么存储的?
答:由于在计算机中数值的存储都是以补码的形式存储的,所以-0 和 +0 在内存中都是按0000 0000存储的(如果是char类型)。
2、int i = -20; unsigned j = 10; i+j的值为多少?为什么?
答:如若按 %d 输出为 -10;按 %u 输出为一个很大的值。i + j 在这里做了隐式的转换
隐式转换各数据类型的级别高低。
隐式转换规则如下:
1. 程式在执行算术运算时,低类型能够转换为高类型。
2. 在赋值表达式中,右边表达式的值自动隐式转换为左边变量的类型,并赋值给他。
3. 当在函数调用时,将实参值赋给形参,系统隐式地将实参转换为形参的类型后,赋给形参。
4. 函数有返回值时,系统将自动地将返回表达式类型转换为函数类型后,赋值给调用函数。
3、下面代码有什么问题?
unsigned i; for(i = 9; i >= 0; i--) { printf("%u \n", i);
答:输出的值为极大的值;主要原因是在输出时 %u 将 9 这个数值由 int 型转换为 unsigned 类型,编译器会把它当作一个很大的值。}
1.7 if 、else 组合
a.对于bool类型的比较:FLASE都是0 TRUE不一定是1 所以应该用if(bool_num); if(!bool_num);对于浮点型与0比较要是否注意:不能直接比较,要定义精度,其实浮点型与浮点型比较也要注意这个问题,就是不能直接比较,要设定精度,如图:原因跟浮点型的存储格式有关,因为float的有效位是6位,超出6位就未知了,所以不能直接进行比较。同样的原因,也不能用一个很大的浮点数去加一个很小的浮点数。这个加法可能体现不出来。b.对于if后面的分号问题 ,一定要注意, 会被解析成if后面有一个空语句, 所以使用空语句的时候最好使用NULL;c.在使用if else的时候,应该if里面先处理正常情况(出现概率大的情况),else里面处理异常情况,这是一个好习惯看着代码舒服。d.指针变量与”零值“比较: int *p = NULL; if(NULL == p); if(NULL != p)。
1.8 switch、case 组合
if、else一般表示两个分支或是嵌套比较少量的分支,但如果分支比较多的话,就得用switch、case组合。
【注意】
【一点】:每个case语句的结尾绝对不要忘了加break,否则将导致多个分支重叠(除非你有意这样做)
【二点】:最后必须使用default分支。即使你的程序真的不需要default处理,也应该保留这个语句。这样做并非画蛇添足,可以避免让人误解为你忘了default处理。
【三点】:case关键字后面的值有什么要求吗?记住:case后面只能是整型或字符型的常量或常量表达式(实际上都是整型)。
【四点】:case语句的排列顺序。 当case语句非常多的时候,就需要考虑case语句的排列顺序了。规则一:按字母或数字顺序排列各条case语句;规则二:把正常情况放在前面,而异常情况放在后面。规则三:把执行频率高的语句放在前面,也方便调试。
【五点】:简化每种情况对应的操作。一般,case语句后面的代码尽量不要超过20行。
【六点】:不要为了使用case语句而刻意制造一个变量。
【七点】:把default子句只用于检查真正地默认情况。
1.9 do、while、for关键字
C语言中循环语句有三种:while循环、do-while循环和for循环。
思考:在switch case语句中,能否使用continue关键字?为什么?
答:不能。switch不是循环语句。
continue语句一般形式为"continue;"
其作用为结束本次循环。即跳出循环体中下面尚未执行的语句,对于while循环,继续求解循环条件。而对于for循环程序流程接着求解for语句头中的第三个部分expression表达式。
continue语句的作用是跳过循环本中剩余的语句,并到循环末尾。
continue语句只用在for、while、do-while等循环体中, 常与if条件语句一起使用, 用来加速循环。
关键字 区别 break 表示:终止本层循环 continue 表示:终止本次循环(即:本轮循环终止,进入下一轮循环)
【注意】
【建议1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。
【建议2】最好不要在for循环体内修改循环变量,防止循环失控。
【建议3】循环要尽可能的短,要使代码清晰,一目了然。
【建议4】把循环嵌套控制在3层以内。
请禁止使用goto语句。
【缺点一】它会破坏程序的结构化设计风格。
【缺点二】经常带来错误或隐患,它可能跳过了变量的初始化、重要的计算等语句。
1.11 void关键字
void是空类型
void *是空类型指针,它可以指向任何类型的数据。
void真正发挥作用在:(1) 对函数返回的限定。(2) 对函数参数的限定。
float *fp;
int *ip;
fp=(float*)ip;// 不同类型赋值时,需要进行强制类型转换
而void*则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换。
void *vp;
int *ip;
vp=ip;// ok,无需强制类型转换
但是,void*不能直接赋给其它类型的指针,必须进行强制类型转换。即,“空类型”可以包容“有类型”,而“有类型”则不能包容“空类型”。
void *vp;
int *ip;
ip=vp;// error, cannot convert from 'void *' to 'int *'
【注意】
【规则1】如果函数没有返回值,那么应声明为void类型。
在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。
【规则2】如果函数无参数,那么应声明其参数为void。
在C语言中,可以给无参数的函数传送任意类型的参数。
在C++中,不能向无参数的函数传送任何参数。
所以,无论在C还是在C++中,若函数不接受任何参数,一定要指明参数为void。
【规则3】千万小心又小心地使用void指针类型。
按照ANSI标准,不能对void指针进行算法操作。
之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。也就是说,必须知道内存目的地址的确切值。
但是,在GNU中则不这么认定。它指定void*的算法操作与char*一致。
【规则4】如果函数的参数可以是任意类型指针,那么应声明其参数为void*。
典型的如,内存操作函数memcpy和memset的函数原型分别为:
- void *memcpy(void *dest, const void *src, size_t len);
- void *memset(void *buffer, int c, size_t num);
- int IntArray_a[100];
- memset(IntArray_a, 0, 100*sizeof(int));// 将IntArray_a清0
- int destIntArray_a[100], srcIntArray_a[100];
- memcpy(destIntArray_a, srcIntArray_a, 100*sizeof(int));// 将src拷贝给dest
任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为,它操作的对象仅仅是一片内存,而不论这片内存是什么类型。
【规则5】void不能代表一个真实的变量。
void a;// 错误
function(void a);// 错误
因为定义变量时必须分配内存空间,定义void类型变量,编译器到底分配多大的内存呢?
void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,就很容易理解void数据类型了。正如,不能给抽象基类定义一个实例,我们也不能定义一个void变量。
(12) return
return用来终止一个函数并返回其后面跟着的值。
return (val);// 此括号可以省略,但一般不省略,尤其在返回一个表达式的值时。
【注意】
【规则1】return语句不可返回指向“栈内存”的指针,因为该内存在函数体结束时被自动销毁。