欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C#的入门

程序员文章站 2022-07-16 19:17:35
...

C#的入门

文章目录

1.第一个C#程序"Hello world!"

程序代码如下:

using System;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello world!");
            Console.ReadKey();
        }
    }
}

相比于C的主函数格式int main (void)int main (int argc,char** argv[]) ;C#的主函数格式则为: static void Main(string[] args)

同时,C#比起C语言的两种注释方法:

//单行注释

/*
多行注释
*/

更多了一种:/// XML注释,如下所示

///<summary>
///两数相加
///</summary>
///<param name="a">第一个整数</param>
///<param name="b">第二个整数</param>
///<returns><returns>
static int twoAdd(int a,int b)
{
    return a + b;
}
  1. 两个///<summary>之间是关于这个函数或者方法的解释
  2. 下行///<param name="参数名">自定义注释,可不填</param>是参数注释
  3. 最后一行,需要作者在<returns>中间填写返回的值的意义
  4. VS2019只需输入///,将自动产生XML注释,关于XML注释到后面再细讲

2.数据类型

2.1 C#的变量命名

规则:
在C的基础上(用英文或_开头,不能以数字开头),多出一项:可以以@符号开头

_ @ 开头暂时不要用,它们有自己特殊的意义】;

注意事项:
与C一样,C#也区分英文的大小写;变量名不能与关键字重复。

规范:

Camel:变量名首单词的首字母小写,其余每个单词首字母单词大写,多用于给变量或字段或方法参数命名。      eg:highSchoolStudent;

2.2 C#的内置数据类型

C#相比于C,有了更多的内置数据类型。并且与C不同的是,由于微软有关于C#的标准规定,所以不会产生随系统的改变而改变数据类型。

这些是C#中已经规定的简单数据类型,其中uint、ushort、ulong和sbyte 为无符号整型,bool无法进行简单运算。

类型名 范围 大小
bool true 或 false 1字节
int -2,147,483,648 到 2,147,483,647 4字节
byte -128到127 1字节
long -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 8字节
short -32,768 到 32,767 2字节
float ±1.5 x 10−45 至 ±3.4 x 1038 4字节
double ±5.0 × 10−324 到 ±1.7 × 10308 8字节
decimal ±1.0 x 10-28 至 ±7.9228 x 1028 16字节
char U+0000 到 U+FFFF 2字节
uint 0 到 4,294,967,295 4字节
sbyte 0到255 1字节
ulong 0 到 18,446,744,073,709,551,615 8字节
ushort 0 到 65,535 2字节

注意:特别是,bool 类型是不同的,并且不同于整型,bool 不能用于替代整型数值,反之亦然。

除此之外,C#还具有 object 和 string 两个内置引用类型,一个是对象类型,一个是字符串类型。

具体说明请查看微软的:msdn.

2.3 C#的变量的未初始化和使用

与C不同,C#的局部变量在定义后如果初始化,所有的C#编译器是不会随机赋值给它。
C#的局部变量必须在代码中初始化,之后才可以在语句中进行使用。
初始化不是在声明变量时进行的,编译器会通过检查所有可能的路径,如果检测到局部变量在初始化之前就使用了它的值,编译器会报错 错误代码 CS0165。反之声明变量时没初始化变量,但在之后未使用它,编译器不会报错。

3.运算符与表达式

3.1 “=”与“+”运算符

1.“=”运算符
与C一样,用作赋值,将等号右边的值,赋值给左边的变量

注意:编译器默认把不带f或F的浮点数常量视为double,所以给float赋值时要使用带f或F的浮点数。

2.“+”运算符

  1. 连接:[当“+”号两边有一边是字符串的时候, “+” 号就起到连接的作用]
string a = "憨憨";
string b = "皮皮虾";
char c = '3';
int d = 1;

a += b;//a为"憨憨皮皮虾"
a += c;//a为"憨憨皮皮虾3"
a += d;//a为"憨憨皮皮虾31"
  1. 相加:[当“+”号两边都是数字的时候,“+” 号就起到相加的作用]

3.2 算术运算符

  1. “+ - * / ”
    加减乘除四则运算,结合性与优先级和C的一样

注意:与C一样,当 / 运算符有左右有一个值是浮点数,则结果为浮点数。

  1. %运算符
    取余运算

注意:与C一样,%左右的值必须为整型数值。

3.3 ++与 - -

自增与自减,与C自增和自减的作用一样。

3.4 复合赋值运算符

有+= 、-= 、*= 、/= 、%=五个复合运算符,作用与C的一样。

3.5 关系运算符

有 > < >= <= == != 六种关系运算符

概念:关系运算符是用来描述两个事物之间的关系。用法与C相同。
由关系运算符连接的表达式称之为“关系表达式”。

注意:关系表达式的运算结果是bool 类型。

3.6 逻辑运算符

有&&与、||或、!非三种逻辑运算符。

注意:逻辑运算符两边放的一般都是 关系表达式bool 类型的值;

注意:逻辑表达式的结果同样也是 bool 类型

3.7 占位符

作用:让字符串与变量连接更舒服。
格式:{n},代表输出第n-1个参数,且n不能大于参数个数,n-1>0

例子如下:

Console.WriteLine("今天是{0}年{1}月{2}日",2020,1,12);

3.8 表达式

表达式是由一个或多个操作数以及零个或多个运算符组成的序列,其计算结果为一个值、对象、方法或命名空间 。 表达式可以包含文本值、方法调用、运算符及其操作数,或简单名称 。
简单名称可以是变量名、类型成员名、方法参数名、命名空间名或类型名。

4.用户输入与转义字符

4.1用户输入

函数:Console.ReadLine();

string name;
name = Console.ReadLine();

4.2转义字符

概念: 指的是一个 \ 加上 一个特殊的字符,组成了一个具有特殊意义的字符。
C#有以下几个转义字符:

转义字符 作用
\’ 单引号
\" 双引号
\\ 反斜杠
\0
\a 警告(产生峰鸣)
\b 退格
\f 换页
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符

@与转义字符的关系 :用在字符串的前面起到两个作用

取消“\”在字符串中的转义作用,使其单纯的就表示一个斜线——[多用于硬盘路径]

string name = @"D:\Game_R&D";
Console.WriteLine(@"D:\Game_R&D");

不加@,编译会提示无法识别的转义序列。
C#的入门
字符串中的 " 要用 “” 表示

string str = @"aaa=""bbb""";
// 不加 @,可以写成
string str2 = "aaa=\"bbb\"";

5.类型转换与常量

5.1 类型转换

更加详细的查看内置数值转换(C# 参考)

  1. 隐式类型转换 [ 自动类型转换 ]
    由于这种转换是类型安全且不会导致数据丢失,因此无需使用特殊语法。
    示例包括:从较小整数类型到较大整数类型的转换以及从派生类到基类的转换。

从 int、uint、long 或 ulong 到 float 的隐式转换以及从 long 或 ulong 到 double的隐式转换可能会丢失精准率,但绝不会丢失一个数量级。 其他隐式数值转换不会丢失任何信息

条件1:两种类型兼容
条件2:原类型要小于目标类型(小范围的转大范围的)
例:int->double int->float

int a = 10;
double b;
b = a;
  1. 显式类型转换 [ 强制类型转换 ]
    条件1:两种类型兼容
    条件2:原类型大于目标类型(大范围的转小范围的)
    例:double->int float->int

注意:显式类型转换可能会导致数据丢失或引发异常

double d = 5673.74;
int i;
// 强制转换 double 为 int
i = (int)d;//i的值为5673
  1. 表达式中的类型转换

有以下几种情况:

1.如果是相同类型进行简单运算,则大部分情况下不会进行类型升级

注:short与char除外,这两种数据类型会自动升级为int类型,再进行运算

2.如果表达式中最大范围的操作数为X类型,则整个表达式的其它数据类型提升为X类型再进行运算。
与C不同的是:C#中如果表达式中最大范围的操作数为float,float在表达式中并不会升为double。

注:

  1. 类似ulong和long、ushort和short、int和uint、sbyte与byte之间进行简单运算时不会自动升级,编译会报错。
    例如:错误 CS0034 运算符“+”对于“long”和“ulong”类型的操作数具有二义性
  2. 类似 ulong 与 ushort 或 uint 与 byte 这样的就通过编译。
  1. Convert类型转换
    概念:
    两个变量的类型如果相互兼容,可以使用 自动类型转换 或 强制类型转换。
    如果两个变量的类型不兼容,比如 string 和 int ,string 和 double 。
    这个时候我们就可以用 Convert 进行转换。
double a;
//由于Console.ReadLine()返回的是string类型的数据,所以要用Convert类型转换
a = Convert.ToDouble(Console.ReadLine());

5.2 常量

1.概念: 一个不能变化的量,常量一旦声明,就不可以再重新赋值。
2.命名规范: 常量名的命名一般是全大写,单词与单词之间用下划线分割。
3.语法: const 数据类型常量名 = 常数值;

6.分支结构之if-else语句

更加详细的请看 微软的官方文档

6.1与C的不同点:

C# 的if语句里的“判断条件” 只能是bool 类型的值,哪怕是关系表达式的返回值也是bool类型。而C的if语句的“判断条件”只看值不管数据类型

在C中不内置bool类型,直到C99才定义了bool类型,使用需要引入头文件<stdbool.h>

//C中可以成立
while ("hh")
{
	printf("hanhan");
}

6.1与C的相同点:

  1. if语句的写法与C的一样
  2. if-else-if 里的else也可以省略
  3. if语句的执行顺序一样,先判断,成立的话执行,不成立的话跳过。

7.分支结构之switch-case语句

更加详细的请看 微软的官方文档

7.1与C的不同点:

若是对应case 中有代码段,在C中允许省略掉该 case 的 break,然后会继续向下执行直到结束或遇到 break 为止。

int Num=5;
switch (Num)
{
	  case 1:
           System.out.println("one");
      case 10:
           System.out.println("ten");
      default:
           System.out.println("other");
 }

而在C#中,若对应 case 中有代码段,则不可以省略该 case 的 break,否则编译器会报错:

//此为错误示例
int Num = 10;
switch (Num)
{
	case 1:
		Console.WriteLine("one");
	case 10:
		Console.WriteLine("ten");
	default:
		Console.WriteLine("other");
}

C#的入门
在C#中,default 不管有没有代码段,都不可以省略 break ,否则编译器报错如下
C#的入门

总之,C# 禁止当一个 switch 部分执行完后,再继续执行到下一个 switch 部分。 编译器会报错:错误 CS0163:“控制不能从一个 case 标签(<case 标签 >)贯穿到另一个 case 标签。”
通常通过使用 break、goto 或 return 语句显式退出开关部分来满足此要求。 不过,以下代码也有效,因为它确保程序控制权无法贯穿到 default switch 部分

switch (caseSwitch)  
{
    case 1:  
        Console.WriteLine("Case 1...");  
        break;  
    case 2:  
    case 3:
        Console.WriteLine("... and/or Case 2");  
        break;
    case 4:  
        while (true)  
           Console.WriteLine("Endless looping. . . ."); //死循环,不会执行到下一个switch部分
    default:
        Console.WriteLine("Default value...");
        break;                 
}

7.2与C的相同点:

  1. 当对应 case 里没有代码段时,可以将 break 省略。编译通过。此时,case 1 和 case 2为一个 switch 部分。
int Num = 10;
switch (Num)
{
case 1:
case 10:
default:
	Console.WriteLine("other");
	break;
}
  1. 可以不写 default 如果匹配表达式与其他任何 case 标签都不匹配,此时没有 default ,程序流就会贯穿 switch 语句。

8.迭代语句之for语句

详细的查看微软的C#文档

在指定的布尔表达式的计算结果为 true 时,for 语句会执行一条语句或一个语句块。
在 for 语句块中的任何点上,可以使用 break 语句中断循环,或者可以使用 continue 语句继续执行到循环中的下一次迭代。 还可以使用 goto、return 或 throw 语句退出 for 循环。

在C#中,for语句的四个部分也都可以为空,就像C一样。

for ( ; ; )
{
    // Body of the loop.
}
//或者下面这种形式,无限循环
for ( ; ; )

“初始化表达式”部分

“初始化表达式”部分的语句仅在进入循环前执行一次 。

  • 本地循环变量的声明和初始化,不能从循环外访问。
  • 以下列表中显示用逗号分隔的零个或多个语句表达式:

赋值语句、方法的调用、自增或自减、通过使用 new 运算符来创建对象、await 表达式

“条件”部分

“条件”部分(如果存在)必须为布尔表达式 。 在每次循环迭代前计算该表达式。 如果“条件”部分不存在或者布尔表达式的计算结果为 true,则执行下一个循环迭代;否则退出循环 。

“迭代器”部分

“迭代器”部分定义循环主体每次迭代后将执行的操作 。 “迭代器”部分包含用逗号分隔的零个或多个以下语句表达式 :

赋值语句、方法的调用、自增或自减、通过使用 new 运算符来创建对象、await 表达式

9.迭代语句之while语句

详细的可查看微软的C#文档

1.同样 while 语句的判断条件必须布尔表达式,且不可省略。

2.在指定的布尔表达式的计算结果为 true 时,while 语句会执行一条语句或一个语句块。 由于在每次执行循环之前都会计算此表达式,所以 while 循环会执行零次或多次。 这不同于 do 循环,该循环执行一次或多次。

3.在 while 语句块中的任何位置,都可使用 break 语句中断循环。

4.可通过使用 continue 语句直接步入 while 表达式的判断条件。 如果表达式计算结果为 true,则继续执行循环中的第一个语句。 否则,将在循环后的第一个语句处继续执行。

5.还可以使用 goto、return 或 throw 语句退出 while 循环。

6.while 的循环代码段只有一句时可以写成下面这种样子:

while (ture)
	Console.Writeline("123");

10.迭代语句之do-while语句

1.同样 do-while 语句的判断条件必须布尔表达式,且不可省略。

2.在指定的布尔表达式的计算结果为 true 时,do 语句会执行一条语句或一个语句块。 由于在每次执行循环之后都会计算此表达式,所以 do-while 循环会执行一次或多次。 这不同于 while 循环(该循环执行零次或多次)。

3.在 do 语句块中的任何点,都可使用 break 语句中断循环。

4.可通过使用 continue 语句直接步入 while 表达式的判断条件。 如果表达式计算结果为 true,则继续执行循环中的第一个语句。 否则,将在循环后的第一个语句处继续执行。

5.还可以使用 goto、return 或 throw 语句退出 do-while 循环。

6.do-while 的循环代码段只有一句时可以写成下面这种样子,不过并不建议:

do
	Console.WriteLine(" ");
while (true);

11.数组之基本语法

详细的可查看微软的C#文档

11.1.数组的概念

概念:

数组是一个存储相同类型元素的固定大小顺序集合。

数组类型是引用类型,声明数组变量只是为引用数组实例预留空间。 实际的数组实例是在运行时使用 new 运算符动态创建而成。 new 运算指定了新数组实例的长度,然后在此实例的生存期内固定使用这个长度。 数组元素的索引介于 0 到 Length - 1 之间。 new 运算符自动将数组元素初始化为其默认值(例如,所有数值类型的默认值为 0,所有引用类型的默认值为 null)。

11.2.数组的语法

1.声明
声明格式:数据类型[] 数组名;
如:int[] intArray; double[] doubleArray;
[] :指定数组的维度,默认是1 维数组。

多维数组的声明和初始化结合如下:

int[] a1 = new int[10];//一维
int[,] a2 = new int[10, 5];//二维
int[,,] a3 = new int[10, 5, 2];//三维
//从左开始依次为一维、二维、三维

2.初始化
初始化格式:数组名= new 数据类型[数组长度];

声明,初始化二合一格式:数据类型[] 数组名= new 数据类型[数组长度];

new : 创建数组实例。

注:左右两端的数据类型要一致。

11.3.赋值

赋值格式:数组名[下标] = 值;

声明,初始化,赋值三合一格式:
数据类型[] 数组名= new 数据类型[数组长度]{值1,值2,…,值N};
数据类型[] 数组名= new 数据类型[]{值1,值2,值3};

只要当数组长度确定为n且(n!=0),并且在声明和初始化的同时对数组进行赋值的时候,必须赋值相同个数。 否则就像下面这行代码,编译会不通过,会报错。

int[] shuzu = new int[10] {1,2,3,4,5,6,7,8,9}; 
//确定了数组长度n=10,却只赋值了9个
//编辑器报错:错误	CS0847	应为一个长度为“10”的数组初始值设定项

若数组长度不确定,则可以写成下面两种形式:

int[] a = new int[] {1, 2, 3};
int[] a = {1, 2, 3};

11.4.使用[取值]

使用格式:数组名[下标]

注意:

  1. 数组的下标是从0 开始。
  2. 数组的长度是固定,赋值与取值的时候,下标不能越界。

12.数组之元素遍历

12.1 for 循环遍历数组

数组长度获取:数组名.length;

for (int n = 0; n < 数组名.length; n++)
{
}

12.2 foreach 遍历数组

foreach(数组的数据类型 临时变量的标识符 in 数组名)
{
Console.WriteLine(临时变量);
}

string[] shengxiao = new string[]{ "子鼠","丑牛","寅虎", "卯兔", "辰龙" };
string tmp = "";
foreach (string n in shengxiao)
{
	tmp += n;//foreach会将该数组当前循环的元素存入临时变量之中方便使用
}

注意:

  1. 在 foreach 语句中,类型和标识符都是必需的。
  2. foreach会将该数组当前循环的元素存入临时变量之中方便使用;

12.3 数组元素的初始值

数组的初始化:数据类型[] 数组名= new 数据类型[数组长度];

new 运算符自动将数组元素初始化为其默认值,可以在数组初始化后用遍历一个个输出查看。

各种类型的数组的默认值为:

  • int[], 元素的值默认都是0;
  • float[], 元素的值默认都是0;
  • double[],元素的值默认都是0.0;
  • string[], 元素的值默认都是null;
  • bool[], 元素的值默认都是false;

13.数组之内存结构

13.1 值类型与引用类型

1.值类型
int,float,double,bool,char

内存:值类型的值存储在内存的栈中

演示:int 类型变量间传值

int a = 10;
int b = a;

注意:这个时候单独修改b 的值,a 的值不会发生改变,这种传递叫做值传递。 这个时候变量之间的传递就是拷贝一个具体的值给对方。

C#的入门
2.引用类型
数组,字符串

内存:引用类型的值存储在内存的堆中,而储存这个值的地址则储存在栈中。

演示:int 数组类型变量间传值

int[] intA = new int[]{00,111,222,444};
int[] intB = intA;

注意:这个时候单独修改intB 中元素的值,intA 的值是会发生改变,这种传递 叫做引用传递

这个时候 变量之间的传递 就是 拷贝一个地址给对方
C#的入门

13.2 数组的内存结构

1.数组在内存中是一块连续的存储空间存储的。
2.最低的地址对应第一个元素,最高的地址对应最后一个元素。

14.数组之二维数组

14.1 二维数组基本语法

1.声明与初始化
声明: 数据类型[,] 数组名;
初始化:数组名= new 数据类型[行数,列数];
声明,初始化二合一格式:
数据类型[,] 数组名= new 数据类型[行数,列数];

2.赋值与取值
赋值: 数组名[下标,下标] = 值;
取值: 数组名[下标,下标];

3.声明初始化赋值三合一:

数据类型[,] 数组名= new 数据类型[,]{
{0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1}
};

14.2 二维数组的遍历

遍历方法:for 语句的循环嵌套

循环次数的获取:数组名.GetLength(轴向下标); //获取某个轴向的元素总数。

轴向下标从0开始,依次表示第一维、第二维……

15.函数(方法)之基本语法

15.1 函数(方法)的概念

一段具有特定功能的代码,有自己的标识符,通过标识符可以重复调用这段代码来帮我们完成特定的事情。可以自己定义。

15.2 静态构造函数的声明与调用

1. Pascal 命名法
格式:每个单词的首字母都要大写其余小写,多用于类或方法
eg:int HighSchoolStudent = 0;

2. 函数的声明

修饰符 返回值类型函数名(函数参数)//两个以上用,隔开,要具有参数类型
{
函数代码段;
}

格式说明:
修饰符: 这个决定了变量或函数对于另一个类的可见性。
返回值类型: 如果不需要返回值就写void,就是无返回值的意思;
函数名: 当前功能函数的名字,要符合Pascal 命名规范;
参数列表: 如果不需要参数,小括号中可以直接留空;如果需要参数,格式为:参数类型 参数名;

例子:

static Bus(int routeNum)
{
	Console.WriteLine("Bus #{0} is created.", routeNum);
}

3. 函数的调用
调用形式:

  1. 函数名([实际参数]);

  2. 变量或常量.函数名([实际参数]);

自定义方法在Program中,实例成员要实例化对象【对象.方法】之后才能调用
自定义方法在Program中,静态成员可以直接调用函数,不需要【类名.方法】后才能调用
自定义方法在自己创建的类中,实例成员要实例化对象【对象.方法】之后才能调用
自定义方法在自己创建的类中,静态成员要用【类名.方法】之后才能调用

static void Main(string[] args)
{
	string name = "hh";
	TextOne(12);//第一种调用
	name = "hh".ToUpper();//第二种调用,函数返回"HH"
	Console.ReadKey();
}
static void TextOne(int n)
{            
}

注意:

  1. 如果函数只声明不调用,则函数中的代码不会被执行。
  2. 与C不一样的是,C#函数不要求在主函数的位置前声明函数。

15.3 函数(方法)的参数与返回值

1.形参与实参
形参: 形式参数,在定义函数的时候,在参数列表中定义的参数。
实参: 实际参数,在调用函数的时候,传递给函数的具体(可能是地址或者是数字)。

对于 static 函数,可以直接在函数调用的时候将对应数据类型的值填入括号内。

对于 public 函数无法实现。C#的入门

2.返回值
格式: return 与函数返回值类型相同的表达式

作用:

  1. 在函数中返回要返回的值;
  2. 立即结束函数;

注: 若函数无返回值,则 return 可以省略,或者写成 return ;,return后不可接表达式。
C#的入门

16.函数(方法)之函数重载

16.1 何为重载?

函数的名称相同,但是参数列表不同

调用该函数的时候,会根据不用的参数,自动选择合适的函数重载形式。
例:定义一个Add 的方法,实现整数,双精度浮点数相加操作。

static void Main(string[] args)
{
	Add (1,2);
}
static int Add (int a,int b)
{
	return a + b;
}
static double Add (double a, double b)
{
	return a + b;
}
static double Add (double a, int b)
{
	return a + b;
}

16.2 函数重载参数的情况

函数重载的参数个数和类型不能完全相同。

注意:函数的返回值和重载没有关系。

17.函数(方法)之高级参数

17.1 函数参数传递

调用函数,会将函数加载到栈空间中,传递的值,是直接拷贝了变量储存的值再传递,也就是 “值传递”

17.2 ref 参数

1.作用
使外部变量的地址传入函数中,从而在函数中改变外部变量的值。也就是"引用传递"。

2.要求
调用函数前,必须在函数外为传入函数的外部变量赋值。在函数中赋值与否都可以。

3.语法
形参和实参前面都要加 ref 关键字。

int num = 9;

//调用函数的时候实参前 + ref
Add(ref num)//声明函数的时候形参前 + ref
static void Add(ref int num){
num += 10;
}

17.3 out 参数

1.作用
函数中如果返回多个值。

2.要求
函数外可以不为 out 变量赋值,而函数内必须为其赋值

3.语法
形参和实参前面都要加 out 关键字。

int num = 10;
int a,b;
b = 10;

//调用函数的时候实参前 + out
Add(num,out a,out b)//声明函数的时候形参前 + out
static void Add(int num,out int a,out int b){
num += 10;
a = num/2;
b = num*num;
}

17.4 params参数

后续更新

18.函数(方法)之递归调用

18.1 何为递归?

递归算法:一种在函数内 直接或者间接调用自身函数或者方法的算法。

18.2 递归要点

  1. 递归时函数会一直调用自己,直到满足某个特定条件 才一层层结束。
    (所以递归要有一个结束条件 来 return)
  2. 递归调用时应该会传递一些参数,而且每次调用都会传递一个的参数。

18.3 递归的优缺点

优点:

  1. 代码简洁
  2. 在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。

缺点:

  1. 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率
  2. 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
  3. 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
相关标签: c#