一.《第6课 - 分支语句》
1. if 语句分析
(1)if 语句用于根据条件选择执行语句
(2)else 不能独立存在且总是与它最近的 if 相匹配,else 语句后可以连接其它if语句
(3)if 语句中零值比较的注意点,这个考点频繁出现在C语言笔试中!!!
- bool类型 ==> bool型变量应该直接出现在条件中,不要进行比较
- 整型 ==> 变量和0值比较时,0值应该出现在比较符号左边 // 防止将 == 写成 =,那样条件永远不成立; 0放在左边,误写为 = 会编译报错
- float型 ==> 因为float是不精确表示,0.0在内存中的表示可能是与其相近的一个值,所以不能直接和0值比较,需要定义精度
2. switch语句分析
(1)switch语句经常用在单个条件多个分支的情形
(2)case语句后面必须要有break,否则会导致分支重叠,实际工作中很多的bug就是由于忘记加break导致的!
(3)default语句有必要加上,以处理特殊情况
(4)case语句中的值只能是整型或字符型
(5)case语句的排列顺序遵照下面的规则,这样代码更加易读
- 按字母或数字顺序排列各条语句
- 正常情况放在前面,异常情况放在后面
- default 语句只用于处理真正的默认情况
【if 和 switch使用示例】
#include <stdio.h>
void f1(int i)
{
if( i < 6 )
{
printf("Failed!\n");
}
else if( (6 <= i) && (i <= 8) )
{
printf("Good!\n");
}
else
{
printf("Perfect!\n");
}
}
void f2(char i)
{
switch(i)
{
case 'c':
printf("Compile\n");
break;
case 'd':
printf("Debug\n");
break;
case 'o':
printf("Object\n");
break;
case 'r':
printf("Run\n");
break;
default:
printf("Unknown\n");
break;
}
}
int main()
{
f1(5); // Failed!
f1(9); // Perfect!
f1(7); // Good!
f2('o'); // Object
f2('d'); // Debug
f2('e'); // Unknown
return 0;
}
if 和 switch有各自适用的场合,这样代码看起来逻辑更加清晰!但是if 和 switch 两者也能进行转换,将前面的代码进行转换......
【有趣的角色互换】
#include <stdio.h>
void f1(int i)
{
switch( i < 6 )
{
case 1:
printf("Failed!\n");
break;
default:
switch( (6 <= i) && (i <= 8) )
{
case 1:
printf("Good!\n");
break;
default:
printf("Perfect!\n");
break;
}
break;
}
}
void f2(char i)
{
if( 'c' == i )
{
printf("Compile\n");
}
else if( 'd' == i )
{
printf("Debug\n");
}
else if( 'o' == i )
{
printf("Object\n");
}
else if( 'r' == i )
{
printf("Run\n");
}
else
{
printf("Unknown\n");
}
}
int main()
{
f1(5); // Failed!
f1(9); // Perfect!
f1(7); // Good!
f2('o'); // Object
f2('d'); // Debug
f2('e'); // Unknown
return 0;
}
二.《第7课 - 循环语句》
1. 循环语句分析
(1)循环语句的基本工作方式
- 通过条件表达式判定是否执行循环体
- 条件表达式遵循 if 语句表达式的原则
(2)do、while、for的区别
- do 语句先执行后判断,循环体至少执行一次
- while 语句先判断后执行,循环体可能不被执行
- for 语句先判断后执行,相比while更简洁
2. 循环的回顾
【三种循环语句使用对比】
#include <stdio.h>
int f1(int n)
{
int ret = 0;
if( n > 0 )
{
do
{
ret += n;
n--;
}
while( n > 0 );
}
return ret;
}
int f2(int n)
{
int ret = 0;
while( n > 0 )
{
ret += n;
n--;
}
return ret;
}
int f3(int n)
{
int ret = 0;
int i = 0;
for(i=1; i<=n; i++)
{
ret += i;
}
return ret;
}
int main()
{
printf("%d\n", f1(100)); // 5050
printf("%d\n", f2(100)); // 5050
printf("%d\n", f3(100)); // 5050
return 0;
}
3. break和continue的区别
(1)break表示终止循环的执行,跳出整个循环
(2)continue表示终止本次循环,进入下次循环
【break和continue的区别】
#include <stdio.h>
void f1(int n)
{
int i = 0;
for(i=1; i<=n; i++)
{
if( (i % 2) == 0 )
{
break; // 跳出循环
}
printf("%d ", i);
}
printf("\n");
}
void f2(int n)
{
int i = 0;
for(i=1; i<=n; i++)
{
if( (i % 2) == 0 )
{
continue; // 终止本次循环,进入下次循环
}
printf("%d ", i);
}
printf("\n");
}
int main()
{
f1(10); // 1
f2(10); // 1 3 5 7 9
return 0;
}
【do...while(0)和break的妙用】
#include <stdio.h>
#include <malloc.h>
int func(int n)
{
int i = 0;
int ret = 0;
int* p = (int*)malloc(sizeof(int) * n);
do
{
if( NULL == p ) break; // 注意这里break的用法
if( n < 5 ) break;
if( n > 100) break;
for(i=0; i<n; i++)
{
p[i] = i;
printf("%d\n", p[i]);
}
ret = 1;
}while( 0 );
printf("free(p)\n");
free(p); // 在任何情况下,free(p)都会被执行,不会泄露内存
return ret;
}
int main()
{
if( func(4) )
{
printf("OK\n");
}
else
{
printf("ERROR\n");
}
return 0;
}
三.《第8课 - goto和void分析》
1. 遭人嫌弃的goto
(1)高手潜规则:禁用goto
(2)项目经验:程序质量与goto的出现次数成反比;c语言是一门结构化语言,goto会破坏C语言的结构化特性。
(3)最后的判决:将goto打入冷宫
【goto副作用分析】
#include <stdio.h>
#include <malloc.h>
void func(int n)
{
int* p = NULL;
if( n < 0 )
{
goto STATUS;
}
p = (int*)malloc(sizeof(int) * n); // 当 n<0 时不执行这行
STATUS:
p[0] = n; // n<0 时,p为野指针,会产生段错误
free(p);
}
int main()
{
printf("begin...\n");
printf("func(1)\n");
func(1);
printf("func(-1)\n");
func(-1);
printf("end...\n");
return 0;
}
2. void的意义
2.1 void修饰函数返回值和参数
(1)void 修饰函数返回值和参数是为了表示 "无"
(2)如果函数没有返回值,那么应该将其声明为void;否则编译器默认返回值类型为int
(3)如果函数没有参数,应该声明其参数为void;否则编译器默认函数可以传递任意多个参数
#include <stdio.h>
/*
// 若函数没有返回值,编译器默认返回值为int类型
// 若函数没有参数,编译器默认函数可以传递任意个参数
f()
{
}
*/
void f(void) // 函数没有返回值,不接受参数
{
}
int main()
{
// int i = f(1, 2, 3);
return 0;
}
2.2 不存在void变量
C语言没有定义void究竟是多大内存的别名,因此没有void的标尺,无法在内存中裁剪出void对应的变量(void只是一种抽象的类型)。
#include <stdio.h>
int main()
{
// void var; // error
// void array[5]; // error
void *pv; // 指针要么为4字节,要么为8字节,可以定义void类型的指针变量
return 0;
}
小贴士:
ANSI C:标准C语言的规范
扩展C:编译器厂商在ANSI C的基础上进行了扩充,像我们平时使用的gcc、VS编译器就是使用的扩展C
【void在ANSI C和扩展C中的区别】
#include <stdio.h>
int main()
{
printf("cc sizeof(void) = %zu\n", sizeof(void)); // 在ANSI C编译器中编译无法通过
// 在支持GNU标准的gcc编译器中编译通过,结果为1
return 0;
}
bcc32编译器(使用ANSI C标准) ==> 编译失败
gcc编译器 ==> 编译通过
2.3 void指针的意义
(1)C语言规定只有相同类型的指针才可以相互赋值
(2)void *指针作为左值用于接收任意类型的指针
(3)void *指针作为右值使用时需要进行强制类型转换
【通过void*实现MemSet函数】
#include <stdio.h>
void MemSet(void* src, int length, unsigned char n)
{
unsigned char* p = (unsigned char*)src;
int i = 0;
for(i=0; i<length; i++)
{
p[i] = n;
}
}
int main()
{
int a[5]; // char a[5] double a[5]
int i = 0;
MemSet(a, sizeof(a), 0);
for(i=0; i<5; i++)
{
printf("%d\n", a[i]);
}
return 0;
}