C基本语法


软件开发概览

学习的标准

思维缜密 逻辑清晰 知识点能够通俗易懂的复述和编码

软件开发的分类

按照职责的不同,需求量比较大的常见的软件开发类型有:

  • 移动开发(Andriod、IOS、Harmony)

    • Andriod:Java、Kotlin
    • IOS:Objective-C、Swift
  • 前端开发(HTML、CSS、JavaScript、TypeScript)

  • 后台开发(Java、C++、PHP、.NET、Python、Go)

  • 嵌入式开发(C、C++、汇编)

  • ……

软件开发的职责分工

C语言简介

电子计算机的发展史

第1代:电子管数字机(1946 - 1958年)

第2代:晶体管数字机(1958 - 1964年)

第3代:集成电路数字机(1946 - 1970年)

第4代:大规模集成电路机(1970年至今)

电路的逻辑状态只有0和1两个状态,0表示低电平,1表示低电平

因此,当代的电子计算机只能识别0和1

注意:计算机除了电子计算机,还有光子计算机、量子计算机、生物计算机、纳米计算机等

C语言的发展简史

C语言于1972年诞生于美国AT&T公司的贝尔实验室

  • 由Dennis MacAlistair Ritchie(丹尼斯.里奇)发明,被称为是C语言之父
  • C语言之所以命名为C,是因为它以B语言为基础发展而来
  • B语言由Kenneth Thompson(肯.汤普森)发明

Thompson和Ritchie用C语言完全重写了UNIX操作系统(以前是用汇编语言)

  • 随着UNIX的发展,C语言也得到了不断的完善

C语言的标准

为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会(美国国家标准协会,ANSI)

  • 1989年,诞生了第一个完备的C标准,简称C89,也就是ANSI C

1990年,ANSI C被国际标准化组织ISO采纳,C语言在ISO有了一个官方名称ISO/IEC 9889:1990

  • 9899是C语言在ISO标准中的代号,C++在ISO标准中的代号是14882
  • 冒号后面的1990表示当前修订好的版本是在1990年发布的
  • 对于ISO/IEC 9889:1990,有人称为C90或C89,是最初的C语言国际标准

1999年,正式发布ISO/IEC 9889:1999,简称为C99标准

2011年,正式发布ISO/IEC 9889:2011,简称为C11标准

C语言的用途

C语言可以说是其他高级编程语言的老祖宗,历史悠久

  • 它的性能及其优越,在很多领域是其他编程语言无法取代的

凡是对性能要求极高的领域,基本都少不了C语言

  • 操作系统开发(内核、驱动等,UNIX、Linux等著名操作系统就是利用C语言编写的)
  • 数据库开发
  • 高性能服务器开发
  • 嵌入式开发
  • 游戏开发
  • ……

开发工具

记事本

  • 功能单一、体验差、易出错、开发效率低

IDE(Integrated Development Environment): 集成开发环境

  • 智能提示、高亮识别、语法检测、开发效率高(功能强大到超乎想象)
  • 常见的C语言IDE有:Visual Studio、QT Creator、CLion、Dev C++

基本概念

程序的结构

任何一个C语言程序都有一个或多个函数(Function)构成

  • 每个函数都有自己的功能

所以,以后编写的C语言代码,基本都是写在函数中

在有些编程语言中(例如Java),函数也叫做”方法(Method)”

main函数

每一个函数都有自己的名称

每一个函数的名称都是唯一的

默认情况下,C语言程序的运行入口点是main函数(翻译为:主函数,没有main函数,C语言程序是无法运行的

语法须知

C语言源代码(Source Code)文件的文件扩展名是.c

每一条语句(Stasement)后面都要以分号;结尾,是语句结束的标志

括号都是成对出现的

C语言是区分大小写的(大小写敏感,case-sensitive)

代码中用到的符号必须都是英文符号(注释、字符、字符串等的内容除外)

代码风格

该换行就换行

该缩进就缩进(按Tab键缩进、按Shift + Tab键反缩进)

该留空格就留空格(一般就留一个空格)

编译、链接知识

编译:将C语言源代码文件编译成可重定位二进制目标文件(以.o或.obj作为扩展名),由编译器(complier)来完成

链接:将所有目标文件以及所需要的库文件合并成一个可执行文件(executable file),由链接器(linker)来完成

编译、链接的细节

常见的C语言编译器有(已经内置了链接器)

  • MSVC:微软出品(用在windows中)

  • GCC:GNU Compiler Collection的缩写,GNU出品

  • MinGW:Minimalist GNU for Windows的缩写 ,GNU出品(用在Windows中)

  • LLVM:常用于苹果的开发工具中

  • ……

对于同一份源代码,经过不同的编译器编译出来的目标文件(体积、格式、运行效率等)是不一样的

对于不同平台(操作系统),最后链接产生的可执行文件格式也是不同的

  • Windows:PE格式(经常以.exe作为文件扩展名)
  • Linux:ELF格式
  • Mac:Mach - O格式

注释

什么是注释

  • 注释常用来解释某段代码的具体含义、作用
  • 注释并不会被当做正常代码进行编译
  • 注释再很多IDE中的默认颜色都是偏绿色

C语言的注释有两种书写风格

  • 多行注释(也被称为C风格注释)
  • 单行注释(C99开始有,也被称为C++风格注释)

注释的嵌套

  • 单行注释可以嵌套单行注释、多行注释
  • 多行注释不能嵌套多行注释

多写注释的好处

  • 方便回忆、检查代码
  • 方便程序员之间的团队协作、提高开发效率
  • 方便旧项目的交接

注释常用技巧

  • 用来检验功能
  • 用来定位BUG

变量(Variable)

变量的作用

可以存储程序运行中会变化的数据

如何声明(declare)一个变量

  • 变量类型 变量名;

  • 变量类型决定了变量能够存储什么类型的数据

如何给变量赋值

  • 赋值(assign): 将数据交给变量去存储
  • 变量名 = 数据;
  • 这个等号 = 表示赋值,会将右边的数据赋值给左边的变量

在变量声明完毕后,可以直接通过变量名访问,不用带上变量类型

变量的细节

  • 变量可以被多次赋值,新值会覆盖旧值

  • 变量的第一次赋值,一般叫做初始化(initialize)

  • 变量可以在声明的同时进行初始化

  • 变量在未被初始化之前,它的值是不确定的

  • 变量在使用之前必须要进行初始化

  • 可以同时声明多个同类型的变量

  • 可以将一个变量的值赋值给另一个变量

变量的作用域(scope)

变量的作用域:就是指变量的作用范围、有效使用范围

  • 从声明变量的那条语句开始,直到变量所在的大括号结束
int main()
{
    printf("%d",age1); //错误
    printf("%d",age2); //错误
    int age1 = 10;
    printf("%d",age1); //正确
    printf("%d",age2); //错误
    {
        int age2 = 20;
        printf("%d",age1); //正确
        printf("%d",age2); //正确
    }
    printf("%d",age1); //正确
    printf("%d",age2); //错误
    return 0;
}
变量的作用域细节
  • 在同一个作用域内,不允许有同名的变量
int main()
{
    //有歧义(二义性)
    int age = 10; //正确
    int age = 20; //错误
    return 0;
}
int test()
{
    int age = 30; //正确
    return 0;
}
int main()
{
    int age = 10;
    {
        int age = 20;
        {
       	   int age = 30;
           printf("3age = %d\n",age);
       }
        printf("2age = %d\n",age);
    }
    printf("1age = %d\n",age);   
    return 0;
}
//result:30 20 10

变量的存储

变量的内存地址是指首字节的内存地址,首字节是指地址值最小的那个字节

越晚定义的变量,内存地址越小

大小端模式

大小端模式:决定了多字节数据的字节存储顺序

大端模式(Big-endian):高低低高

  • 高字节放在低地址,低字节放在高地址

小端模式(Little-endian):高高低低

  • 高字节放在高地址,低字节放在低地址

不同CPU架构的模式不一样

  • 比如x86架构是小端模式
  • 有些CPU架构是大端模式
  • 目前比较常见的是小端模式
int类型的存储

字符的存储细节

计算机中的数据都是以二进制形式存储的,字符数据也不例外

  • 每一个自负都会被转化成对应的整数值进行存储

在1967年,美国发布了ASCII码表,里面规定了128个单字节字符对应的整数值(ASCII码值)

int i =97;
char c1 = 'a';
cahr c2 = 'A';

ASCII码表

ASCII,全称是American Standard Code for Information Interchang,译为”美国信息交换标准码”,是一种标准的单字节字符编码方案

  • 共128个字符,码值范围:0-127(也就是0x00-0x7F)
  • 有33个是控制字符或通信专用字符,码值范围:0-31、127
    • 控制字符:LF(换行)、DEL(删除)、BS(退格)等
    • 通信专用字符:SOH(文头)、EOT(文尾)、ACk(确认)等
  • 有95个可显示字符,码值范围:32-126
    • 48-57:十个阿拉伯数字(0~9)
    • 65-90:26个大写英文字母(A~Z)
    • 97-122:26个小写英文字母(a~z)
    • 其余为一些标点符号、运算符等

#include <stdio.h>
 
int main ( void ) 
{ 
    puts ( "Printable ASCII:" ) ; 
    for  ( int i =  32 ; i <  127 ;  ++ i )  { 
        putchar ( i ) ; 
        putchar ( i %  16  ==  15  ?  ' \n '  :  ' ' ) ; 
    } 
}
/*
Printable ASCII:
  ! " # $ % & ' ( ) * + , - . /
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O
P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o
p q r s t u v w x y z { | } ~
*/

字符串 (String)

用双引号包住的内容叫做:字符串(由若干个【字符】组成的一串数据)

和注释类似,字符串里的内容可以随便写

标识符(Identifier)

标识符:由开发者自定义的一些名称(比如变量名、函数名等)

标识符的命名规则大致如下

  • 不限长度
  • 可以使用数字、下划线、英文字母
  • 可以使用以\u及\U转义记号指定的Unicode字符(从C99开始)
  • 不能以数字开头
  • 不能使用关键字

标识符的命名规范(命名建议)

  • 尽量用正确的英文单词命名,见名知意

当名称中包含多个单词时

  • 小驼峰(第一个单词的首字母小写,其它单词的首字母大写)(mHandle)

  • 大驼峰(所有单词的首字母大写)

  • 用下划线连接

关键字(Keyword)

关键字,也叫做保留字(reserved word)

  • 是编程语言内部已经定义好的一些有特殊含义的符号

C语言的关键字如下所示

字面量(Literal)

字面量:直接写出来的一个固定值

int age = 20;
double height = 1.68;
char *hody = "code";
char cc = 'm';

上面代码中,等号 = 右边的固定值都叫做字面量

代码中的进制书写形式

C语言标准规定

  • 默认是十进制
  • 以0开头是八进制
  • 以0X或0x开头是十六进制

C语言标准并不支持二进制的书写形式

  • 不过有些编译器支持,比如GCC规定以0b或0B开头是二进制
int a = 20;
printf("八进制:0%o\n",a);
printf("十六进制:0x%x\n",a);

有符号数的二进制表示方法

int 类型属于有符号整数类型(signed integer types)

  • 可以表示正数、负数

有符号数的二进制有三种表示方法:原码、反码、补码

三种表示方法均有符号位和数值位两部分

  • 符号位:最高位作为符号位,用0表示”正”,用1表示”负”
  • 数值位:三种表示方法各不相同

正数的原码、反码、补码一致,负数则不一样,计算机用补码表示和存储数值

字符的使用细节

  • 可以将char类型当做整数类型来使用

数字整数、数字字符、数字字符串

int i = 9;
char c = '9';
char *s = "9";
//10
printf("%d\n",i + 1);
//58
prinft("%d\n",c + 1);
//4210689
printf("%d\n",s + 1);

中文、日文、韩文等非英文字符是如何存储在计算机中的

  • 首先,必然是以二进制的形式存储在计算机中
  • 其次,每个字符对应的二进制数值取决于具体的编码方案

GBK主要支持CJK字符(C指中国、J指日本、K指朝鲜),而UTF-8支持几乎世界上所有的文字字符

转义序列(Escape sequences)

转义序列,一般也叫做转义字符,是一些有特殊含义的字符

scanf函数

scanf函数的功能是:输入(input),读取数据(比如读取通过键盘输入的数据)

scanf函数开始执行后,会等待用户输入

  • 程序卡在scanf函数那里,不会执行scanf函数后面的代码
  • 当用户敲Enter键(回车键)时,表示输入完毕
  • 程序才会开始执行scanf函数后面的代码
循环中校验scanf输入
int n = 0;
while(n < 1 || n > 100)
{
    printf("请输入1-100之间的正整数:");
    scanf("%d",&n);
}
sancf函数 - 匹配的细节
  • 当中途有匹配失败时,将结束匹配

当尝试把输入的数字赋值给字符变量时

  • scanf把输入的数字当成是数字字符来处理
  • 并不会把输入的数字当成是字符的ASCII码值来处理
scanf函数 —— 空白字符

空白字符包括:空格(‘ ‘)、Tab(‘\t’)、Enter(‘\n’)

  • 如果在输入数据的开头,有一段任意长(长度 >= 0)连续空白字符,是不需要与格式化字符串中的字符进行匹配的

  • 在格式化字符串中,任意长(长度 >= 0)连续空白字符能匹配输入的任意长(长度 >= 0)连续空白字符

数据类型

C语言拥有丰富多彩的数据类型,可以分为4大类型

void类型

基本类型(basic types)

  • 字符类型(character types)
  • 有符号整数类型(signed integer types)
  • 无符号整数类型(unsigned integer types)
  • 浮点类型(floating types)

枚举类型(enumerate)

派生类型(derived types)

  • 数组类型(array types)
  • 结构体类型(structure types)
  • 联合体类型(union types)
  • 函数类型(function types)
  • 指针类型(pointer types)
  • 原子类型(atomic types)

基本类型(Basic Types)

有符号整数类型(signed integer types)

  • char (等价类型:signed char)
  • short(等价类型:signed short、short int、signed short int)
  • int(等价类型:signed int、signed)
  • long(等价类型:signed long、long int、signed long int)(C99起)
  • long long(等价类型:signed long long、long long int、signed long long int)(C99起)

无符号整数类型(unsigned integer types)

  • unsigned char
  • unsigned short(等价类型:unsigned short int)
  • unsigned int(等价类型:undigned)
  • undigned long(等价类型:unsigned long int)(C99起)
  • unsigned long long(等价类型:unsigned long long int)(C99起)
  • _Bool(C99起)

字符类型(character types)

char类型属于字符类型(character types)

一个char类型的变量占用一个字节的内存

  • 所以它只能存储一个单字节字符
  • 26个英文字母的大小写(az,AZ)、10个阿拉伯数字(0~9)等都是单字节字符

浮点类型(floating types)

  • float、duoble、long double

整数类型的大小

C标准规定

  • sizeof(long long) >= sizeof(long) >= sizeof(int) >= sizeof(short) >= sizeof(char) == 1

数据模型(Data Models)

  • LP32(2/4/4):int为16位、long、指针为32位
  • ILP32(4/4/4):int、long、指针均为32位
  • LLP64(4/4/8):int、long为32位,指针为64位(Win64 API)
  • LP64(4/4/8):int为32,long、指针为64位(Unix、类Unix系统(Linux、Mac OS X))

char、unsigned char区别

  • c1、c2变量在内存中存放的二进制数据是完全一样的
  • 对于同一份二进制数据,分别以有符号数形式、无符号数形式解读出来的含义可能是不一样的

整数的取值范围

char、unsigned char都只占用一个字节,能够存放的二进制数据范围都是【0b0000 0000,0b1111 1111】

  • 有n个二进制位的有符号数的取值范围是【-2^n-1^,2^n-1^ - 1】
  • 有n个二进制位的无符号数的取值范围是【0,2^n^ - 1 】

溢出

当出现溢出时,会优先保留低字节的数据,舍弃高字节的数据

所以在给取值范围小的变量赋值时,要注意防止数据溢出,否则,结果可能会跟预期不符合

无符号数和有符号数溢出时的钟表机制

浮点类型(Floating Types)

浮点类型可以用来表示小数(比如0.5),包括了float、double、long double类型

最常用浮点类型的是float和double,一般在数值后面加上f或者F表示是float类型的数值

float f = 1.45F;
double d = 1.89;
//1.450000 1.890000
printf("%f %f",f,d);

float:单精度(Single)浮点类型,占用32bit,可以保证精确到小数点后6位

  • 最小值大约是:-3.4 * 10^38^,最大值大约是:3.4 * 10^38^

double:双精度(Double)浮点类型,占用64bit,可以保证精确到小数点后15位

  • 最小值大约是:-1.8 * 10^308^,最大值大约是:1.8 * 10^308^

浮点类型的存储细节

浮点数在计算机中是按照IEEE 754标准存储的

printf中的转换格式指定符

printf中常用的转换格式指定符(conversion format specifier)如下所示

printf("zd\n",sizeof(int));

long long age = 10LL;
printf("%lld\n",age);

unsigend long no =8UL;
printf("lu%\n",no);

printf格式符细节

  • 用%%来显示一个%
  • 用%6d表示占用6个字符位置,默认靠右对齐
  • %-6d中的减号( - )表示靠左对齐
  • %+6d中的加号( + )表示显示正负号
  • %.2f表示四舍五入保留2位小数

类型转换

强制类型转换

类型1 v1 = xx;
类型2 v2 = (类型2) v1;
隐式类型转换

在进行一些算数运算时,小类型会被隐式转换成大类型

  • char < short < int < long < long long < float < double < long double

注意:任何小于int的整数类型,在运算时会隐式转换为int类型

char c = 97;
short s =10;
// 1 2
printf("%zd %zd\n",sizeof(c),sizeof(s));
// 4 4 
printf("%zd %zd\n",sizeof(+c),sizeof(-s));
// 4 4
printf("%zd %zd\n",sizeof(c + s),sizeof(c/s));
// 4
printf("%zd \n",sizeof('A'));

运算符(Operators)

C语言常用运算符,如下表所示

算术运算符(Arithemetic Operators)

  • 模运算符不能用在浮点数上
  • 模运算结果的正负性跟随运算符左边的操作数
printf("%d\n",10%-7);//3
printf("%d\n",-10%7);//-3
printf("%d\n",-10%-7);//-3

位运算符(Bitwise Operators)

位运算符,属于算数运算符,位运算符的运算数只能是整数

  • 位移运算符b的值必须为非负整数
  • 左移低位补0
  • 右移高位用符号位填充

按位逻辑运算符,也叫逐位逻辑运算符,留意下与位运算相关的赋值运算符即可

位运算的实践应用

  • 尽量使用位运算取代乘(*)、除(/)、模(%)运算,因为位运算效率比它们高

  • 用a & 1来判断a的奇偶性

int a = 30;
printf("%s",(a & 1) ? "奇数" : "偶数");
  • 不使用第三方变量交换两个整形变量的值
a = a + b; // a = a * b; // a = a - b; // a = a ^ b; //
b = a - b; // b = a / b; // b = a + b; // b = a ^ b; //
a = a - b; // a = a / b; // a = b - a; // a = a ^ b; //

前三种方法可能有溢出产生bug,而异或操作则不用担心,虽然省了空间,但时间增加了

按位亦或的特点

  • a ^ 0 = a
  • a ^ a = 0
  • a ^ b = b ^ a
  • (a ^ b) ^ c == a ^ (b ^ c) == (a ^ c) ^ b

运算符的优先级

当一个表达式中同时使用多个运算符时

  • 会根据运算符的优先级和结合性,来决定运算符的执行顺序
  • 优先级越高(优先级值越小,越先被执行)
  • 优先级一样,根据结合性决定执行顺序
  • 为了确保优先级和代码可读性,应该多使用小括号

赋值运算符(Assignment Operators)

自增/自减运算符

自增/自减运算符(Increment/Decrement Operators)包括了

  • 前缀(prefix)

    • 自增运算符:++a
    • 自减运算符:–a
  • 后缀(postfix)

    • 自增运算符:a++
    • 自减运算符:a–
    b = a++;
    //等效于下面3句代码
    tmp = a;
    a += 1;
    b = tmp;
int a = 1;
int b = a++ + a++ + a++ + a++;
printf("%d %d\n",a,b);
//MSVC: b = 1 + 1 + 1 + 1 = 4
//MinG: b = 1 + 2 + 3 + 4 = 10
//不容易理解,可读性差,结果具有不确定性
//不建议在实际开发中变编写此类代码

最大吞噬规则(maximal much)

  • 当多个运算符紧挨在一起时,编译器会按照最大吞噬规则去解析
int a = 1,b = 2;
int c = a +++ b;
// 2 2 3
printf("%d %d %d\n",a,b,c);
// 等价于int c = (a++) + b;

比较运算符(Comparison Operators)

  • 比较运算符,也称为关系运算符(Relational Operators)
  • 比较运算符的结果只可能是整数0和1

逻辑运算符(Logical Operators)

在C语言中,任何值都有真假性

  • 任何非0的值都为”真”(比如59、1、-17、6.4、0.1、1.0、”123”、’K’等)
  • 只有数值0才为”假”(比如0、0.0、’\0’等)

逻辑运算符的运算数可以是任何值,它的运算结果要么是真、要么是假

  • 运算结果为真,就返回整数1
  • 运算结果为假,就返回整数0
printf("%d\n",'0');// 1
printf("%d\n",'\0');// 0
printf("%d\n",!'666');// 0

在表示范围的时候,逻辑与(&&)是取交集(∩),逻辑或(||)是取并集(∪)

逻辑与的短路现象

a && b

  • 如果a为假,就不会再去执行代码b。因为不管b是真是假,运算结果都为假

  • 如果a为真,就还需要执行代码b。因为得知b是真是假,才知道运算结果

    int a = 0;
    int b = 0;
    printf("%d\n",a++ && ++b); //0
    printf("%d %d\n",a,b); // 1 0

逻辑或的短路现象

a || b

  • 如果a为真,就不会再去执行代码b。因为不管b是真是假,运算结果都为真

  • 如果a为假,就还需要执行代码b。因为得知b是真是假,才知道运算结果

    int a = 0;
    int b = 0;
    printf("%d\n",++a || b++); //0
    printf("%d %d\n",a,b); // 1 0

短路现象可以减少一些不必要的代码的执行,缩短了程序的执行时间

条件运算符(Conditional Operator)

  • 条件运算符,一般也叫作三目运算符、三元运算符
  • a ? b : c
    • 如果a为真,就返回b
    • 如果a为假,就返回c
//将整形变量a、b、c中的最大值打印出来
int a = 11, b = 22, c = 33;
// 33
printf("%d\n",(a > b) ? ((a > c) ? a : c)) : ((b > c) ? b : c));
/**
int a = 11, b = 22, c = 33;
int d = (a >b) ? a : b;
d = (c > d) ? c : d;
// 33
printf("%d\n",d);
**/
//如果字符变量c是小写字母,就转成大写字母;否则保持原样
char c = 'g';
c = (c >= 'a' %% c <= 'z') ? (c - 'a' + 'A') : c;
// G
printf("%c\n",c);
//有一个字符变量c,如果它是小写字母,就转成大写字母;如果它是大写字母,就转成小写字母;否则保持原样
char c = 'G';
int diff = 'a' - 'A';
c = (c >= 'a' && c <= 'z') ? (c - diff): ((c >= 'A' && c <= 'Z') ? (c + diff) : c); 
// g
printf("%c\n",c);

逗号运算符(Comma Operator)

(a, b, ……)

  • 从左到右依次执行表达式
  • 返回最后一个表达式的运算结果
int a = (11, 22, 33);
int b = (a += 5, a = a > 0 ? 10 : -10, a++);
// 11 10
printf("%d %d",a,b);

流程控制(Flow Control)

按照执行程序流程的不同,可以将平时编写的代码分成3大结构

  • 顺序结构:默认的流程结构,按照代码的书写顺序执行每一句代码

  • 选择结构:根据表达式的真假性,来决定执行哪一段代码

  • 循环结构:在表达式为真的情况下,重复执行某一段代码

选择结构

if语句

if语句陷阱

  • 表达式后面多加分号,变成空语句
  • 判断相等时 == 误写成赋值 =

if - else语句

if语句 vs if-else语句
// if else语句          
int score = 70;        
if(score >= 60)        
{                      
    printf("及格");     
}                      
else                   
{                     
    printf("不及格");   
}

// 多个if语句
int score = 70;
if(score >= 60)
{
    printf("及格");
}
if(score < 60)
{
    printf("不及格");
}

if-else语句能实现的功能,完全也可以用多个if实现,更推荐if-else的写法

  • if-else代码更简洁,省掉了不必要的”score < 60”的判断
  • if-else的代码执行效率更高,只需要进行1次表达式判断:”score >= 60”

else if语句

输入一个整数代表分数,根据分数输出等级(A-E)

  • A:90-100 ——B:80-89——C:70-79——D:60-69——E:0-59
//较复杂的写法
#include<stdio.h>
int main()
{
    int score;
    scanf("%d",&score);
    
    if(90 <= score && score <= 100)
    {
        printf("A");
    }
    else if(80 <= score && score <= 89)
    {
        printf("B");
    }
    else if(70 <= score && score <= 79)
    {
        printf("C");
    }
    else if(60 <= score && score <= 69)
    {
        printf("D");
    }
    else if(0 <= score && score <= 60)
    {
        printf("E");
    }
    else
    {
        printf("非法输入");
    }
    
}

//更简单的写法
#include<stdio.h>
int main()
{
    int score;
    scanf("%d",&score);
    
    if(score < 0 || score > 100)
    {
        printf("非法输入");
    }
    else if(score >= 90)
    {
        printf("A");
    }
    else if(score >= 80)
    {
        printf("B");
    }
    else if(score >= 70)
    {
        printf("C");
    }
    else if(score >= 60)
    {
        printf("D");
    }
    else
    {
        printf("E");
    }
    
}

else-if语句注意点

  • else if中在对合法输入作限定后,应该将小范围写在前面,大范围写在后面
  • 更推荐下面简单的写法,省掉了不必要的判断,代码执行效率更高
if语句 vs else if语句
//下面两段代码的功能是不一样的
//只有其中一个printf会被执行
int price = 200;
if(++price >= 100)//[100,+∞)
{
    printf("太贵了\n");
}
else if(++price >= 50)//[50,100]
{
    printf("还行吧\n");
}
else if(++price >= 10)//[10,50]
{
    printf("很便宜\n");
}

//有可能三个printf都会被执行
int price = 200;
if(++price >= 100)//[100,+∞)
{
    printf("太贵了\n");
}
if(++price >= 50)//[50,+∞)
{
    printf("还行吧\n");
}
if(++price >= 10)//[10,+∞)
{
    printf("很便宜\n");
}

当多段代码中,只需要选择其中一段来执行时,建议用else if语句

int value = 9;
if(value > 0)
{
    printf("正数");
}
else if(value < 0)
{
    printf("负数");
}
else
{
    printf("0");
}

当多段代码可能都要被执行时,不能用else if语句

int value = 9;
if(value > 0)
{
    printf("正数");
}
if(value & 1)
{
    printf("奇数");
}
  • if、else后可以省略大括号,只会跟其后的第一条语句进行关联
  • 多个if出现,else后会跟其前面最接近的if相关联

if语句 ——编译错误

int price = 200;
if(price > 100)
{
    printf("太贵了\n");
}
printf("买不起\n"); //夹在if与else中间 编译错误
else
{
    printf("不算贵\n");
}
//注意if和else语句中变量的作用域
int a = 10;
if(a > 0)
{
    int b = a++;
}
else
{
    int c = a + b;//编译错误
}
printf("%d",a);
printf("%d",b);//编译错误
printf("%c",c);//编译错误
if-else语句 vs 条件运算符

有一些简单的if-else语句,可以使用条件运算符来替代,以达到简化代码的目的

表达式的等价性

image-20210615004111109

switch语句

break语句

在switch语句中,break语句的作用:中止switch语句

如果case、default后面没有break语句,会出现”贯穿”现象

  • 执行完case、default中的代码后,会继续往下执行其他case、default中的代码
  • 直到遇到break语句或switch语句的结尾为止

//利用贯穿
switch(month)
{
    case 3:
    case 4:
    case 5:
        printf("春季");
        break;
}
case的使用注意
  • case后面紧跟的表达式不能带有变量
  • 如果想在case后面声明新的变量,那就必须加上大括号,否则编译报错

if和switch之间可以相互替代

if和switch的选择
  • 当表示一个范围时,建议使用if
  • 当判断一个变量是否等于一些固定值时,if、switch均可使用

循环结构

很多时候,使用while、do-while、for可以完成一样的功能,只是书写格式不一样

在实际开发中,使用频率最多的是for、while

while语句

while固定次数的打印

//1 - 2 + 3 - 4 + 5 - 6 ... + n
int value  = 1;
int sum = 0;
while(value <= n)
{
    sum += (value & 1) ? value : -value;
	value++;
}    

do-while语句

int n;
do{
   printf("请输入1-100之间的正整数:");
   scanf("%d",&n)
}while(n < 1 || n > 100);
printf("%d",n);

for语句

//请从小到大打印出3、5的前10个公倍数
for(int i = 0,count = 0; count < 10;i++)
{
    if((i % 3 == 0) && (i % 5 == 0))
    {
        printf("%d",i);
        count++;
    }
}
for 语句 vs while语句

break语句

  • break语句只用在while、do-while、for、switch语句中

  • break语句的作用:终止其所在的while、do-while、for、switch语句

  • 在有多重循环的情况下,break语句只能终止其所在的那一层循环语句

continue语句

  • continue语句只用在while、do-while、for语句中
  • continue语句的作用:跳过循环体的剩余部分
  • 在有多重循环的情况下,continue语句只能作用于其所在的那一层循环语句
  • 很多时候,巧用continue,可以减少大括号、缩进的数量

for (int i = 1,count = 0; count < 10; i++)
{
    if(i % 3 != 0 || i % 5 != 0) continue;
    printf("%d",i);
    count++;
}

goto语句

  • goto语句的作用:可以为所欲为地灵活跳转
  • goto语句使用起来非常灵活,在有些情况下,还能提高程序的效率
  • 但goto语句破坏了结构化的设计风格,容易造成代码执行流程的混乱,导致代码难以调试和维护

文章作者: Holy Chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Holy Chen !
  目录