C语言基础篇

C语言

C语言的特点及入门

代码级别的跨平台:由于标准的存在,使得几乎同样的C代码可用于多种操作系统,如Windows,Dos,Unix等等,也适用于多种机型。

由于C语言允许直接访问物理地址,可以直接对硬件进行操作,因此它即具有高级语言的功能,又具有低级语言的许多功能,C语言可以用来写系统软件(比如操作系统,数据库,杀毒软件,防火墙,驱动,服务器)

C语言是一个有结构化的程序设计,具有变量作用域(variable scope)以及递归功能的过程式语言

C语言传递参数可以是值传递(pass by value)传递的是值,也可以传递指针(a pointer passed by value)传递的是地址。

C语言中,没有对象,不同的变量类型可以用结构体(struct)组合在一起,不是面向对象的

预编译处理(preprocessor),生成目标代码质量高,程序执行效率高

为什么主函数定义为int型

C99标准未发布前,一般把main函数定义为void,即无返回值类型,c99标准发布后,里面明确规定定义main时要定义成int型,原因是因为main函数是系统调用的,在main函数运行结束后,要返回一个值给操作系统,以此验证main函数是否执行正常,如果main返回值为0说明正常结束,否则说明程序运行出错,需要调用一些错误处理
(如内存分配失败,资源申请失败等)。

C89 中,main( ) 是可以接受的的经典巨著(C程序设计语言第二版)用的就是void main( );不过在最新的C99 标准中,只有以下两种定义方式是正确的:

int main( void )

*int main( int argc, char argv[] )

如果不需要从命令行中获取参数,请用int main(void);否则请用int main( int argc, char *argv[] )

main函数的返回值类型必须是int,这样返回值才能传递给程序的调用者(如操作系统)

如果main函数的最后没有写return语句的话,C99规定编译器要自动在生成的目标文件中(如exe文件)加入return 0;,表示程序正常退出不过,我还是建议大家最好在main函数的最后加上return语句,虽然没有这个必要,但这是一个好的习惯注意,vc6不会在目标文件中加入return 0;,大概是因为vc6是98年的产品,所以才不支持这个特性现在明白我为什么建议你最好加上return语句了吧!

格式化符号

1
2
3
4
5
6
7
8
9
10
11
12
13
%a,%A 读入一个浮点值(仅C99有效)   
%c 读入一个字符   
%d 读入十进制整数   
%i 读入十进制,八进制,十六进制整数   
%o 读入八进制整数   
%x,%X 读入十六进制整数   
%s 读入一个字符串,遇空格、制表符或换行符结束。   
%f,%F,%e,%E,%g,%G 用来输入实数,可以用小数形式或指数形式输入。   
%p 读入一个指针   
%u 读入一个无符号十进制整数   
%n 至此已读入值的等价字符数   
%[] 扫描字符集合   
%% 读%符号

第一个C语言程序

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>//引入头文件

//开发项目
//这是一个main函数,是程序的执行入口,即程序是从main函数开始执行
//main(){//函数体:即一条条语句}
int main(){//这是一个main函数,是程序的执行入口
//int 表示main函数返回值为int
//printf是一个函数,需要一个头文件才能使用
//printf是在<stdio.h>,需要引入该头文件
printf("helloWorld");
return 0;//返回代码为0
}

C程序运行机制

1.编辑:编写我们的c文件,就是源代码。

2.编译:将c程序翻译成目标文件(.obj)//在计算机底层执行

3.链接:将目标文件.obj + 库文件 生成可执行文件(.exe)//在计算机底层执行

4.运行:执行.exe文件,得到运行结果

源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables)

C语言编译过程详解

编译,链接和运行详解

什么是编译

有了C源文件,通过编译器将其编译成obj文件(目标文件)

如果程序没有错误,没有任何提示,但在Debug目录下会出现一个Hello.obj文件,该文件称为目标文件。

什么是链接

有了目标文件(.obj文件),通过链表程序将其和运行需要的c库文件链接成exe文件(可执行文件)

如果程序没有错误,没有任何提示,但在Debug目录下会出现一个Hello.exe文件,该文件称为可执行文件。

为什么需要链接库文件呢?因为我们的c程序中会使用c程序库的内容,比如<stdio.h> <stdlib.h> 中的函数printf() system()等等,这些函数不是程序员自己写的,而是c程序库中提供的,因此需要链接

你会发现链接后,生成的.exe文件,比obj大了很多。

什么是运行

有了可执行的exe文件,也称为可执行程序(二进制文件)

在控制台下可以直接运行exe文件

C程序开发注意事项

对修改后的hello.c源文件需要重新编译链接,生成新的exe文件后,再执行,才能生效。

C程序的主题结构说明

1
2
3
4
5
#include...
void main(){ //包括内容,称为函数体
语句1;
语句2;
}

C程序源文件以“c”为拓展名。

C程序的执行入口时main()函数

C语言严格区分大小写

C程序由一条条语句构成,每个语句以”;”结束

大括号都是成双成对出现的,却一不可

C常用的转义字符

\t:一个制表位,实现对齐的功能

\n:换行符

\\:一个\

\“:一个“

\‘:一个’

\r:一个回车 printf(“张无忌赵民周\r芷若小赵”); 输出结果是”芷若小赵民周”

学习编程最容易犯的错是语法错误,c语言要求必须按照语法规则编写代码。如果你的程序违反了语法规则,例如:忘记了分号,大括号,引号,或者拼错了单词,c编译器都会报语法错误,尝试着去看懂编译器报告的错误信息

C语言注释

介绍:用于注解说明解释程序的文字就是注释,注释提高了代码的阅读性;

注释是一个程序员必须要具有的良好编程习惯。将自己的思想通过注释先整理出来,再用代码实现

有单行注释和多行注释两种

1
2
3
4
//表示单行注释
/*
表示多行注释
*/

规范代码风格

正确的注释和注释风格:

如果注释的一个函数,可以使用多行注释

如果注释函数中的某一条语句,可以使用单行注释

正确的缩进和空白:

使用一次tab操作,实现缩进,默认整体向右移动,按shift+tab可以整体向左移

运算符习惯性的两边各加一个空格

有两种代码风格:

1
2
3
4
5
6
7
8
9
10
11
include<stdio.h>
void main()
{//行首风格
prinf("helloWorld");
return 0;
}

includ<stdio.h>
void main(){//行尾风格
prinf("helloWorld");
}

C语言标准库

C标准库是一组C内置函数,常量的头文件,比如<stdio.h>,<stdlib.h>,<math.h> 等等。这个标准库可以作为C程序员的参考手册

可参考C标准库 参考手册来查看。

程序实例:

1
2
3
4
5
6
7
#include<math.h>
#include<stdio.h>

int main(){
double res = pow(2.0, 3.0);
printf("res=%.2f\n",res);
}

C 标准库 – 参考手册 | 菜鸟教程 (runoob.com)

变量和常量

变量快速入门

一个程序就是一个世界,不论是使用哪种高级程序语言编写程序,变量都是其程序的基本组成单位,比如:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>

int main(){
int a = 1;//定义了一个整型变量,取名为a,并赋值为1(强数据类型语言)
int b = 3;//定义了一个整型变量,取名为b,并赋值为3
b = 89;//给变量赋值为89
printf("a=%d \n",a);//使用输出语句,把变量a值输出%d表示输出的形式为整数
printf("b=%d \n",b);//使用输出语句,把变量b值输出%d表示输出的形式为整数
return 0;
}

简单的原理示意图

1669448008921

变量使用的基本步骤

声明变量

int num;

赋值

num = 60;

使用

printf(“num=%d”,num);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>

int main(){
int num = 1;
double score = 2.3;
char gender = 'A';
char name[] = "字符串";//这里[]是固定的只能写在这里
//说明
//如果输出的整数 %d
// 如果输出的是小数 %f,如果希望保留小数点 %.2f
//如果输出的是字符 %c
//如果输出的是字符串 %s
//在输出不同数据时,对应的格式化的形式要对应起来
printf("num=%d score=%.2f gender=%c name=%s\n",num,score,gender,name);
return 0;
}

变量的注意事项

  1. 变量表示内存中的一个存储区域(不同的数据类型,占用的空间大小不一样)
  2. 该区域有自己的名称和类型
  3. 变量必须先声明,后使用
  4. 该区域的数据可以在同一类范围内不断变化
  5. 变量在同一个作用域内不能重名
  6. 变量三要素(变量名+值+数据类型),这一点请大家注意。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>

int main(){
//变量必须先声明 后使用
num = 10;//未定义标识符num
int num;
//该区域可以再同一类型范围不断变化
int num = 90;
num = 60;
num = 46;
num = 3.5;//会报错,从double转换到int,可能丢失数据
//不可以定义两个重名的变量
int num = 60;//会报错
//变量有三个要素,(变量名 num 变量值 60 数据类型 int)
}

变量的数据类型

每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间(使用字节多少表示)

注意:在c中,没有字符串类型,使用字符数组表示字符串

在不同的系统上,部分数据类型字节长度不一样,int 2或者4个字节

1669450090044

基本数据类型

整数类型

C语言的整数类型就是用于存放整数值的,比如10,30,22,55

char的数据在底层也是使用整数来存放的

类型 存储大小 值范围
char 1 字节 -128 到 127 或 0 到 255
unsigned char 1 字节 0 到 255
signed char 1 字节 -128 到 127
int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
long 4 字节 -2,147,483,648 到 2,147,483,647
unsigned long 4 字节 0 到 4,294,967,295
1
2
3
4
5
6
7
#include<stdio.h>

int main(){
int len = sizeof(int);
printf("int的字节数=%d\n",len);
return 0;
}

C语言基本数据类型C99

整型的使用细节

各种类型的存储带下与操作系统,系统位数和编译器有关,目前通用的以64为系统为主。

在实际工作中,c程序通常运行在linux/unix操作系统下,在考试中使用windows

Windows vc12 Linux gcc-5.3.1 Linux gcc-5.3.1 Compiler
win32 x64 i686 x86_64 target
1 1 1 char
1 1 1 unsigned char
2 2 2 short
2 2 2 unsigned short
4 4 4 int
4 4 4 unsigned int
4 4 8 long
4 4 8 unsigned long
4 4 4 float
8 8 8 double
4 4 8 long int
8 8 8 long long
8 12 16 long double

C语言的整数类型,分为有符号和无符号两种,默认是signed

C程序中整型常声明为int型,除非不足以表示大数,才使用long long

bit:计算机中最小的存储单位,byte(字节):计算机中基本存储单元。1bit = 8bit

【二进制再详细说,简单举例一个short和int】

short s = 3在内存中占用2个字节,一个字节有8个比特,就占用了16个bit

int i = 3在内存中占用4个字节,一个字节有8个比特,就占用了32个bit

浮点类型

C语言的浮点类型可以表示一个小数,比如123.4,6.8,0.12等等

浮点型的分类

类型 存储大小 值范围 精度
float 4字节 1.2E-38到3.4E+38 6位小数
double 8字节 2.3E-308到1.7E+308 15位小数

说明一下:

关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位

尾数部分可能丢失,造成精度损失

浮点数使用细节

浮点数常量默认为double型,声明float型常量时,须后加 f 或 F。

浮点型常量有两种表示形式

十进制数形式:如:5.12 512.0f .512(必须有小数点)

科学计数法形式:如:5.12e2,5.12E-2

通常情况下,应该使用double型,因为它比float型更精确

printf(“d1=%f”,d1); 在输出时,默认保留小数点6位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>

int main(){
//浮点型常量默认为double型,声明float型常量时,须后加f 或 F
float d1 = 1.1;//从double到float截断,是double
d1 = 1.1f;//1.1f就是float
double d3 = 1.3;//ok

double d4 = 5.12;
double d5 = .512;//等价0.512

double d6 = 5.12e2;//等价 5.12*(10^2) = 512
double d7 = 5.12e-2;//等价 5.12*(10^2) = 5.12 / 100 = 0.0512
//在输出时,如果%f默认保留小数点6位
printf("d1=%f d3=%f d4=%f d5=%f d6=%f d7=%f\n",d1,d3,d4,d5,d6,d7);
return 0;
}

字符类型

字符类型可以表示单个字符,字符类型是char,char是1个字节(可以存字母或者数字)多个字符称为字符串,在c语言中 使用char数组表示,数组不是基本数据类型,而是构造类型【数组我们后面讲解】

字符常量使用单引号括起来的单个字符。

c语言中还允许使用转义字符,\ 来将其后的字符转变为特殊字符型常量。例如:char c3 = ’\n’ ;

在c中,char的本质是一个整数,在输出时,是ascll码对应的字符。

可以直接给char赋一个整数,然后输出时,会按照对应的ascll字符输出【97】

char类型时可以进行运算的,相当于一个整数,因为它都有对应的unicode码。

ASCII码介绍(了解)

二进制 十进制 十六进制 字符/缩写 解释
00000000 0 00 NUL (NULL) 空字符
00000001 1 01 SOH (Start Of Headling) 标题开始
00000010 2 02 STX (Start Of Text) 正文开始
00000011 3 03 ETX (End Of Text) 正文结束
00000100 4 04 EOT (End Of Transmission) 传输结束
00000101 5 05 ENQ (Enquiry) 请求
00000110 6 06 ACK (Acknowledge) 回应/响应/收到通知
00000111 7 07 BEL (Bell) 响铃
00001000 8 08 BS (Backspace) 退格
00001001 9 09 HT (Horizontal Tab) 水平制表符
00001010 10 0A LF/NL(Line Feed/New Line) 换行键
00001011 11 0B VT (Vertical Tab) 垂直制表符
00001100 12 0C FF/NP (Form Feed/New Page) 换页键
00001101 13 0D CR (Carriage Return) 回车键
00001110 14 0E SO (Shift Out) 不用切换
00001111 15 0F SI (Shift In) 启用切换
00010000 16 10 DLE (Data Link Escape) 数据链路转义
00010001 17 11 DC1/XON (Device Control 1/Transmission On) 设备控制1/传输开始
00010010 18 12 DC2 (Device Control 2) 设备控制2
00010011 19 13 DC3/XOFF (Device Control 3/Transmission Off) 设备控制3/传输中断
00010100 20 14 DC4 (Device Control 4) 设备控制4
00010101 21 15 NAK (Negative Acknowledge) 无响应/非正常响应/拒绝接收
00010110 22 16 SYN (Synchronous Idle) 同步空闲
00010111 23 17 ETB (End of Transmission Block) 传输块结束/块传输终止
00011000 24 18 CAN (Cancel) 取消
00011001 25 19 EM (End of Medium) 已到介质末端/介质存储已满/介质中断
00011010 26 1A SUB (Substitute) 替补/替换
00011011 27 1B ESC (Escape) 逃离/取消
00011100 28 1C FS (File Separator) 文件分割符
00011101 29 1D GS (Group Separator) 组分隔符/分组符
00011110 30 1E RS (Record Separator) 记录分离符
00011111 31 1F US (Unit Separator) 单元分隔符
00100000 32 20 (Space) 空格
00100001 33 21 !
00100010 34 22
00100011 35 23 #
00100100 36 24 $
00100101 37 25 %
00100110 38 26 &
00100111 39 27
00101000 40 28 (
00101001 41 29 )
00101010 42 2A *
00101011 43 2B +
00101100 44 2C ,
00101101 45 2D -
00101110 46 2E .
00101111 47 2F /
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
00110011 51 33 3
00110100 52 34 4
00110101 53 35 5
00110110 54 36 6
00110111 55 37 7
00111000 56 38 8
00111001 57 39 9
00111010 58 3A :
00111011 59 3B ;
00111100 60 3C <
00111101 61 3D =
00111110 62 3E >
00111111 63 3F ?
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01000100 68 44 D
01000101 69 45 E
01000110 70 46 F
01000111 71 47 G
01001000 72 48 H
01001001 73 49 I
01001010 74 4A J
01001011 75 4B K
01001100 76 4C L
01001101 77 4D M
01001110 78 4E N
01001111 79 4F O
01010000 80 50 P
01010001 81 51 Q
01010010 82 52 R
01010011 83 53 S
01010100 84 54 T
01010101 85 55 U
01010110 86 56 V
01010111 87 57 W
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01011011 91 5B [
01011100 92 5C \
01011101 93 5D ]
01011110 94 5E ^
01011111 95 5F _
01100000 96 60 `
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01100100 100 64 d
01100101 101 65 e
01100110 102 66 f
01100111 103 67 g
01101000 104 68 h
01101001 105 69 i
01101010 106 6A j
01101011 107 6B k
01101100 108 6C l
01101101 109 6D m
01101110 110 6E n
01101111 111 6F o
01110000 112 70 p
01110001 113 71 q
01110010 114 72 r
01110011 115 73 s
01110100 116 74 t
01110101 117 75 u
01110110 118 76 v
01110111 119 77 w
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111011 123 7B {
01111100 124 7C |
01111101 125 7D }
01111110 126 7E ~
01111111 127 7F DEL (Delete) 删除

在计算机内部,所有数据都使用二进制表示。每一个二进制位(bit)有0和1两种状态,因此8个二进制位就可以组合出256种状态,这被称为一个字节(byte),一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111.

ASCII码,上个世纪60年代,美国指定了一套字符编码,对英语字符与二进制之间的关系,做了统一规定。这被称为ASCII码。ASCII码一共规定了127个字符的编码,比如空格”SPACE“是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定位0.

缺点:不能表示所有字符,相同的编码表示的字符不一样,比如130在法语编码中代表了e,在希伯来语编码中却代表了字母Gimel

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>

int main(){
char a1 = 'a';
char b2 = 'b';
char c3 = 97;//这时我们以%c输出时,就会安装ASCII编码表对应的97对应字符输出
printf("a1=%c b2=%c c3=%c\n",a1,b2,c3);
int num = b2 + 10;
printf("num=%d\n",num);
return 0;
}

字符类型本质探讨

字符型存储到计算机中,需要将字符对应的码值(整数)找出来存储:字符’a’ -> 码值(97) -> 二进制(1100001) -> 存储() 读取:二进制(1100001) -> 码值(97) -> 字符‘a’ -> 读取(显示)

字符和码值的对应关系是通过字符编码表决定的(是规定好)

布尔类型

在 C 语言标准(C89)没有定义布尔类型,所以 C 语言判断真假时以 0 为假,非 0 为真。所以我们通常使用逻辑变量的做法:

但这种做法不直观。所以我们可以借助 C 语言的宏定义:

所以在最新的 C 语言标准(C99)解决了布尔类型的问题。C99 提供了 _Bool 型,所以布尔类型可以声明为 _Bool flag。

_Bool 依然仍是整数类型,但与一般整型不同的是,_Bool 变量只能赋值为 0 或 1,非 0 的值都会被存储为 1。

C99还提供了一个头文件 <stdbool.h> 定义了 bool 代表 _Bool,true 代表 1,false 代表 0。只要导入 stdbool.h ,就能非常方便的操作布尔类型了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>

//宏定义
#define BOOL int
#define true 1
#define false 0

int main(){
int isPass = -1;
if(isPass){//0表示假,其他数表示真
printf("通过考试\n");
}


//可以使用宏定义来完成(这里大家先看一下,体会以下,后面会详细讲解)
//定义一个布尔变量
BOOL isOK = false;//等价int isOK = 1
if(isOK){
printf("ok可以\n");
}
return 0;
}

C99导入相关的库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
#include<stdbool.h>


int main(){
int n = 10;
int sum = 1;
bool flag = false;

int num = n;
while(!flag){
sum = sum * (num--);
//当num=1退出循环
if(num == 1){
flag = true;
}
}
printf("%d的叠层值为 %d \n",n,sum);
return 0;
}

C语言的布尔类型(bool) (runoob.com)

基本数据类型的转换

自动类型转换:当C程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换。

数据类型按精度(容量)大小排序为

1669465800076

数据类型自动转换表

1669466093484

自动转换的细节说明

有多种类型的数据混合运算时,系统首先自动将所有数据转换成激动最大的那种数据类型,然后再进行计算(如int型和short型运算时,先把short转成int型后再进行计算)

如果两种类型的字节数不同,转换成字节数大的类型,若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

再赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型,如果右边变量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

int main(void){
//举例1
char c1 = 'a';
int num1 = c1;//ok
double d1 = num1;//ok
printf("d1=%f\n",d1);//定义一个变量必须要使用,否则无法编译成功
//举例2
short s1 = 10;
int num2 = 20;
int num3 = s1 + num2;
printf("num3=%d\n",num3);
//举例3
float f1 = 1.1f;//ok
double d2 = 4.353424321;
f1 = d2;//出现精度损失
printf("f1=%.8f\n",f1);
return 0;
}

强制类型转换

将精度高的数据乐星转换为精度小的数据类型。使用时要加上强制转换符() ,但是可能造成精度降低或溢出,格外要注意。

格式如下:(类型名)表达式

这种强制类型转换操作并不改变操作数本身

1
2
3
4
5
6
7
8
#include<stdio.h>

int main(){
double d1 = 1.834;
int num = (int)d1;//精度损失,但是强制类型转换
printf("num=%d\nd1=%.3f\n",num,d1);//d1仍然是浮点数
return 0;
}

强制类型转换细节说明

当进行数据的从精度高—>精度低,就需要使用强制转换

强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

1
2
int x = (int)10*3.5+6*1.5;
int x = (int)(10*3.5+7*1.5);

指针入门

本身指针需要放在后面讲解,但是授课很快就会使用到指针的概念,因此先对指针做一个入门讲解(后面还会详细讲解的),简单的说指针表示指向一个地址,举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>

int main(){
//讲解指针的入门
int num = 1;
//num的地址是多少
//说明1,如果要输出一个变量的地址,使用格式是%p,在想要查看地址变量前加上&
printf("num的值=%d num地址=%p\n",num,&num);
//定义一个指针变量,指针
//int *表示类型为指针类型,ptr是一个 int *类型(ptr指向一个int类型变量地址的指针)
int *ptr = &num;
//指针变量,本身也有地址
printf("ptr的地址=%p,ptr存放的值是一个地址=%p,ptr指向最终的值=%d\n",&ptr,ptr,*ptr);
return 0;
}

1669471823308

指针的应用案例和细节

写一个程序,获取一个int变量的num的地址,并显示到终端

将num的地址赋给指针ptr,并通过ptr去修改num的值

并画出案例的内存布局图

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>

int main(){
int num = 3;
printf("num=%p\n",&num);
//注意指针的类型要和最终指向的变量类型要对上
int *ptr = &num;
*ptr = 5;
printf("ptr=%d,num=%d\n",*ptr,num);
return 0;
}

指针的细节

基本类型,都有对应的指针类型,形式为数据类型 *,比如int的对应的指针就是 int * ,float对应的指针类型就是 float *,以此类推。

此外还有指向数组的指针,指向结构体的指针,指向共用体的指针,(二级指针,多级指针)后面我们再讲到数组,结构体和共用体时,还会再次详细讲解。

值传递和地址传递

值传递:将变量指向的存储内容,再传递、赋值时,拷贝一份给接收变量。

地址传递也叫指针传递:如果是指针,就将指针变量存储的地址,传递给接送变量,如果是数组,就将数组的首地址传递给接收变量。

C语言传递参数(或者赋值)可以是值传递(pass by value),也可以传递指针(a pointer passed by value),传递指针也叫地址传递。

默认传递值的类型:基本数据类型(整数类型,小数类型,字符类型),结构体,共用体。

默认传递地址的类似:指针,数组

1
2
3
int num1 = 100;
int num2 = num1;//值传递
int num2 = 40;

1669521828450

1
2
3
4
5
int num = 100;
int *p = &num;//地址传递
int *p2 = p;
*p2 = 55;
printf("num=%d *p=%d *p2=%d\n",num,*p,*p2);//55,55,55

1669522612969

常量的基本介绍

常量是固定值,在程序执行期间不能改变,这些固定的值,又叫做字面量。

常量可以是任何的基本数据类型,比如整数常量,浮点常量,字符常量,或字符串字面值,也有枚举常量。

常量的值在定义后不能进行修改。

整数常量可以是十进制,八进制或十六进制的常量,前缀指定基数,0x或ox表示十六进制,0表示八进制,不带前缀则默认表示十进制。整数常量也可以带一个后缀,后缀是U和L的组合,U表示无符号整数(unsigned),L表示长整数(long),后缀可以是大写,也可以是小写,U和L的顺序任意

整数常量举例说明

85 十进制

0213 八进制

0x4b 十六进制

30 整数

30u 无符号整数

30l 长整数

30ul 无符号长整数

1
2
3
4
int main(){
int n1 = 0213; //八进制 对应十进制的139
int n2 = 0x4b; //十六进制 对应十进制的75
}

浮点常量

浮点常量由整数部分,小数点,小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

浮点常量举例说明

1
2
3
3.14159;	//double常量
314159E-5; //科学计数法
3.1f; //float常量

字符常量

字符常量是括在单引号中,例如,‘x’可以存储在char类型的变量中。字符常量可以是一个普通的字符(例如’x’),一个转义序列(例如’ \t ‘)

字符常量举例说明

1
2
3
4
'X';
'Y';
'\t';
'1';

字符串常量

字符串字面值或常量是括在双引号“”中的。一个字符串包含类似于字符常量的字符:普通的字符,转义字符和通用的字符。可以使用空格做分隔符,把一个很长的字符串常量进行分行

字符串常量的举例说明

1
2
3
4
"hello world";
"北京";
"hello \
world";//等价hello world

常量的定义

使用#define预处理器

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>

#define PI 3.14

int main(){
PI = 3.1415;//不能修改已经定义的常量
double area;
double r = 1.2;//半径
area = PI * r * r;
printf("面积:%.2f\n",area);
return 0;
}

使用const关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
//语法格式:coast 数据类型 常量名 = 常量值
const double PI = 3.14;
//const 是一个关键字,规定好,表示后面定义了一个常量
//PI是常量名,既是一个常量,常量值是3.14
int main(){
PI = 3.1415;//不能修改已经定义的常量
double area;
double r = 1.2;//半径
area = PI * r * r;
printf("面积:%.2f\n",area);
return 0;
}

const和define的区别

const定义的常数带类型,define不带类型

const是在编译,运行的时候起作用,而define是在编译的预处理阶段起作用

define只是简单的替换,没有类型检查。简单的字符串替换会导致边界效应

const常量可以进行调试的,define是不能进行调试的,主要是预处理阶段就已经替换掉了,调试的时候就没它了

const不能重定义,不可以定义两个一样的,而define通过undef取消某个符号的定义,再重新定义

define可以配合#ifdef,#ifndef,#endif来使用,可以让代码更加灵活,比如我们可以通过#define来启动或者关闭调试信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>

#define A 1
#define B A+3
#define C A/B*3

//const不能重定义
const double PI = 3.14;
//const double PI = 3.15;//报错
//通过undef取消定义
#define PI2 = 3.14
#undef PI2 //取消PI2的定义
#define PI2 = 3.145
int main(){
printf("c=%d",C);
//分析过程
//#define就是一个简单的替换
//C其实是A/A+3*3 = 10
//如果B定义的值是 (A+3)
//C其实是A/4*3 = 0;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>

//#define DEBUG

int main(){
#ifdef DEBUG //如果定义过DEBUG
printf("ok,调试信息\n");
#endif
#ifndef DEBUG //如果没有定义过DEBUG
printf("hello,其他信息\n");
#endif
return 0;
}

运算符和逻辑语句

运算符是一种特殊的符号,用以表示数据的运算,赋值和比较等。

算术运算符 (+,-,*,/,%)

赋值运算符 (= += -=)

关系运算符 (> >= < <= ==)

逻辑运算符 (&& || !)

位运算符 (& 按位与 | 按位或 ^ 按位异或 ~按位取反等等)

三元运算符 (表达式 ?表达式1 :表达式2)

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
自减运算符,整数值减少 1 A– 将得到 9
运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。
运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。
|| 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 (A || B) 为真。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 !(A && B) 为真。
运算符 描述 实例
& 按位与操作,按二进制位进行”与”运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; (A & B) 将得到 12,即为 0000 1100
| 按位或运算符,按二进制位进行”或”运算。运算规则:`0 0=0; 0
^ 异或运算符,按二进制位进行”异或”运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; (A ^ B) 将得到 49,即为 0011 0001
~ 取反运算符,按二进制位进行”取反”运算。运算规则:~1=-2; ~0=-1; (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<< 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 A >> 2 将得到 15,即为 0000 1111
运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。
运算符 描述 实例
sizeof() 返回变量的大小。 sizeof(a) 将返回 4,其中 a 是整数。
& 返回变量的地址。 &a; 将给出变量的实际地址。
* 指向一个变量。 *a; 将指向一个变量。
? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y

C 运算符 | 菜鸟教程 (runoob.com)

键盘输入语句

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。

步骤:写入头文件 include<stdio.h> , 使用scanf函数 , 使用适当的格式参数接收输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>

int main(){
//使用字符数组接收名字
char name[10] = "";
int age = 0;
double sal = 0.0;
char gender = ' ';
//提示用户输入信息
printf("请输入名字:");
scanf("%s",name);//表示接收一个字符串,存放到name字符数组中
printf("请输入年龄:");
scanf("%d",&age);//因为我们将得到输入存放到age变量指向地址,因此需要加&
printf("请输入薪水:");
scanf("%lf",&sal);//接收一个double时,格式参数%lf
printf("请输入性别<m/f>:");//在薪水输入完成后,回车符会被这个scanf所接收
scanf("%c",&gender);//这里是接收到了回车字符
scanf("%c",&gender);
//输出得到的信息
printf("name %s age %d sal %.2f gender %c\n",name,age,sal,gender);
return 0;
}

进制转换和位运算

对于整数,有四种表示方式

二进制:0,1,满二进一,C语言没有二进制常数的表示方法

十进制:0,9,满十进一

八进制:0,7,满8进一

十六进制:0-9,A-F,满十六进一,以0x开头表示,A-F表示10-15

进制转换

二进制转十进制

从最低位开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和。

1011 = 1 * 2 ^ 0 + 1 * 2 ^ 1 + 0 * 2 * 2 + 1 * 2 ^ 3 = 1 + 2 + 0 + 8 = 11

八进制转十进制

从最低位开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和。

0123 = 3 * 8 ^ 0 + 2 * 8 ^ 1 + 1 * 8 ^ 2 = 3 + 16 + 64 = 83

十六进制转十进制

从最低位开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和。

0x34A = 10 * 16 ^ 0 + 4 * 16 ^ 1 + 3 * 16 ^ 2 = 10 + 64 + 768 = 842

十进制转二进制

将该数不断除以2,直到商为0止,然后将每步得到的余数倒过来,就是对应的二进制。

56 = 56 / 2 = 28余0 / 2 = 14余0 / 2 = 7余0 / 2 = 3余1 % 2 = 1余1 % 2 = 0余1

倒过来只看余数就是 111000

十进制转八进制

将该数不断除以8,直到商为0止,然后将每步得到的余数倒过来,就是对应的八进制。

156 = 156 / 8 = 19余4 / 8 = 2余3 / 8 = 0余数2 = 234

十进制转十六进制

将该数不断除以16,直到商为0止,然后将每步得到的余数倒过来,就是对应的十六进制。

356 = 356 / 16 = 22余4 / 16 = 1余6 / 16 = 0余1 = 164

二进制转八进制

从最低位开始,将二进制数每三位一组,转成对应的八进制数即可。

011010101 011 = 3 010 = 2 101 = 5 结果是 325

二进制转十六进制

从最低位开始,将二进制数每四位一组,转成对应的八进制数即可。

11010101 1101 = 13 0101 = 5 结果是D5

八进制转二进制

将八进制数每一位,转成对应的一个3位的二进制数即可。

0237 = 00 + 10 + 011 + 111 = 10011111

十六进制转二进制

将十六进制数每一位,转成对应的一个4位的二进制数即可。

0x23B = 0010 + 0011 + 1011 = 1000111011

源码反码和补码

1
2
3
4
5
6
7
8
9
10
11
//一些测试题
1 >> 2 = ?;
-1 >> 2 = ?;
1 << 2 = ?;
-1 << 2 = ?;
~2 = ?;
2 & 3 = ?;
~-5 = ?;
13 & 7 = ?;
5 | 4 = ?;
-3 ^ 3 = ?;

网上对源码,反码,补码的解释过于复杂,我这里精简几句话:

对于有符号的而言:

  • 二进制的最高位是符号位:0表示整数,1表示负数
  • 正数的原码,反码,补码都一样(三码合一)
  • 负数的反码=它的源码符号位不变,其他位取反(0变1,1变0)
  • 负数的补码=他的反码+1,负数的反码=他的补码-1
  • 0的反码,补码都是0
  • 在计算机运算的时候,都是以补码的方式来运算的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
~2 = ?;
首先得到2的补码,2是正数,三码合一
00000010
取反之后是补码
11111101
将补码转源码 ,首先因为是负数,所以要先转反码
11111100
将反码转为源码
10000011 结果为 -3

2 & -3 = ?;
首先得到2的补码,2是正数,三码合一
00000010
得到-3的补码,首先要得到-3源码
10000011
将源码转反码
11111100
补码是反码+1
11111101
开始按位与运算
00000000 结果为 0

1 >> 2 = ?;
相当于 1 / 2 / 2 = 0
-1 >> 2 = ?
-1的源码和补码不一样
首先拿到-1的源码
10000001
将源码转反码
11111110
补码是反码+1
11111111
向右移动两位
11111111 补码
将上面的补码转成源码
反码是补码-1
11111110
补码转源码
10000001
结果是 -1

程序流程控制

在程序中,程序运行的刘崇控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句:

顺序控制

顺序控制是程序从上到下逐行的执行,中间没有任何判断和跳转。

分支控制

分支控制有选择分支的执行,分支控制有三种:

单分支,双分支,和多分支

单分支

1
2
3
4
if(条件表达式){
执行代码块;
}

当条件表达式为真(非0)时,就会实行{} 的代码,返回假(0)时,不会执行{} 的代码

双分支

1
2
3
4
5
6
if(条件表达式){
执行代码块1;
}else{
执行代码块2;
}

当条件表达式为真(非0)时,就会实行代码块1,否则实行代码块2

多分支

1
2
3
4
5
6
7
8
9
if(条件表达式1){
执行代码块1;
}else if(条件表达式2){
执行代码块2;
}
...else{
执行代码块n;
}

嵌套分支

1
2
3
4
5
6
7
8
9
10
if(){
if(){//被包含的可以是单分支,双分支,多分支,嵌套分支最多三层

}else(){

}
}else{

}

switch分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
switch(表达式){
case 常量1:
语句块1;
break;
case 常量2
语句块2;
break;
case 常量3:
语句块3:
break;
case 常量n:
语句块n;
break;
default:
default语句块;
break;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<stdio.h>
#include<math.h>

int main(){
char c1 = ' ';
printf("请输入一个字符:");
scanf("%c",&c1);
switch(c1){
case 97://不能是字符串,97就是ascii码的a
printf("今天星期一\n");
break;
case 'b'://case不能是变量
printf("今天星期二\n");
break;
case 'c':
printf("今天星期三\n");
break;
case 'd':
printf("今天星期四\n");
break;
default:
printf("今天星期日\n");
break;
}
printf("switch结束\n");
//2022年11月28日 11:20
return 0;
}

循环分支

就是让你的代码可以循环的执行

for循环

1
2
3
4
for(1循环变量初始化;2循环条件;4循环变量迭代){
3循环操作(多条语句);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<math.h>

int main(){
//定义循环变量
for(int i = 1;i <= 5;i++){
printf("你好,i=%d\n",i);
}
for(int i = 0,j = 1;j <= 5;i++,j++){
printf("i=%d,j=%d\n",i,j);
}
return 0;
}

While循环

1
2
3
4
5
6
循环变量初始化;
while(循环条件){
循环体(多条语句);
循环变量迭代;
}

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>

int main(){
int i = 1;//循环变量初始化
while(i <= 5){
printf("你好 i=%d\n",i);
i++;
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <string.h>
int main() {
//<string.h>中有一个strcmp函数,判断两个字符串是否相同
char name[10] = "";
while(strcmp(name,"exit") != 0){
printf("请输入名字:");
scanf("%s",name);
printf("您输入的名字是:%s\n",name);
}
printf("您退出了输入名字\n");
return 0;
}


doWhile循环

1
2
3
4
5
6
循环变量初始化;
do{
循环体(语句);
循环变量迭代;
}while(循环条件);

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>
int main() {
int i = 1;
int max = 5;
do{
printf("你好,i=%d\n",i);
i++;
}while(i <= max);
return 0;
}

多重循环控制

将一个循环放在另一个循环体内,就形成了嵌套循环,最多三层。

嵌套循环就是把内存循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可以结束外层的本次循环,开始下一次的循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
//九九乘法表
int main() {
for(int i = 1;i <= 9;i++){
for(int j = 1;j <= i;j++){
int t = i*j;
if(i == j){
printf("%d * %d = %d\n",i,j,t);
}else{
printf("%d * %d = %d\t",i,j,t);
}
}
}
}

空心菱形三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<stdio.h>
//空心菱形三角形
int main(){
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++){
for(int j = 0;j<n-i;j++){
printf(" ");
}
int oddIncrease = 0;//2*i-1 奇数递增
for(int j = 0; j < (oddIncrease = 2*i-1);j++){
if(j == 0 || j == oddIncrease-1){
printf("*");
}else{
printf(" ");
}
}
printf("\n");
}
for(int i = 1;i<=n-1;i++){
for(int j = 0;j<i;j++){
printf(" ");
}
int oddDecrease = 0;//2*(n-1-i)+1 奇数递减
for(int j = 0;j < (oddDecrease = 2*(n-1-i)+1);j++){
if(j == 0 || j == oddDecrease-1){
printf("*");
}else{
printf(" ");
}
}
printf("\n");
}
return 0;
}

跳转控制语句

break

break用于终止某个代码块的执行,一般使用在switch或者循环中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>

int main(){
//100以内的数求和,求出当和第一次大于20的当前数for
int i = 0;
for(int j = 1;j <= 100;j++){
if(i > 20){
break;
}
i+=j;
}
printf("当前的i值为:%d\n",i);
return 0;
}

实现简单的登录验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<stdio.h>
#include<string.h>

int main(){
//实现登录验证,有三次机会,如果用户名为"zero",密码为"2022"提示登录成功
//否则提示还有几次机会,请使用for循环完成
char account[10] = "";
char password[10] = "";

for(int i = 1;i<=3;i++){
printf("请输入您的账户名称:");
scanf("%s",account);
printf("请输入您的账户密码:");
scanf("%s",password);
if(strcmp("zero",account) == 0 && strcmp("2022",password) == 0){
printf("恭喜您登录成功!");
break;
}else{
printf("登录失败,您还有%d次机会\n",3-i);
}
}
printf("程序即将退出..\n");
return 0;
}

C 库函数 – strcmp() | 菜鸟教程 (runoob.com)

Continue

continue用于结束本次循环,继续执行下一次循环

continue不能单独在switch或if中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
#include<string.h>

int main(){
//从键盘中读取个数不确定的整数,并判断读入的正数和负数的个数.
//输入为0结束程序,并返回正负数个数
int positive = 0;
int nagative = 0;
int tempNum = 0;
while(1){
printf("请输入数字:");
scanf("%d",&tempNum);
if(tempNum > 0){
positive++;
continue;
}else if(tempNum < 0){
nagative++;
continue;
}else{
printf("退出输入");
break;
}
}
printf("正数为%d,负数为%d\n",positive,nagative);
return 0;
}

goto和return语句

goto语句可以无条件的转移到程序中指定的行。

goto语句通常与条件语句配合使用。可用来条件转义,跳出循环体等功能。

C程序设计中一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序都产生困难

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
#include<string.h>

int main(){
printf("start\n");
goto label1;
printf("ok1\n");
printf("ok2\n");
label1:
printf("ok3\n");
printf("ok4\n");
return 0;
}

return使用在函数,表示跳出所在的函数。

1
2
3
4
5
6
#include<stdio.h>
#include<string.h>

int main(){
return 0;//表示跳出主函数返回0
}

枚举

引出枚举

举个例子,比如:一星期有7天,请定义数据表示星期一到星期天

传统的方法

使用#define完成,这个代码量比较多,并且不好管理,在此引出了枚举

1
2
3
4
5
6
7
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUn 7

枚举介绍

枚举是C语言中的一种构造数据类型,它可以让数据更简介,更易懂,对于只有几个有限的特定数据,可以使用枚举。

枚举对应英文enumeration,简写enum

枚举是一组常量的集合,包含一组有限的特定的数据

枚举语法定义格式为

enum 枚举名{枚举元素1,枚举元素2,……};

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<string.h>

int main(){
enum DAY{
MON=1,TUE=2,WED=3,THU=4,FRI=5,SAT=6,SUN=7
};
enum DAY day;//day就是枚举类型,包含了7个枚举元素
day = WED;
printf("%d",day);
return 0;
}

枚举的遍历

C语言中,枚举类型是被当作int或者unsigned int类型来处理的,枚举类型必须连续是可以实现有条件的便利。以下实例使用for来遍历枚举的元素

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>

enum DAY{
MON=1,TUE,WED,THU,FRI,SAT,SUN //如果没有赋值,就会按照顺序赋值
}day;//表示定义了一个枚举类型,enum DAY,同时定义了一个变量 day,类型是enum DAY

int main(){
for(day = MON;day <= SUN;day++){//遍历枚举元素,枚举的每个元素都对应数值
printf("枚举元素:%d\n",day);
}
return 0;
}

枚举在switch中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

int main() {
enum SEASONS { SPRING = 1, SUMMER, AUTUMN, WINTER };
enum SEASONS season;
printf("请输入你喜欢的季节:(1.spring;2.summer;3.autumn;4.winter):");
scanf("%d", &season);
switch (season) {
case SPRING:
printf("是春天\n");
break;
case SUMMER:
printf("是夏天\n");
break;
case AUTUMN:
printf("是秋天\n");
break;
case WINTER:
printf("是冬天\n");
break;
default:
printf("你没有选择最喜欢的季节");
}
return 0;
}

枚举的细节

第一个枚举成员的默认值为整形的0,后续枚举成员的值在前一个成员上加1,我们在这个示例中把第一个枚举成员的值定义为1,第二个就为2,以此类推。

在定义枚举类型时改变枚举元素的值。

枚举变量的定义的形式1-先定义枚举类型,再定义枚举变量。

1
2
3
4
enum DAY{
MON=1,TUE,WED,THU,FRI,SAT,SUN
}
enum DAY day = MON;

枚举变量的定义的形式2-定义枚举类型的同时定义枚举变量。

1
2
3
enum DAY{
MON=1,TUE,WED,THU,FRI,SAT,SUN
}day;

枚举变量的定义形式3-省略枚举名称,直接定义枚举变量。

1
2
3
enum{
MON=1,TUE,WED,THU,FRI,SAT,SUN
}day;//这种枚举只能用一次,因为没有枚举名称

可以将整数转换为对应的枚举值

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
enum SEASONS { SPRING = 1, SUMMER, AUTUMN, WINTER };
enum SEASONS season;
int n = 4;
season = (enum SEASONS)n;//强制转换
printf("season=%d",season);
return 0;
}