C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

时间:2023-03-09 21:14:05
C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

1.运算符、类型转换

计算某年y某月m某日d是周几的基姆拉尔森公式公式:int week = (d + 2*m + 3*(m + 1)/5 + y + y/4 - y/100 + y/400 + 1)%7; 除了算数运算符,C#还提供了大量数学函数,这些数学函数归为一类,称为Math 类,其常用函数如下表所示:

表 1‑7 Math类中的常用函数

功能

函数

C#示例

数学表示

结果

乘方

Math.Pow()

Math.Pow(2,3)

23

8

开方

Math.Sqrt()

Math.Sqrt(16)

√16

4

e的次方

Math.Exp()

Math.Exp(8)

e8

2980.96

绝对值

Math.Abs()

Math.Abs(-3)

| -3 |

3

对数

Math.Log()

Math.Log(8,2)

log28

3

常用对数

Math.Log10()

Math.Log10(100)

lg100

2

正弦函数

Math.Sin()

Math.Sin(Math.PI/3)

sin(π/3)

√3/2

余弦函数

Math.Cos()

Math.Cos(Math.PI/3)

cos(π/3)

1/2

正切函数

Math.Tan()

Math.Tan(Math.PI/3)

tan(π/3)

√3

Math类定义了的两个常量Math.PI=3.14159265358997、Math.E=2.71828182845905。

五种算术运算符+、-、*、/、%,%为取余。自增自减运算符++、--,记住后置先使用后自增,前置先自增后使用。赋值运算符=,注意逻辑判断相等符号为= =。复合赋值运算符+=、-=、*=、/=、%=,例如ave %= 100相当于ave = ave % 100。位运算,它是两个二进制数之间的运算,共有“按位与&”、“按位或|”、“按位异或^”、“按位取反~”、“左移<<”、“右移>>”六种。优先级,由高到低依次为:++x、--x => x++、x-- => *、/、% => <<、>> => +、- =>=、*=、/=、+=、-=。

  类型转换

隐式转换,如果A类型的取值范围是B类型的子集,则A类型可以安全地转换为B类型,例如short向int,int向long的转换,float到double的转换等。显式转换,是在源数据前的括号内添加目标类型:(destinationType)sourceVar。要将取值范围较大的类型转换为取值范围较小的类型,必须用显式转换,例如double pi = 3.14; int n = (int)pi; int n = 8226589; short s = (short)n; 但这种情况下的显式转换可能会造成数据丢失。因显式转换而发生溢出错误时,系统不会产生提示,除非在代码中用关键字checked对显式转换进行检查。int n = 8226589; short s = checked((short)n);

字符串和数值间的转换,C#还提供了一批函数,能把字符串转换为各种数值类型,这些函数被归为一类,称为“Convert 类”,Convert 类中的函数如表所示。

表 1‑8 Convert类中的函数

函数

Convert.ToByte(val)

val转换为byte型

Convert.ToInt16(val)

val转换为short开型

Convert.ToInt32(val)

val转换为int型

Convert.ToInt64(val)

val转换为long型

Convert.ToSByte(val)

val转换为sbyte型

Convert.ToUInt16(val)

val转换为ushort型

Convert.ToUInt32(val)

val转换为uint型

Convert.ToUInt64(val)

val转换为ulong型

Convert.ToSingle(val)

val转换为float型

Convert.ToDouble(val)

val转换为double型

Convert.ToDecimal(val)

val转换为decimal型

Convert.ToChar(val)

val转换为char型

Convert.ToString(val)

val转换为string型

Convert.ToBoolean(val)

val转换为bool型

例如string str = "3.14";double pi = Convert.ToDouble(str); 当我们通过控制台输入数字时,也需要通过Convert 类中的函数进行转换,因为通过Console.ReadLine()得到的是一条字符串。Convert.ToInt32()函数可以把非十进制数转换为十进制数,例如Convert.ToInt32(“110011”,2);这里“110011”为原数,2为基数,又如Convert.ToInt32(“567”,8); Convert.ToInt32(“6F”,16);。通过Convert.ToString()可以把十进制转换为非十进制,如Convert.ToString(89,2); Convert.ToString(89,16); 这里分别将89转换为二进制数和十六进制数。

2. 流程控制

传统的程序设计思想把程序看作处理数据的流程,编写代码之前必须认真考虑数据结构和算法,算法就是计算机解决问题的程序步骤,著名计算机科学家Nikiklaus Wirth 提出一个公式:数据结构 + 算法 = 程序。算法是程序设计的灵魂,它解决了“做什么”和“怎么做”的问题,程序中的语句,实际上就是算法的体现。

1)流程图

流程图也叫程序框图,美国国家标准化协会规定了一些常用的程序框图符号,如下图所示,已成为程序工作者普遍采用的标准,用来描述程序控制流程。程序框图是算法的体现,设计好程序框图后,只需将框图翻译成对应的语句即形成可执行的程序。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

图 1‑5常用的程序框图符号

2)逻辑表达式

关系运算符,就是比较两值大小关系的运算符,包括<、>、<=、>=、==、!=。如果逻辑表达式所表示的逻辑关系是正确的,我们称之为“真”,反之,称之为“假”。每个逻辑表达式都有“真”和“假”两种结果。

逻辑运算符,通常,将与(&&)、或(||)、非(!)等逻辑运算符将关系运算式串联起来,即构成各种种样的逻辑表达式。可以直接把逻辑表达式赋给布尔变量,例如bool isEven=(n%2==0); 需要注意的优先级顺序:赋值运算符 < &&和||< 关系运算符 < 算术运算符 < !(非)。

3)程序结构

顺序结构、分支结构(对于分段函数计算,通常可使用if… else if…else if…else。

switch语句,它处理某些多分支问题非常方便,其一般形式如下图。switch 语句中的测试量可以是一个变量,也可以是一个表达式。程序逐个测试case 分支中的常量,测试量匹配哪个分支中的常量,就执行哪个分支中的语句;如果找不到匹配的常量,就执行default 分支中的语句。一般情况下,每个分支都以break 语句结束,程序一旦遇到break 语句,就会结束整个switch 语句,case 分支的内嵌语句在冒号后面,可以是一条,可以是多条,可以写成一行,也可以写成多行。,执行完匹配的case 分支后,不再执行下面的case 分支,整个switch 语句立即结束。如果想继续执行下面的case 分支,需要把break 语句换成goto 语句。例如case 3: total -= 31; goto case 4;case 4: total -= 30; goto case 5;。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

图 1‑6 switch语句一般形式

条件表达式,每个分支都只有一条语句的选择结构可以用条件表达式简洁的表示出来。其一般形式:逻辑表达式?表达式1:表达式2; 如果逻辑表达式布尔值为真,则执行表达式1,否则执行表达式2。

循环结构,while、do-while、fort循环语句,其语句格式如图所示,特别注意后一种循环结构while语句后需要添加;。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

图 1‑7 while、do-while、for语句格式

break语句和continue语句,break 语句可以使流程跳出switch 结构,实际上break 语句还可以跳出循环语句,即提前结束循环。,但作用和break 有所不同,continue 语句不是中断整个循环,而是中断本圈循环。

 3.枚举、结构体和数组

1)枚举类型

定义格式:enum WeekDays {Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}。引用的方式WeekDays.Sunday…等,默认情况下,枚举项的关联值依次对应0、1、2、3…。必要时我们可以通过赋值的方法来改变枚举项所关联的整数值,也可以设定改变枚举项关联的整数值:enum WeekDays {Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday},如果将某个枚举项初始化为特定的值, 其后面未被初始化的枚举项将在这个值的基础上依次递增,直接用枚举项关联的整数进行赋值,但必须进行显式转换: WeekDays today = (WeekDays)3; 当然,必要时我们也可以输出枚举项所关联的整数,同样需要进行显式转换:Console.WriteLine((int)today);。因为每个枚举项都关联一个整数,所以枚举型变量可以参与运算和循环:for (today = WeekDays.Sunday; today <= WeekDays.Saturday; today++) {…}。如果将某个枚举项初始化为特定的值, 其后面未被初始化的枚举项将在这个值的基础上依次递增,,如果需要, 我们可以为枚举项指定更合适的数据类型。关联值可以是byte、sbyte、short、ushort、int、uint、long 和ulong 等类型,在 编译阶段,编译器会直接把枚举项编译成对应的整数。

2)结构体

很多相互联系的信息可以组成一个整体,在C#中可以把这些紧密联系变量定义成结构体(Structure)。例如:struct Student { public int number; public string name; public string sex; public string birthday; } ,相当于定义了一种新数据类型,使用该类型就要创建该类型的变量:Student hfy;

3)数组

具有相同类型的一组数据可以用数组(Array) 捆绑成整体。在C#中,声明数组的语法如下

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

C#中数组的索引从0 开始计数,在有n 个元素的数组中,第一个元素的索引为0,最后一个元素的索引是n-1,可以通过数组名.Length的方法获得数组的长度。只想声明一个数组而不同时进行初始化,但必须通过new关键字为其分配空间,因为创建数组时,系统会在内存中分配相应的空间,而且这个空间的大小是固定不变的,所以声明数组时必须指明数组的长度,因为只有这样,系统才会知道为其分配多少内存空间。

foreach循环语句,foreach语句是针对于集合例如数组的循环语句,但它只能读取集合中元素,不能修改其中的值。二维数组:对于矩阵类似的数据可以通过二维数组进行表示。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

当用for语句处理二维数组时,需要两个嵌套的for语句,外层for语句遍历所有行,当遍历到某一行时,内层for语句遍历该行的所有元素,而用 foreach 语句可以更方便的访问二维数组,其方式与一维数组相同。三维、四维等多维数组的定义方式和二维数组类似。例如 int[, ,] Matrix = new int[3, 3, 4];。

可变数组:声明可变数组需要指定行数,但不需要指定列数。初始化这样的数组不像初始化二维数组那样简单,需要逐行初始化。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

jaggedArray[0] = new int[3]; jaggedArray[1] = new int[4]; 也可以同时为所有元素赋值。jaggedArray[0] = new int[3] { 1, 2, 3 }; jaggedArray[1] = new int[4] { 1, 2, 3, 4 }; 输出可变数组时,要逐行输出foreach(int element in jaggedArray[0]){…},如果要用双重特征,必须这样foreach (int[] line in jaggedArray){…}。可变数组的每一行都可以看做一个数组,因此可变数组也叫做以数组为元素的数组。

4.函数

函者,匣也。函数这个名称准确的说明了它的用处——实现一定功能的黑匣子。函数是一段被封装起来的能实现一定功能的代码。函数由两部分组成,一部分是函数的签名(Signature),指明了函数的名称、参数、和返回类型。函数的具体实现代码定义在签名后的一对大括号中,称为函数体(Function body)。

C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数

关键字 static 说明该函数是一个静态函数,非静态函数需要实例化后才能使用,静态函数则可直接使用.NET 推荐采用PascalCasing(帕斯卡式)方式为函数命名,即所有单词的首字母都大写。如果在函数A()中调用函数B(),则函数A()称为主调函数,函数B()称为被调函数。

函数参数,函数定义中的参数称为形式参数,调用函数时的参数称为实际参数,设备函数时,程序首先为形参分配内存空间,然后把实参的值复制一份给形参,这种参数传递方式称为值传递。当参数为大型数据时,这种传值方法就会遇到性能问题。假如实参为有一万个元素的数组,把这一万个元素复制给形参要花费相当多的时间, 这种情况一般把实参的地址传给形参,即实参和形参指向同一块内存空间,这种传递方法叫做地址传递。地址传递时实参和形参指向同一块内存空间,形参的变化会影响到实参,也就是说在函数执行过程中可以修改参数内部的值。总而言之,①当参数类型为int、double等值类型时,实参和形参之间进行值传递,实参和形参占用不同的内存空间,形参的变化不对实参造成影响。②当参数类型为数组等引用类型时,实参和形参之间进行地址传递,实参和形参指向同一块内存空间,实参随着形参的变化而变化。可以通过在值类型参数前添加修饰符ref,使实参与形参指向同一块内存空间,从而使值类型实参将会随着形参的变化而变化,调用该函数的时候,也要使用ref 关键字。总之,当参数为数组、结构体等复杂类型时,自动为地址传递;当参数为基本类型时,要用关键字ref才能实现地址传递。例如

static void Swap(ref int x, ref int y)

{  int temp = x;

x = y;

y = temp; }

int a=12,b=11;

Swap(ref a, ref b);

out参数,函数的return语句只能返回一个数据,为能将更多的数据返回到主调函数中,除了引用型参数外,还可以使用输出型参数(out)返回有用的计算结果。。out型参数和ref型参数的用法几乎完全一样,二者之间只用一个区别,ref型参数传入函数前必须赋值,而out型参数传入前不用赋值,即使赋值也会被忽略。

参数匹配,调用函数时,实参和形参的类型应当匹配。如果不匹配,编译器将尝试进行隐式转化, 把实参提升到形参的类型。如果实参不能转换为形参的类型,编译器将报错。

可选参数,可选参数必须有一 个默认值,当调用函数时,如果没有为可选参数传递实参,它将按默认值进行计算,可选参数必须在必选参数的后面。调用函数时,实参必须和可选参数的顺序对应,不能改变位置。例如:

static double VolumeOfColumn(double r, double h = 1, double pi = 3.14){…}

VolumeOfColumn(3); VolumeOfColumn(3, 2);  VolumeOfColumn(3, 2, 3.14159);

递归调用,函数反复调用本身的行为称为递归调用。递归调用常常能简便的解决某类复杂的问题。实现递推公式的递归函数中也要包括初始值和递推关系这两个要素。

static int Fac(int n)

{     if(n<=1) return 1;

else return n * Fac(n - 1); }

变量的作用域,①局部变量,函数内部定义的变量,只在该函数内部有效,它的作用域从定义的地方开始,直到函数结束为止。当调用函数时,变量在内存中创立,当退出函数时,变量从内存中清除,因此称为局部变量。有时需要在函数内部的程序块中定义变量,这些变量只在定义它的程序块中有效。