导图社区 C语言知识点总结ii
C语言知识点总结ii、为确保兼容8.0之前的代码,C#在默认情况下并不支持引用类型的可空性特性,要想启用此特性,需要使用# nullable语句,或在项目属性配置中启用该特性。
编辑于2022-11-10 17:30:17时间管理-读书笔记,通过学习和应用这些方法,读者可以更加高效地利用时间,重新掌控时间和工作量,实现更高效的工作和生活。
本书是法兰教授的最新作品之一,主要阐明了设计史的来源、设计史现在的状况以及设计史的未来发展可能等三个基本问题。通过对设计史学科理论与方法的讨论,本书旨在促进读者对什么是设计史以及如何写作一部好的设计史等问题的深入认识与反思。
《计算机组成原理》涵盖了计算机系统的基本组成、数据的表示与运算、存储系统、指令系统、中央处理器(CPU)、输入输出(I/O)系统以及外部设备等关键内容。通过这门课程的学习,学生可以深入了解计算机硬件系统的各个组成部分及其相互之间的连接方式,掌握计算机的基本工作原理。
社区模板帮助中心,点此进入>>
时间管理-读书笔记,通过学习和应用这些方法,读者可以更加高效地利用时间,重新掌控时间和工作量,实现更高效的工作和生活。
本书是法兰教授的最新作品之一,主要阐明了设计史的来源、设计史现在的状况以及设计史的未来发展可能等三个基本问题。通过对设计史学科理论与方法的讨论,本书旨在促进读者对什么是设计史以及如何写作一部好的设计史等问题的深入认识与反思。
《计算机组成原理》涵盖了计算机系统的基本组成、数据的表示与运算、存储系统、指令系统、中央处理器(CPU)、输入输出(I/O)系统以及外部设备等关键内容。通过这门课程的学习,学生可以深入了解计算机硬件系统的各个组成部分及其相互之间的连接方式,掌握计算机的基本工作原理。
C语言知识点总结
指针
指针变量
变量的指针就是变量的地址。存放变量地址的变量是指针变量
定义指针变量
int *p2;//*p2是指向整型变量的指针变量
float*p3; //*p3是指向浮点变量的指针变量
char *p4; //*p4是指向字符变量的指针变量
指针变量的引用
&:取地址运算符
*:指针运算符
指针变量初始化方法
int a; int*p=&a
赋值语句的方法
int a; int *p; p=&a;
不可以把一个数赋予指针变量
列如: int *p; p=1000; //这是错的,被赋值的指针变量前不能再加“*”说明符,如写为*p=&a也是错的。
指针变量作为函数参数
注意:不能企图通过改变指针形参的值而使指针实参的值改变
1.指针作为函数参数时,实际上是将变量的地址传给函数;
2.需要注意:函数 swap 的形参的定义形式为“int *p1”;而调用该函数时,实参形式为指针(变量地址)“pointer_1”。
3.函数 swap 的作用是交换两个变量(a 和 b)的值,其实现方法是通过交换指针变量所指向的值(*p1 和 *p2),而并非交换指针变量自身值(p1 和 p2)。这是因为:函数参数传递的方式是“单向值传递”的“值传递”方式(指针变量作为函数参数也是遵循这一规则),形参值(p1 和 p2)的改变不能使实参的值(pointer_1 和 pointer_2)随之改变,即,受“单向值传递”约束的只是指针变量(pointer_1 和 pointer_2),而指针变量(pointer_1 和 pointer_2)所指向的值(a 和 b)并不受约束,因此,可以通过指针变量作为函数参数的方式,改变指针变量所指向的参数值;
4.函数的调用可以(而且只可以)得到一个返回值(即函数值),而运用指针变量作为函数参数,可以得到多个变化的值。
通过指针引用字符串
通过指针引用数组
数组元素的指针
数组元素的指针就是数组元素的地址
在引用数组元素时指针的运算
p++;使p指向下一元素a[1]
*p;得到下一个元素a[1]的值
*(p++);先取*p值值然后使p加1
*(++p);先使p加1,再取*p
如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p+1指向同一数组的上一个元素
通过指针引用数组元素
下标法,如a[i]形式
指针法,如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量,其初值为p=a
用数组名作函数参数
(1) 如果函数实参是数组名,形参也应为数组名(或指针变量),形参不能声明为普通变量(如int array;)。实参数组与形参数组类型应一致(现都为int型),如不一致,结果将出错。
(2) 需要特别说明的是: 数组名代表数组首元素的地址,并不代表数组中的全部元素。因此用数组名作函数实参时,不是把实参数组的值传递给形参,而只是将实参数组首元素的地址传递给形参。形参可以是数组名,也可以是指针变量,它们用来接收实参传来的地址。如果形参是数组名,它代表的是形参数组首元素的地址。在调用函数时,将实参数组首元素的地址传递给形参数组名。这样,实参数组和形参数组就共占同一段内存单元
通过指针引用多维数组
多维数组元素的地址
指向函数的指针
返回指针值的函数
指针数组和多重指针
分支结构程序
关系运算符
>大于
>=大于等于
==等于
!=不等于
逻辑运算符和表达式
“&&”和“||”低于关系运算符,“!”高于算术运算符
逻辑运算的值
与运算&&:参与运算的两个量都为真时,结果才为真,否则为假。
或运算||:参与运算的两个量只要有一个为真,结果就为真,两个都为假时,结果为假。
非运算!:参与运算量为真时,结果为假;参与运算为假时,结果为真。
if语句
在if语句中,条件判断表达式必须用括号括起来,在语句之后必须加分号
条件运算符和条件表达式
一般形式:表达式1?表达式2:表达式3;
其求值规则为:如果表达式1的值为真,则以表达式1的值作为条件表达式的值,否则以表达式2的值作为整个表达式的值。
如 if(a>b) max=a; else max=b; 可以用条件表达式写为 max=(a>b)?a:b; 该语句的含义为:如a>b为真,则把a赋予max,否则把b赋予max。
switch语句(考点)
在case后的各常量表达式的值不能相同,否则会出现错误
在case后允许有多个语句。可以不用{}括起来
各case和default子句的先后顺序可以变动,而不影响程序执行的结果
default子句可以省略不用。
case之后如果不写break语句,则执行case之后不会跳出switch语句,会执行后面的语句
一般case-break都是成对出现,而这也是编码好习惯。但考试的时候会考case之后不加break的情况,是十分重要的考点
函数
函数的参数和函数的值
形式参数和实际参数(考点)
数组(重难点)
一维数组的定义和引用(重点)
一维数组的定义方式: 类型说明符 数组名 [常量表达式];
列如: int a[10]; 说明整型数组a,有十个元素
float b[10],c[20];说明实型数组b,有10个元素,实型数组c,有10个元素
注意:
1.数组名不能与其它变量名相同
方括号中常量表达式表示数组元素的个数,如a[5]表示数组a有5个元素。但是其下标从0开始计算。因此5个元素分别为a[0],a[1],a[2],a[3],a[4].
不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式
允许在同一个类型说明中,说明多个数组和多个变量,列如: int a,b,c,d,k1[10],k2[20];
一维数组的引用
数组名[下标]
列如:a[5] a[i+j] a[i++] 都是合法的
一维数组的初始化
列如: int a[10]={0,1,2,3,4,5,6,7,8,9};
可以只给部分元素赋初值 ,其它的元素自动赋0值
只能给元素逐个赋值,而不能给数组整体赋值。
如给全部元素赋值,则在数组说明中,可以不给出数组元素的个数 列如: int a[5]={1,2,3,4,5}; 可写为: int a[]={1,2,3,4,5,};
二维数组的定义和引用
二维数组定义: 类型说明符 数组名[常量表达式1][常量表达式2]
二维数组的引用: 数组名[下标][下标] 列如: a[3][4] 表示a数组三行四列的元素。
二维数组的初始化
按行分段赋值可写为: int a[5][3]={ {80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85} };
按行连续赋值可写为: int a[5][3]={ 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85};
若对全部元素赋初值,则第一维的长度可以不给出
字符数组
定义列如: char c[10];
字符数组也可以是二维数组或多维数组
字符数组的初始化 列如: char c[10]={'c',' ','p','r','o','g','r','a','m'};
字符串和字符串结束标志(考点)
c语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。且字符串总是以‘\0'作为串的结束符。
可以用字符串的方式对数组做初始化赋值 列如: char c[]={'c',' ','p','r','o','g','r','a','m'}; 可写为: char c[]={"c program"}; 或去掉{}写为 char c[]="c program"
字符串处理函数
用于输入输出的字符串函数,在使用前应包括头文件“stdio.h”,使用其他字符串函数则应包含头文件“string.h"。
1.字符串输出函数puts
功能:把字符数组中的字符串输出到显示器。即在屏幕上显示该字符串。
2.字符串输入函数gets
从标准输入设备键盘上输入一个字符串
3.字符串连接函数strcat
格式: strcat(字符数组1,字符数组2)
功能:把字符数组2中的字符串连接到字符数组1中字符串的后面,并删去字符串1后的串标志“\0"。本函数返回值是字符数组1的首地址。
4.字符串拷贝函数strcpy
5.字符串比较函数strcmp
6.测字符串常数函数strlen
算法
特性:有穷性,确定性,有零个输入或多个输入,有一个或多个输出,有效性。
三种结构:顺序结构,选择结构,循环结构。
三种结构的特点:只有一个入口,只有一个出口,结构内的每一部分都有机会被执行到,结构内不存在“死循环”。
顺序程序设计
c语句
1.表达式语句
即表达式加一个分号
2.函数调用语句
由函数名、实际参数加上分号“;”组成
3.控制语句
条件判断语句:if语句、switch语句
循环执行语句:do while语句、while语句、for语句
转向语句:break语句、goto语句、continue语句、return语句
4.复合语句
把多个语句用括号{}括起来组成的一个语句称复合语句
5.空语句
字符数据的输入输出
putchar函数(字符输出函数)
功能是在显示器上输出单个字符
putchar(‘A’); (输出大写字母A)
putchar(x); (输出字符变量x的值)
getchar函数(键盘输入函数)
getchar函数的功能是从键盘上输入一个字符。
getchar函数只能接受单个字符,输入数字也按字符处理。输入多于一个字符时,只接收第一个字符
格式输入和输出(重点考点)
注意:必须熟记%d,%c,%s,%f,%lf区别和使用场景。
scanf格式输入函数必须在屏幕严格按照格式才能准确输入,如scanf(“%dsss%d”,&x,&y,);那么如果赋值x=5,y=8,则屏幕上必须是5sss8才能将5和8分别赋值给x和y。又如scanf(“%d,%d”,&x,&y);则屏幕上必须是5,8才能赋值。
printf格式输出常考小数点后几位输出,如printf(“%.2f",a); 则表示输出a小数点后2位。其它还有很多格式输出形式
格式字符串(重难点)
形式; %[*][输入数据宽度][长度]类型
数据类型,运算符与表达式
数据类型
1.基本类型
整型int
字符型char
实型(浮点型)
单精度型float
双精度型double
枚举类型
2.构造类型
3.指针类型
4.空类型void
常量和变量
常量可以直接引用,变量必须先定义后使用
常量和符号常量
直接常量
整型常量
实型常量
字符常量
标识符
符号常量
用标识符代表一个常量,称为符号常量。
符号常量与变量不同,它的值在其作用域内不能改变,也不能再被赋值
使用符号常量的好处是:含义清楚,能做到一改全改
变量
变量是一种使用方便的占位符,用于引用计算机内存地址。
1、变量名必须以字母或下划线打头,名字中间只能由字母、数字和下划线“_”组成;最后一个字符可以是类型说明符;
2、变量名的长度不得超过255个字符;
3、变量名在有效的范围内必须是唯一的。
4、变量名不能是保留字(关键字),也不能是末尾带类型说明符的保留字,但可以把保留字嵌入变量名,
整形数据
整形常量的表示方法(必考点)
1.十进制整常数
只有0~9,可以有正负号,第一个数字不能为0
2.八进制整常数
必须以0开头,只有0~7,八进制数通常是无符号数,有正负号就不合法了
3.十六进制整常数
十六进制整常数的前缀为0x或0X。其数码取值为0~9,A~F或a~f。
整型常数的后缀
当超过该进制的表示范围时,就必须用长整型来表示。长整型是用后缀“l”或“L"来表示的
无符号数也可用后缀表示,整型常数的无符号数的后缀为“U”或“u”。列如:358u,0x38Au,235Lu均为无符号数。
前缀,后缀可同时使用以表示各种类型的数。如0XA5Lu表示十六进制无符号常整数A5,其十进制为165。
整型变量
注意:任何数据的存放都是二进制形式。
1.基本型:int
2.短整型:short int或short
3.长整型:long int或long 。在内存中占4个字节
4.无符号型:unsigned
无符号基本型:unsigned int或unsigned
无符号短整型:unsigned short
无符号长整型:unsigned long
整型变量的定义
类型说明符 变量名标识符,变量名标识符,...;
列如:int a,b,c;(a,b,c为整型变量)
long x,y;(x,y为长整型变量)
unsighed p,q;(p,q为无符号整型变量)
实型数据
实型常量的表示方法
十进制数形式
由数码0~9和小数点组成
必须要有小数点,如:0.0 、25.0、0.13、300. 、-267.8
指数形式(考点)
由十进制数,加阶码标志“e”或“E”以及阶码(只能为整数,可以带符号)组成
如2.1E5 、3.7E-2 、0.5E7 、-2.8E-2
c语言允许浮点数使用后缀。后缀为“f”或“F”即表示该数为浮点数。如365f和365.是等价的
实型变量
单精度float型
双精度double型
长双精度long double型
实型常数不分单、双精度,都按双精度double型处理
字符型数据
字符常量
字符常量是用单引号括起来的一个字符,不能用双括号或其它括号
字符常量只能是单个字符,不能是字符串
字符可以是字符集中任意字符。但数字被定义为字符型之后就不能参与数值运算。如‘5’和5是不同的。‘5’是字符常量,不能参与运算
转义字符
/n 换行
/t 跳到下一制表位置
字符变量
字符变量用来存储字符常量,即单个字符
字符变量的类型说明符是char
定义 如 :char a,b;
字符串常量
字符串常量用双引号括起来
字符常量只能是单个字符,字符串常量可以是一个或多个字符
可以把一个字符常量赋予一个字符变量,但不能把一个字符串常量赋予一个变量。在c语言中没有相应的字符串变量。这是与BASIC语言不同的。但是可以用一个字符数组来存放一个字符串常量。
变量赋初值
在定义中不允许连续赋值,如a=b=c=5是不合法的。
各类数值型数据之间的混合运算
自动转换
自动转换(自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成)
自动转换遵循以下规则:
若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转换成long型后再进行运算。
所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再做运算。
char型和short型参与运算时,必须先转换成int型。
在赋值运算中,赋值号两边的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的办法按四舍五入向前舍入。
类型自动转换的规则如下
char,short->int->unsigned->long->double
强制转换
强制类型转换是通过类型转换运算来实现的
其一般形式为:
(类型说明符)(表达式)
其功能是把表达式的运算结果强制转换成类型说明符所表示的类型
例如:
(float)a 把a转换为实型
(int)(x+y)把x+y的结果转换为整型
在使用强制转换时应 注意以下问题:
类型说明符和表达式都必须 加括号(单个变量可以不加括号), 如把(int)(x+y)写成(int)x+y则成了把x转换成int型之后再与y相加了。
无论是强制转换或自动转换,都 只是为了本次运算的需要而对变量的数据长度进行临时性转换,而不改变数据说明时对该变量定义的类型。
运算符
1.算术运算符:加(+)、减(-)、乘(*)、除(/)、求余(%)、自增(++)、自减(--)共七种
注意:除法“/",当两侧都为整型时,结果也为整型。如10/3=3而不是3.3.
求余运算符“%”两侧必须为整型,其它类型错误。(常考点)
自增、自减运算符(重考点)
++i i自增1后再参与其它运算
--i i自减1后再参与其它运算
i++ i参与运算后,i的值在自增1
i-- i参与运算后,i的值再减1
2.关系运算符:用于比较运算。包括大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)、不等于(!=)六种
3.逻辑运算符:与(&&)、或(||)、非(!)三种
4.位操作运算符:参与运算的量,按二进制位进行运算。包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>)六种
5.赋值运算符:简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)、复合位运算赋值(&=,|=,^=,>>=,<<=)
6.条件运算符:这是一个三目运算符用于条件求值(?:)。
7.逗号运算符:用于把若干表达式组合成一个表达式(,)
8.指针运算符:用于取内容(*)和取地址(&)两种运算
9.求字节数运算符:用于计算数据类型所占的字节数(sizeof)
10.特殊运算符:有括号(),下标[],等
赋值运算符和赋值表达式
类型转换
1.实型赋予整型,舍去小数部分。
2.整型赋予实型,数值不变,但以浮点形式存放,即增加小数部分(小数部分的值为0)
3.字符型赋予整型,由于字符型为一个字节,而整型为二个字节,故将字符的ASCII码值放到整型量的低八位中,高八位为0.整型赋予字符型,只把低八位赋予字符量
逗号运算符和逗号表达式
一般形式:表达式1,表达式2; 其求值过程通常是分别求两个表达式的值,并以表达式2的值作为整个逗号表达式的值
选择程序设计
循环结构程序设计
while(表达式)语句 (重点)
while语句的含义是:计算表达式的值,当值为真(非0)时,执行循环体语句。
循环体如包括有一个以上的语句,则必须用{}括起来,组成复合语句
do-while语句(重点)
do 语句 while(表达式);
do-while循环至少要执行一次循环语句
for语句(重点)
一般形式:for(表达式1;表达式2;表达式3)语句
for语句运行过程
它的运行过程为:
先执行“表达式1”。
2) 再执行“表达式2”,如果它的值为真(非0),则执行循环体,否则结束循环。
3) 执行完循环体后再执行“表达式3”。
4) 重复执行步骤 2) 和 3),直到“表达式2”的值为假,就结束循环。
上面的步骤中,2) 和 3) 是一次循环,会重复执行,for 语句的主要作用就是不断执行步骤 2) 和 3)。
也可理解为:for(循环变量赋初值;循环条件;循环变量增量)语句
列如:for(i=1;i<=100;i++)sum=sum+i;
break和continue语句
break语句
break语句用于switch和循环语句。用于switch语句时,跳出switch;用于循环语句时则跳出循环。
break语句对if-else的条件语句不起作用
在多层循环中,一个break语句只向外跳一层
continue语句
continue语句的作用是跳过循环本中剩余的语句而强行执行下一次循环。continue语句只用在for、while、do while等循环体中。
C# 8.0本质论
第二十四章 — 公共语言基础结构
CLI的定义
CLI的实现
.NET Standard
基类库
将C#编译成机器码
运行时
程序集、清单和模块
公共中间语言
公共类型系统
公共语言规范
元数据
.NET native和AOT编译
第二十三章 — 平台互操作性和不安全代码
平台调用
指针和地址
通过委托执行不安全代码
第二十二章 — 线程同步
线程同步的意义
计时器
第二十一章 — 并行迭代
并行执行循环迭代
并行执行LINQ查询
第二十章 — 基于任务的异步模式编程
同步执行高延迟操作
使用TPL实现异步执行高延迟操作
使用async/await实现基于任务的异步模式
异步返回值ValueTask< T >
异步流
IAsyncDisposable接口以及await using声明和语句
使用LINQ和IAsyncEnumerable
返回void的异步方法
异步Lambda表达式和本地函数
任务调度器和同步上下文
在Windows UI程序中使用async/await
第十九章 — 多线程概述
多线程基础
异步任务
取消任务
使用System.Threading
第十八章 — 反射、特性和动态编程
反射
nameof操作符
特性
使用动态对象进行编程
第十七章 — 构建自定义集合
更多集合接口
主要集合类
提供索引器
返回null或者空集合
迭代器
第十六章 — 使用查询表达式的LINQ
查询表达式概述
查询表达式只是方法调用
第十五章 — 支持标准查查询操作符的集合接口
集合初始化器
IEnumerable使类成为集合
标准查询操作符
匿名类型之于LINQ
第十四章 — 事件
使用多播委托实现Publish-Subscribe模式
理解事件
第十三章 — 委托和Lambda表达式
委托概述
声明委托类型
Lambda表达式
匿名方法
委托没有结构相等性
外部变量
表达式树
第一章 — C#概述
"Hello, World"
C#代码需要基于.NET框架编译和运行,比如.NET Core
选择合适的源代码编辑工具,如Visual Studio 2022
C#语法基础
关键字:C#中具有特殊地位和含义的某些单词,如class,static,void
标识符:用于标识编码的构造,如HelloWorld和Main
清晰(甚至是详细),简洁,有意义,“框架设计准则”建议不要使用单词缩写
下划线合法但一般不用
两种命名方式
PascalCase,每个首字母大写
camelCase,第一个首字母小写其他大写
类型定义
C#所有代码都出现在一个类型定义的内部
类型名称(如class HelloWorld中的HelloWorld)可以随便取,但约定使用PascalCase风格
Main方法
C#方法是已命名代码块,由一个方法声明(如 static void Main( ))引入,后跟一对大括号,其中包含0或多条语句
C#程序从Main方法开始执行
语句和语句分隔符
C#通常用分号标识语句结束
switch语句不要求跟分号
代码块本身就被视为语句,不要求以分号结尾
有的编程元素(如using指令)虽然以分号结尾,但不被视为语句
多条语句可以放置在一行,一条语句也可以跨越多行
空白
空白是一个或多个连续的格式字符(格式字符有制表符、空格和换行符等)
删除单词或引号字符串中的任何空白都会造成歧义
常用空白对代码进行缩进以增加可读性
使用变量
声明
变量是存储位置的符号名称,程序以后可以对该存储位置进行赋值和修改。局部意味着变量在方法或代码块内部声明
声明变量需要:
1. 指定变量要包含的数据的类型
2. 为变量分配标识符
约定局部变量名采用camelCase风格
声明变量后可赋值,但不可更改数据类型
可以在同一条语句中声明多个变量,指定数据类型一次,然后用逗号分隔不同标识符
如:string message1, message2;
赋值
局部变量在声明后必须在读取前赋值。即可在声明时赋值,也可以在声明后。
允许在同一个语句中进行多次赋值(如:message1 = message2 = “Fuck C#.”)
简单赋值操作符 =
使用
赋值后就能用变量名引用值
字符串不可变:不能修改变量最初引用的数据,只能重新赋值
控制台输入和输出
system.Console.Readline( )
暂停程序执行,等待用户输入,获取控制台输入的文本。用户输入后回车,程序继续执行
也称为返回值
字符串插值功能
注释
种类
单行注释://注释
带分隔符的注释:/*注释*/
XML单行注释:///注释
XML带分隔符的注释:/**注释**/
要尽量写清晰的代码,而不是通过注释澄清复杂算法
CIL和ILDASM
托管执行和CLI
处理器使用机器码,不能直接解释程序集
程序集用的是公共中间语言(Common Intermediate Language,CIL),或者称为中间语言(IL)
C#编译器将C#源代码文件转换为中间语言
VES(Virtual Execution System,虚拟执行系统)也称为“运行时”(runtime),它根据需要编译CIL代码,这一过程称为即时编译或JIT编译(just-in-time compilation)
代码在“运行时”这样的“代理”的上下文中执行,被称为托管代码(managed code)
执行时不需要“运行时”的代码称为本机代码(native code)或非托管代码(unmanaged code)
“运行时”包含在一个更广的规范中,即CLI(Common language Inftastructure,公共语言基础结构)规范,作为国际标准,CLI包含以下几方面规范
VES
CIL
支持语言互操作性的类型系统,称为CTS(Common Type System,公共类型系统)
编写通过CLI兼容语言访问的库的指导原则
使各种复位能被CLI识别的元数据(包括程序集的布局或文件格式规范)
在“运行时”执行引擎的上下文中运行,程序员不需要直接写代码就能使用的几种服务和功能,包括
语言互操作性
类型安全
代码访问安全性
垃圾回收
平台可移植性
BCL(基类库)
CIL和ILDASM
通常使用Microsoft特有的文件名ILDASM来称呼CIL反汇编程序(ILDASM是IL Disassembler的简称),它能对程序集执行反汇编,提取C#编译器生成的CIL
反汇编(disassemble)的得到的是汇编代码,反编译(decompile)得到的是所用语言的源代码
第二章 — 数据类型
基本数值类型
整数类型
sbyte,8位,-128 ~ 127
byte,8位,0 ~ 255
short,16位,-32 768 ~ 32 767
ushort,16位,0 ~ 65 535
int,32位,-2 147 483 648 ~ 2 147 483 647
uint,32位,0 ~ 4 294 967 295,后缀U或u
long,64位,-9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807,后缀L或l
ulong,64位,0 ~ 18 446 744 073 709 551 615,后缀UL或ul
浮点类型
float,32位,±1.5 x 10^-45 ~ ±3.4 x 10^38,有效数位7,后缀F或f
double,64位,±5.0 x 10^-324 ~ ±1.7 x 10^308,有效数位15 ~ 16,后缀D或d
decimal类型
decimal,128位,1.0 x 10^-28 ~ 7.9 x 10^28,有效数位28 ~ 29,后缀M或m
decimal具有比浮点型更高的精度,但范围较小,所以从浮点型转换为decimal类型可能会发生溢出错误。此外,decimal计算速度稍慢(差别不大以至于完全可以忽略)
字面值
字面值(literal value)代表源代码中的固定值
更多基本类型
布尔类型(Boolean)
在条件语句和表达式中便是真或假,允许的值包括关键字true和false
实际大小是一个字节
bool的BCL名称是System.Boolean
字符类型
char表示16位字符,取值范围对应于Unicode字符集
从技术上说,char大小和16位无符号整数(ushort)相同,后者取值范围是0 ~ 65 535,但char是C#特有类型,在代码中要单独对待
输入char字面值需要将字符放到一对单引号中,比如‘A’
有的字符不能直接插入源代码,需特殊处理。首先输入反斜杠(\)前缀,再跟随一个特殊字符代码。反斜杠和特殊字符代码统称为转义序列(escape sequence)
转义字符
\',单引号
\“,双引号
\\,反斜杠
\0,Null
\a,Alert(system beep)
\b,退格
\f,换页(Form feed)
\n,换行(Line feed或者newline)
\r,回车
\t,水平制表符
\v,垂直制表符
\uxxxx,十六进制Unicode字符
\x[n][n][n]n,十六进制Unicode字符 (前三个占位符可选),\uxxxx的长度可变版本
\Uxxxxxxxx,Unicode转义序列,用于创建代理项对
字符串
示意代码
零或多个字符的有限序列称为字符串。C#基本字符串类型是string,BCL名称是System.String
逐字前缀@,指明转义序列不被处理。结果是一个逐字字符串字面值(verbatim string literal),它不仅将反斜杠当作普通字符,还会逐字解释所有空白字符。以@ 开头的字符串唯一支持的转义序列是” “,代表一个双引号,不会中止字符串
字符串插值$,逐字和插值可以组合使用,但需要先指定$,再指定@
字符串方法
静态方法
实例方法
字符串格式化
换行符
Microsoft Windows的换行符是\r和\n这两个字符的组合,UNIX则是单个\n为消除平台间的不一致性,一个办法是使用System.Console.WriteLine( )自动输出空行
为确保跨平台兼容性,可用System.Environment.NewLine代表换行符
System.Console.WriteLine("Hello World")和System.Console.Write("Hello World" + System.Environment.NewLine)等价
在Windows中,System.Console.WriteLine( )等价于System.Console.Write("\r\n" )而非System.Console.Write("\n" )
字符串长度
判断字符串长度可以用string的Length成员
该成员是只读属性,不能设置,调用也不需要任何参数
字符串不可变
string类型的一个关键特征是它不可变(immutable)
无法修改,只能重新创建一个内存位置进行存储,或者覆盖原有值
System.Text.StringBuilder
如果有大量字符串需要修改,可考虑使用System.Text.StringBuilder类型而不是string
StringBuilder包含Append( )、AppendFormat( )、Insert( )、Remove( )和Replace( )等方法
两者关键的区别在于,上述方法会修改StringBuilder本身中的数据,而不是返回新字符串
null和void
null
null可以用作”文字“的一种类型,标明变量为”空“,不指向任何位置
将null赋给引用类型的变量,和不赋值是不一样的概念。赋值了null的变量已设置,未赋值则未设置,使用未设置的变量会造成编译错误
将null值赋给string变量,和为变量赋值" "也是不一样的,前者意味着变量无任何值,后者则有一个称为”空白字符串“的值,这种区分相当有用
可空修饰符
声明一个变量时,在其类型后加问号,表示该变量可以被设置为null
在C# 2.0引入可空修饰符前,除string类型外其他都不可以被设置为null。string是引用类型所以可以,其他是值类型
在C# 8.0前,因为引用类型变量默认可被赋值为null,所以那时没有“可空引用类型”的概念。从8.0开始,这一默认行为变为了可配置,声明引用类型时变量时可以使用可空修饰符,将变量声明为可空,也可以不使用,将变量默认地不可赋值为null。这一概念被启用时,没有可空修饰符的变量设置为null时会产生警告信息
void
有时C# 语法要求指定数据类型但不传递任何数据。在返回类型的位置使用void意味着方法不返回任何数据
void本质上不是数据类型,它只是指出没有数据类型这一事实
数据类型转换
显式转型
C# 允许使用转型操作符执行转型
通过在圆括号中指定希望变量转换成的类型,表明已确认在发生显式转型时可能丢失精度和数据,或可能造成异常
显式转型中,如数据未能转换成功,“运行”时还是会抛出异常
转型操作符不是万能的,不能将一种类型任意转换为其他类型,如不能将long转换为bool
隐式转型
有些情况下,如int转换为long,不会发生精度的丢失,且值不会发生根本性改变,代码只需指定赋值操作符,转换将隐式发生
如果愿意,在隐式转型时也可以强制添加转型操作符
不使用转型操作符的类型转换
由于未定义从字符串到数值类型的转换,因此需要使用像Parse ( )这样的方法,每个数值数据类型都包含一个Parse ( )方法,允许将字符串转换成对应的数值类型
使用float.Parse( )将string转换为数值类型
使用System.Convert进行类型转换
但System.Convert只支持少量类型,且不可扩展,允许从bool、char、sbyte、short、int、long、ushort、uint、ulong、float、double、decimal、DateTime和string转换到这些类型中的任一种
所有类型都支持ToString( )方法,可用它提供类型的字符串表示
TryParse( ),和Parse( )的关键区别在于,TryParse( )不会抛出异常,而是返回false
第三章 — 更多数据类型
类型的划分
值类型
值类型直接包含值,即变量引用的位置就是内存中实际存储值的位置
例:将一个值赋给变量A,再将变量A赋给变量B,会在B的位置创建值得拷贝,而不是引用变量A的位置。这进一步造成了修改变量A的值不会影响变量B的值
将值类型的实例传给Console.WriteLine( )这样的方法也会生成内存拷贝
由于值类型需要创建内存拷贝,因此定义时不要让它们占用太多内存
引用类型
引用类型的变量存储对数据存储位置的引用,而不是直接存储数据
为了访问数据,“运行时”要先从变量中读取内存位置,再“跳转”到包含数据的内存位置。
“运行时”的这种操作称为“解引用”
为引用类型的变量分配实际数据的内存区域称为堆(heap)
将引用类型的变量赋给另一个引用类型的变量,只会拷贝引用而不需要拷贝所引用的数据
每个引用总是处理器的“原生大小”,32位处理器拷贝32位引用,64位处理器拷贝64位引用,显然拷贝对一个大数据块的引用要比拷贝整个大数据块快得多
将变量声明为可空
对null值引用类型变量进行解引用
虽然将null赋值给一个变量,或者作为参数去调用一个方法并不会直接产生问题,但如果对一个值为null的引用类型变量进行解引用(例如调用其方法),则会引发System.NullReferenceException异常,例如调用text.GetType( ),当text值为null时,该异常就会发生
因此,默认情况下不允许将变量赋值为null是一个更好的方案。若想赋值为null,则必须用可空修饰符进行显式声明。这要求程序员主动声明并对可能出现的null值担负更多责任。
判断一个变量的值是否为null的方法有很多,最简单的就是在if语句中用is操作符来检查。虽然也可以使用等于操作符“==”,但由于等于操作符可能被重写并实现不同的行为,因此最好还是使用is
可空值类型
一个值类型变量存储的是一个实际的值,而不是一个引用,并且值类型变量本质上不应该有null值。但在调用一个值类型变量的方法或者访问其属性时,仍然认为是在对该值类型变量进行解引用。虽然技术上不太正确,但当谈论解引用时,普遍并不在意一个变量是值类型还是引用类型
可空引用类型
C# 8.0之前,所有引用类型变量都可以被赋值为null,这一规则导致了大量bug
C# 8.0中,将可空性概念赋予了引用类型变量,从此,在声明任何类型的变量时,默认都为不可空
为确保兼容8.0之前的代码,C#在默认情况下并不支持引用类型的可空性特性,要想启用此特性,需要使用# nullable语句,或在项目属性配置中启用该特性
隐式类型的局部变量
元组
数组
第四章 — 操作符和控制流程
操作符
控制流程概述
代码块
代码块、作用域和声明空间
布尔表达式
关于null的编程
按位操作符
控制流程语句
跳转语句
C#预处理器指令
第五章 — 方法和参数
方法的调用
方法的声明
using指令
Main()的返回值和参数
高级方法参数
递归
方法重载
可选参数
用异常实现基本错误处理
第六章 — 类
类的声明和实例化
示例字段
实例方法
使用this关键字
访问修饰符
属性
构造函数
不可空引用类型属性于构造函数
可空特性
结构函数
静态成员
扩展方法
封装数据
嵌套类
分部类
第七章 — 继承
派生
重写基类
抽象类
所有类都从System.Object派生
用is操作符进行模式匹配
switch语句中的模式匹配
避免对多态类对象使用模式匹配
第八章 — 接口
接口概述
通过接口实现多态性
接口实现
在实现类和接口之间转换
接口继承
多接口继承
接口上的扩展方法
版本升级
扩展方法于默认接口成员
比较接口和抽象类
比较接口和特性
第九章 — 值类型
结构
装箱
枚举
第十章 — 合式类型
重写Object的成员
操作符重载
引用其他程序集
类型封装
定义命名空间
XML注释
垃圾回收
资源清理
推迟初始化
第十一章 — 异常处理
多异常类型
捕捉异常
重新抛出现有异常
常规catch块
异常处理规范
自定义异常
重新抛出包装的异常
第十二章 — 泛型
如果C#没有泛型
泛型类型概述
约束
泛型方法
协变性和逆变性
泛型的内部机制