在堆中创建二维数组



C++堆上申请二维数组

假设要申请的是double型大小m*n数组

有如下方法

  • 方法一:优点:申请的空间是连续的 缺点:较难理解
1  double (*d)[n] = new double[m][n]
  • 方法二:优点:容易理解 缺点:申请的空间不能连续且需要多个指针才能管理
double *d[m];

for (int i=0; i<m; i++)

   d[i] = new double[n];
 
 
 
 
真正实用的方法:创建A[m][n]
int **A = new int*[m];
	for (int i = 0; i < m; i++)
		A[i] = new int[n];

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 算法的基本概念 利用计算机算法为计算机解题的过程实际上是在实施某种算法。 (1)算法的基本特征 算法一般具有4个基本特征:可行性、确定性、有穷性、拥有足够的情报。 (2)算法的基本运算和操作 算法的基本运算和操作包括:算术运算、逻辑运算、关系运算、数据传输。 (3)算法的3种基本控制结构 算法的3种基本控制结构是:顺序结构、选择结构、循环结构。 (4)算法基本设计方法 算法基本设计方法:列举法、归纳法、递推、递归、减半递推技术、回溯法。 (5)指令系统 所谓指令系统指的是一个计算机系统能执行的所有指令的集合。 (2)数据结构研究的3个方面 ① 数据集合各数据元素之间所固有的逻辑关系,即数据的逻辑结构; ② 在对数据进行处理时,各数据元素在计算机的存储关系,即数据的存储结构; ③ 对各种数据结构进行的运算。 2. 逻辑结构 数据的逻辑结构是对数据元素之间的逻辑关系的描述,它可以用一个数据元素的集合和定义在此集合的若干关系来表示。数据的逻辑结构有两个要素:一是数据元素的集合,通常记为D;二是D上的关系,它反映了数据元素之间的前后件关系,通常记为R。一个数据结构可以表示成:B=(D,R) 其,B表示数据结构。为了反映D各数据元素之间的前后件关系,一般用二元组来表示。 例如,如果把一年四季看作一个数据结构,则可表示成:B =(D,R) D ={春季,夏季,秋季,冬季} R ={(春季,夏季),(夏季,秋季),(秋季,冬季)} 3. 存储结构 数据的逻辑结构在计算机存储空间的存放形式称为数据的存储结构(也称数据的物理结构)。 由于数据元素在计算机存储空间的位置关系可能与逻辑关系不同,因此,为了表示存放在计算机存储空间的各数据元素之间的逻辑关系(即前后件关系),在数据的存储结构,不仅要存放各数据元素的信息,还需要存放各数据元素之间的前后件关系的信息。 一种数据的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序、链接等存储结构。 顺序存储方式主要用于线性的数据结构,它把逻辑上相邻的数据元素存储在物理上相邻的存储单元里,结点之间的关系由存储单元的邻接关系来体现。 链式存储结构就是在每个结点至少包含一个指针域,用指针来体现数据元素之间逻辑上的联系。 1.2.2 线性结构和非线性结构 根据数据结构各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:线性结构与非线性结构。 (1)如果一个非空的数据结构满足下列两个条件: ① 有且只有一个根结点; ② 每一个结点最多有一个前件,也最多有一个后件。 则称该数据结构为线性结构。线性结构又称线性表。在一个线性结构插入或删除任何一个结点后还应是线性结构。栈、队列、串等都为线性结构。 如果一个数据结构不是线性结构,则称之为非线性结构。数组、广义表、树和图等数据结构都是非线性结构。 (2)线性表的顺序存储结构具有以下两个基本特点: ① 线性表所有元素所占的存储空间是连续的; ② 线性表各数据元素在存储空间是按逻辑顺序依次存放的。 元素ai的存储地址为:ADR(ai)=ADR(a1)+(i-1)k,ADR(a1)为第一个元素的地址,k代表每个元素占的字节数。 (3)顺序表的运算有查找、插入、删除3种。 1.3 栈 1. 栈的基本概念 栈(stack)是一种特殊的线性表,是限定只在一端进行插入与删除的线性表。 在栈,一端是封闭的,既不允许进行插入元素,也不允许删除元素;另一端是开口的,允许插入和删除元素。通常称插入、删除的这一端为栈顶,另一端为栈底。当表没有元素时称为空栈。栈顶元素总是最后被插入的元素,从而也是最先被删除的元素;栈底元素总是最先被插入的元素,从而也是最后才能被删除的元素。 栈是按照“先进后出”或“后进先出”的原则组织数据的。例如,枪械的子弹匣就可以用来形象的表示栈结构。子弹匣的一端是完全封闭的,最后被压入弹匣的子弹总是最先被弹出,而最先被压入的子弹最后才能被弹出。 二级公共基础知识速学教程 2. 栈的顺序存储及其运算 栈的基本运算有3种:入栈、退栈与读栈顶元素。 ① 入栈运算:在栈顶位置插入一个新元素; ② 退栈运算:取出栈顶元素并赋给一个指定的变量; ③ 读栈顶元素:将栈顶元素赋给一个指定的变量。 1.4 队列 1. 队列的基本概念 队列是只允许在一端进行删除,在另一端进行插入的顺序表,通常将允许删除的这一端称为队头,允许插入的这一端称为队尾。当表没有元素时称为空队列。 队列的修改是依照先进先出的原则进行的,因此队列也称为先进先出的线性表,或者后进后出的线性表。例如:火车进遂道,最先进遂道的是火车头,最后是火车尾,而火车出遂道的时候也是火车头先出,最后出的是火车尾。若有队列: Q =(q1,q2,…,qn) 那么,q1为队头元素(排头
Javascript Basic 1、Javascript 概述(了解) Javascript,简称为 JS,是一款能够运行在 JS解释器/引擎 的脚本语言 JS解释器/引擎 是JS的运行环境: 1、独立安装的JS解释器 - NodeJS 2、嵌入在浏览器的JS解释器 JS的发展史: 1、1992年 Nombas 开发了一款语言 ScriptEase 2、1995年 Netscape(网景) 开发了一款语言 LiveScript,更名为 Javascript 3、1996年 Microsoft(微软) 开发了一款语言 JScript 4、1997年 网景 将Javascript 1.1 提供给了ECMA(欧洲计算机制造商联合会),ECMA 获取了 JS 的核心,称之为 ECMA Script (ES) 完整的JS组成: 1、核心(ES) 2、文档对象模型(Document Object Model) - DOM 允许让 JS 与 HTML 文档打交道 3、浏览器对象模型(Browser Object Model) - BOM 允许让 JS 与 浏览器进行交互 JS是一款基于对象的编程语言 2、JS的基础语法 1、浏览器内核 内核负责页面内容的渲染,由以下两部分组成: 1、内容排版引擎 - 解析HTML/CSS 2、脚本解释引擎 - 解析Javascript 2、搭建JS运行环境(重点) 1、独立安装的JS解释器 - NodeJS console.log("Hello World"); console.log('Hello World'); 2、使用浏览器的内核(JS解释引擎) 1、在浏览器的控制台(Console),输入脚本并执行 2、将JS脚本代码嵌入在HTML页面执行 1、采用HTML元素事件执行JS代码 事件 : 1、onclick 当元素被点击时执行的操作 ex: 当按钮被点击时,在控制台输出 Hello World 2、将JS脚本编写在 [removed][removed] 并嵌入在HTML文档的任何位置 [removed] console.log("... ..."); [removed]("Hello Wolrd"); [removed] 3、将JS脚本编写在外部独立的JS脚本文件(***.js) 步骤: 1、编写JS脚本文件 2、在HTML引入脚本文件 [removed][removed] 练习: 1、先创建一个 base.js 的文件 2、在文件执行以下代码 console.log(" .... ... "); [removed](" ... ... "); window.alert("这是在外部脚本文件的内容"); 3、在 html 文档,引入 base.js 文件 3、JS调试 当代码编写出现错误时,在运行的时候,在错误位置会停止 碰到错误代码,会终止当前语句块的执行,但不影响后续块的执行 [removed][removed] 为一块 4、JS语法 1、语句 - 可以被JS引擎执行的最小单元 由表达式、关键字、运算符 来组成的 严格区分大小写 :name 和 Name 所有的语句都是以 ; 来表示结束 所有的标点符号都是英文的 ; 和 ; . 和 。 : 和 : " 和 “ ' 和 ‘ () 和 () [] 和 【】 {} 和 {} 2、注释 单行注释: // 多行注释: /* */ 3、变量 与 常量 1、变量 1、什么是变量 用来存储数据的一个容器 2、声明变量(重点) 1、声明变量 var 变量名; 2、为变量赋值 变量名=值; 3、声明变量并赋初始值 var 变量名=值; 注意: 1、变量在声明时没有赋值的话,那么值为 undefined 2、声明变量允许不使用var关键字,但并不推荐 练习: 1、创建一个网页 04-variable.html 2、声明一对 [removed][removed],并完成以下变量的声明 1、声明一个变量用于保存用户的姓名,并赋值为 "张三丰"; 2、声明一个变量用于保存用户的年龄,赋值 68 3、如何 将变量的数据 打印在控制台上?? 4、一条语句声明多个变量 var 变量名1=值,变量名2=值,变量名3; 3、变量名命名规范 1、由字母,数字,下划线以及 $ 组成 var user_name; 正确 var user-name; 错误 var $uname; 正确 2、不能以数字开头 var 1name;错误 3、不能使用JS的关键字 和 保留关键字 4、变量名不能重复 5、可以采用"驼峰命名法",小驼峰命名法使用居多 6、最好见名知意 var a; var uname; 4、变量的使用 1、为变量赋值 - SET操作 只要变量出现在 赋值符号(=)的左边一律是赋值操作 var uname="张三丰"; uname="张无忌"; 2、获取变量的值 - GET操作 只要变量没有出现在赋值符号(=)的左边,一律是取值操作 var uname="wenhua.li";//赋值操作 console.log(uname); var new_name = uname; new_name 是赋值操作 uname 是取值操作 uname = uname + "bingbing.fan"; 赋值符号出现的话,永远都是将右边的值,赋值给左边的变量(从右向左运算) 2、常量 1、什么是常量 在程序,一旦声明好,就不允许被修改的数据 2、声明常量 const 常量名=值; 常量名在命名时采用全大写形式 作业: 1、声明一个变量 r ,来表示一个圆的半径,并赋值 2、声明一个常量PI ,来表示圆周率3.14 3、通过 r 和 PI 来计算 该圆的周长,保存在变量l 周长 = 2 * π * 半径 4、通过 r 和 PI 来计算 该圆的面积,保存在变量s 面积 = π * r * r; 5、在控制台打印输出 半径为 * 的圆的周长是 * 半径为 * 的圆的面积是 * 笔记本名称:ThinkPad E460 笔记本价格:3000 笔记本库存:100台 1、运算符 1、位运算符 1、作用 将数字转换为二进制后进行运算 只做整数运算,如果是小数的话,则去掉小数位再运算 2、位运算 1、按位 与 :& 语法 :a & b 特点 :将 a 和 b 先转换为二进制,按位比较,对应位置的数字都为1的话,那么该位的整体结果为1,否则就为0 ex:5 & 3 5 :101 3 :011 =========== 001 结果 :1 使用场合:任意数字与1做按位与操作,可以判断奇偶性,结果为1,则为奇数,否则为偶数 0 :0 1 :1 2 :10 3 :11 4 :100 5 :101 5 & 1 101 001 ========== 001 4 & 1 100 001 ==== 000 2、按位 或 :| 语法 :a | b 特点 :将 a 和 b 转换为 二进制,按位比较,对应位置的数字,至少有一位为1的话,那么该为的整体结果就为1,否则为 0 ex : 5 | 3 101 011 ======== 111 结果为 :7 适用场合:任何小数与0 做 按位或的操作,可以快速转换为整数(并非四舍五入) 5 | 0 101 000 ==== 101 3、按位 异或 :^ 语法:a ^ b 特点:将 a 和 b 先转换为二进制,按位操作,对应位置上的两个数字,相同时,该位整体结果为0,不同时,该位的整体结果为 1 使用场合:快速交换两个数字 5 ^ 3 101 011 ========== 110 结果为 6 练习: var a = 5; var b = 3; a = a ^ b; b = b ^ a; a = a ^ b; console.log(a,b); 3、赋值 和 扩展赋值运算符 1、赋值运算符 := 2、扩展赋值运算符 +=,-=,*=,/=,^=,... ... a += b; ==> a = a + b; a = a + 1; ==> a += 1; ==> a++ ; ==> ++a 3、练习 1、从弹框,分两次输入两个数字,分别保存在 a 和 b 2、如果 a 大于 b的话 ,则交换两个数字的位置 使用 短路&&,扩展赋值运算符,位运算 4、条件运算符(三目运算) 单目(一元)运算符 :++,--,! 双目(二元)运算符 :+,-,*,/,%,>,= 18 ? "你已成年" : "你未成年"; 练习: 从弹框录入一个数字表示考试成绩(score) 如果 成绩为 100 分 ,提示 :满分 如果 成绩 >= 90 分 ,提示 :优 如果 成绩 >= 80 分 ,提示 :良 如果 成绩 >= 60 分 ,提示 :及格 否则 :提示 不及格 2、函数 1、什么是函数 函数(function),是一段预定义好,并且可以被反复使用的代码块 预定义好 :事先声明,但不是马上执行 反复使用 :可以被多次调用 代码块 :包含多条可执行的语句 2、函数的声明与调用 1、普通函数 语法: function 函数名(){ 语句块; } 调用:在JS任何的合法位置处,都可以通过 函数名() 的方式进行调用 练习: 1、声明一个函数,名称为 change 2、在函数 1、通过弹框,分两次,录入两个数字,保存在 a 和 b 2、先打印 a 和 b的值 3、如果 a > b 的话,则交换两个数字的位置 4、再打印 a 和 b的值 3、在网页创建一个按钮,点击按钮时,完成 change 函数的调用 2、带参函数 1、定义语法: function 函数名(参数列表){ 语句块; } 参数列表: 可以由0或多个参数的名称来组成,多个参数的话间用 , 隔开 定义函数时的参数列表,都称为 "形参(形式参数)" 2、调用语法 任意合法JS位置处 函数名(参数列表); 调用函数时,所传递的参数列表,称之为"实参(实际参数)" 3、练习 1、定义一个函数 change ,该函数接收两个参数(a,b) 2、在函数体,如果 a 大于 b的话,则交换两个数字的位置,再打印输出结果 3、通过一个按钮调用函数,并且将 两个数字传递给函数 4、常用带参函数 parseInt(变量); parseFloat(变量); Number(变量) console.log(""); 3、带返回值的函数 var result = parseInt("35.5"); 1、什么是返回值 由函数体内 带到 函数体外的数据,叫做"返回值" 2、语法 function 函数名(参数列表){ 语句块; return 值; } 允许通过一个变量来接收调用函数后的返回值 var 变量名 = 函数名(实参列表); 3、练习 定义一个可以接收三个Number参数的方法(名称为getMax),在该方法,计算并返回 最大的数值 3、作用域 1、什么事作用域 指的是变量和函数的可访问范围,作用域分为以下两类 1、函数作用域 只在定义的函数内允许访问变量 和 函数 2、全局作用域 一经定义,在任何位置处都能访问 2、函数作用域的变量 在某个函数声明的变量,就是函数作用域的变量,也可以称之为 "局部变量"。 function calSum(){ var a = 3; var b = 5; console.log(a,b); // 输出 3 5 } console.log(a,b); //错误,因为 a ,b已经出了它的定义范围 练习: 1、定义一个 函数calSum,声明两个变量a,b,并赋值,并且在函数内打印两个变量 2、定义一个 函数calMax,直接打印输出 a,b,观察结果 3、全局作用域的变量 一经声明了,任何位置都能使用。也称为 "全局变量" 声明方式: 1、将变量声明在最外层,所有的function之外 [removed] var a = 15; var b = 18; function showMsg(){ console.log(a); console.log(b); } [removed] 2、声明变量时,不使用var关键字,一律是全局变量,但有风险 建议: 全局变量 ,尽量都声明在 所有的 function 之外 声明变量时,也一律都使用 var 关键字 4、声明提前 Q : function show(){ console.log(num);//报错 } 1、什么是声明提前 JS程序在正式执行前,会将所有var声明的变量以及function声明的函数,预读到所在作用域的顶端。但赋值还保留在原位置上 2、建议 最好将变量的声明 和 函数的声明都集到所在作用域的最顶端 5、按值传递 Q : function change(a,b){ a ^= b; b ^= a; a ^= b; console.log("在change函数"); console.log(a,b); } function testChange(){ var a = 15; var b = 18; console.log("调用前:"); console.log(a,b);//15 18 change(a,b); console.log("调用后:"); console.log(a,b);//15 18 } 1、什么是按值传递 基本数据类型的数字在做参数传递时,实际上时传递的实参的副本到函数,原始数据并未发生过改变 2、建议 基本数据类型的数据在做参数传递时,尽量不要在函数修改,因为即便修改,也不会影响原数据的 6、局部函数 将函数 再声明在某个函数内,就是局部函数 [removed] function show(){ function inner(){ console.log("show 的 inner 函数"); } inner();//正确 } inner(); // 错误,inner 是局部函数,只能在 show被调用 [removed] 7、全局函数 ECMAScript预定义的全局函数,可供用户直接使用 1、paseInt(); 2、parseFloat(); 3、Number(); 4、isNaN(); 5、encodeURI() URL:Uniform Resource Locator :统一资源定位符/器 URI:Uniform Resource Identifier : 统一资源标识符 1、作用 对统一资源标识符格式的字符串进行编码,并返回编码后的字符串 编码:将多字节的字符,编译成多个单字节的字符 6、decodeURI() 1、作用 对已编码的URI进行解码(将已编码内容再转换回文) 7、encodeURIComponent() 在 encodeURI 基础之上,允许将 特殊符号(:,/,...) 进行编码 8、decodeURIComponent() 9、eval() 作用:计算并执行以字符串方式表示的JS代码 var msg = "console.log('Hello World');"; eval(msg);//将 msg 作为 JS代码去执行 练习: 1、创建页面,通过弹框,输入一段JS代码 2、通过 eval() ,将输入的JS代码执行一次 3、递归调用 1、什么是递归 递归指的是在一个函数,又一次的调用了自己 2、递归的实现 1、边界条件 2、递归前进 - 继续调自己 3、递归返回 - 向上返回数据 3、解决问题 1、计算某个数字的阶乘 5的阶乘 5!=5*4*3*2*1 5! = 5 * 4! 4! = 4 * 3! 3! = 3 * 2! 2! = 2 * 1! 1! = 1 /*计算 n 的阶乘*/ function f(n){ ... ... } 4、作业 已知下列数列 (菲波那切数列) 1,1,2,3,5,8,13,21,34,55,... ... 已知 第一个数为1,第二个数为1 从第三个数开始,每个数字等于前两个数之和 问题:通过一个函数,求某个位置上的数字(用递归实现) https://www.baidu.com/s?wd=佳能 https://www.baidu.com/s?wd=尼康 function test(){ console.log("Hello World"); } var result = test(); console.log(result); =========================== 输出结果: Hello World undefined [removed] var g_uname = "sanfeng.zhang"; function showName(){ var uname = "wuji.zhang"; console.log(uname);//wuji.zhang } [removed] function test(){ console.log(num); // 打印 :undefined var num = 15; } 演变成: function test{ var num; // 先声明,但不赋值(自动完成) console.log(num); // 打印 :undefined num=15;// 只赋值,不声明 } 推荐写法: function test(){ var num = 15; console.log(num); // 打印 :15 } *********************************************** 作业: 1,1,2,3,5,8,13,21,34,55,... ... 已知: 该数列前两个数都是 1 从第三个数开始,每个数字等于前两个数的和 /*通过 f 函数,计算第 n 个数字是多少*/ function f(n){ } f(5) : 求第五个数字是多少 ================================================= 1、分支(选择)结构 1、流程控制结构 1、顺序结构 - 自顶向下的执行 2、分支结构 - 有条件的选择某一段代码去执行 3、循环结构 - 有条件的选择某一段代码反复执行 程序 = 数据结构 + 算法 2、练习 编写一个收银柜台收款程序,根据商品单价,购买数量以及收款金额计算并输出应收金额和找零 数据:商品单价,购买数量,收款金额,应收金额,找零 输入数据:商品单价(price),购买数量(count),收款金额(money) 输出数据:应收金额(total),找零(change) Q : 当商品总价>=500时,享受8折优惠 3、if 结构 语法: if(条件){ 满足条件要执行的语句块 } 流程: 1、判断条件 2、如果条件为真,则执行 语句块 的内容 3、如果条件为假,则跳过语句块去执行其他内容 注意: 1、if的条件,最好是一个boolean的值,如果不是boolean类型,则会自动转换 以下情况,条件会自动转换为 false if(0) if(0.0) if(undefined) if(null) if("") if(NaN) if(35.5){ //真 } var num; if(num){ //假 } if("李文华真帅"){ //真 } 2、if 后的 { } 是可以被省略的,如果省略的话,只控制 if 下的第一条语句 问题:考虑异常情况,如果收款金额小于应收金额 如果 收款金额大于等于应收金额,则正常执行 否则 则给出异常提示 4、if ... else ... 结构 语法: if(条件){ 满足条件时,执行的语句块 }else{ 不满足条件时,执行的语句块 } 练习: 1、从弹框,输入一个年份信息 2、判断该年是否为闰年,并给出提示 输入年:2009 2009年不是闰年 输入年:2012 2012年是闰年 5、if ... else if ... 结构 语法: if(条件1){ //满足条件1时,要执行的语句块 }else if(条件2){ //满足条件2时,要执行的语句块 }else if(条件n){ //满足条件n时,要执行的语句块 }else{ //以上条件都不满足时,要执行的语句块 } 练习: 从弹框,分三次录入 年,月,日 判断该日是该年的第多少天 提示: 1,3,5,7,8,10,12 :每月31天 4,6,9,11 :每月30天 2 :闰年29天,平年28天 2017年8月21日 : 31+29+31+30+31+30+31+21 6、switch ... case Q : 从弹框录入 1-7 的任一一个数字 录入 1 :输出 :今天吃红烧肉 录入 2 :输出 :今天吃红烧排骨 录入 3 :输出 :今天吃红烧丸子 录入 4 :输出 :今天红烧鱼 录入 5 :输出 :今天吃烤羊腿 录入 6 :输出 :今天休息 录入 7 :输出 :今天休息 场合:等值判断 语法: switch(变量){ case 常量值1: 语句块1; break;//通过 break 结束switch的执行(可选 ) case 常量值2: 语句块2; break;//可选 default: 语句块n; break;//可选 } 练习: 1、使用 特殊的 switch 结构完成下列要求 输入年 ,月 输出 该月有多少天 1,3,5,7,8,10,12 : 31天 4,6,9,11 : 30 天 2 : 平年28天,闰年29天 ex: 年:2017 月:5 输出:31天 1、使用 特殊的 switch 结构完成下列要求 2、改版日期计算器(难度) 输入年月日,判断该日是这一年的多少天 var month = Number(prompt()); var totalDays = 0; switch(month-1){ case 11: totalDays += 30; case 10: totalDays += 31; case 9: totalDays += 30; ... ... } 2、循环结构 1、问题 1、控制台上打印一句Hello World 2、控制台上打印十句Hello World 3、控制台上打印10000句 Hello World 4、改版第3步,增加 第 ? 句 Hello World 第 1 句 Hello World 第 2 句 Hello World ... 第 10000 句 Hello World 5、改版第4步 将 Hello Word 替换成 "你好 世界" 练习: 6、打印输出 1-100之间所有数字的和 2、什么是循环结构 循环,就是一遍又一遍的执行相同或相似的代码 两个重要的要素: 1、循环条件 :循环执行的次数 2、循环操作 :循环体,要执行相同 或 相似的代码是什么 3、while 循环 1、语法 while(条件){ //循环操作 } 流程: 1、判断条件 2、如果条件为真,则执行循环操作,然后再次判断条件。当条件为假时,则退出循环结构 4、循环的流程控制语句 1、break 破坏了整个循环的执行 - 结束循环 2、continue 结束本次循环,继续执行下次循环 练习: 1、使用 continue ,打印输出 1-100之间所有偶数的和 2、让用户循环从弹框录入信息,并将信息打印在控制台上,直到用户输入 exit 为止 输入数据:sanfeng.zhang sanfeng.zhang 输入数据:wuji.zhang wuji.zhang 输入数据:exit 3、猜数字游戏 1、随机生成一个 1-100 之间的数字 Math.random() 返回 0-1 之间的小数 var r = parseInt(Math.random()*100)+1; 2、要求用户输入一个整数 若比生成的随机数大,则输出 “猜大了” 若比生成的随机数小,则输出 “猜小了” 直到用户 猜对为止 3、如果用户输入exit ,也可以退出游戏 3、作业 用户从弹框输入年,月,日,判断该日是星期几? 1900年1月1日 是星期1 推荐思路: 从1900年1月1日 到 输入的年月日 共有多少天求出来,与 7 取余 1、通过一个循环计算从1900年 到输入年前一年有多少天? 2、再从1月,到输入月份的前一个月共有多少天? 3、再累加 输入的日 4、用整体结果 与7取余,给出提示 附加作业: 将 输入的月份的日历打印输出 2017年8月 日 一 二 三 四 五 六 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 while(i > 0){ switch(i){ case 1: console.log(); break; case 2: console.log(); continue; // 作用在 while } } switch(i){ case 1: console.log(); continue; // 错误 } =================================================== 1、循环结构 1、do ... while() do{ 循环操作 }while(循环条件); do...while VS while 1、while 先判断条件,再执行循环操作 如果条件不满足,循环一次都不执行 2、do ... while 先执行循环操作,再判断循环条件 即便条件不满足,也要执行一次循环操作 练习: 将猜数字的游戏,改版成 do ... while 循环版本 2、for() 循环 1、语法 for(表达式1;表达式2;表达式3){ //循环操作 } 表达式1:循环条件的声明 表达式2:循环条件的判断(boolean) 表达式3:更新循环条件(i++,... ...) 执行过程: 1、计算表达式1的值 2、计算表达式2的值,如果结果为true则执行循环体,否则退出 3、执行循环体 4、执行表达式3 5、再计算表达式2的值,为true执行循环体,否则退出 while do...while 和 for 的使用场合: 1、while 和 do...while 适合使用在不确定循环次数的循环下 2、for 适合使用在确定循环次数时使用 练习: 1、定义一个函数,打印乘法口诀表的任一一行 输入要打印的行数:5 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 2、定义一个函数,打印 1/1+1/3+1/5+...1/999的和 3、定义一个函数,判断指定的数字是否为素数 素数:只能被1 和 它本身整除的数字 输入一个数字:7 是素数 循环条件:从 2 开始 ,到输入的数字-1结束 循环操作:判断输入的数字能否被循环条件整除,能被整除,则不是素数,不能被整除,是素数 4、有一对兔子,从出生后的第3个月起都生一对兔子,小兔子长到第3个月后每个月又生一对兔子,假如兔子都不死的话,打印输出 前10个月,每个月有多少对兔子 n1 n2 n1 n2 n1 n2 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 2、三个表达式的特殊用法 1、for(;;) 三个表达式可以任意的省略,不推荐省略 2、for 第一个 和 第三个表达式的多样化 for(var i=1,j=2;i=1;i++,j--){ console.log(i+"+"+j+"="+(i+j)); } 3、嵌套循环 在一个循环的内部又出现一个循环 for(var i=0;i<10;i++){ // 外层循环 for(var j=0;j<10;j++){ // 内层循环 } } 外层循环执行一次,内层循环要执行一轮 练习: 1、控制台输出下面的图形 ****** ****** ****** ****** ****** * ** *** **** ***** * *** ***** ******* ********* 2、数组 1、什么是数组 数组,即一组数据,使用一个变量来存放多个数据 数组的元素是按照"线性"书序来排列的,所以也可以称之为是一个 "线性表" 2、声明数组 1、声明一个空数组变量 var 数组名 = []; 2、声明数组并赋初始值 var 数组名 = [数据1,数据2,数据3,... ...]; 3、声明一个空数组变量 var 数组名 = new Array(); 4、声明一个数组并赋初始值 var 数组名 = new Array(数据1,数据2,... ...); 练习: 1、创建一个函数,分别用两种不同的方式创建数组(arr1,arr2) arr1保存 :张三丰,张翠山,张无忌 arr2保存 :金花婆婆,殷素素,赵敏 2、直接将数组打印在控制台上 3、数组是引用类型的对象 引用类型的对象,是将数据保存在 "" 的 在"栈",会对 "" 数据的地址进行引用 1、将数组赋值给其它变量时,实际赋的是数组的地址 练习: 1、创建一个数组 arr1 ,包含"张无忌","赵敏" 2、将 arr1 赋值给 arr2 3、将 arr2 的第一个元素更改为 "金花婆婆",打印输出 arr1 和 arr2 的所有元素 2、数组在做参数的时候,传递进去的实际上是地址(按引用传递 即 按地址传递) 练习: 1、创建一个数组array,保存"张无忌","赵敏"俩元素 2、创建一个函数change,接收一个数组作为参数,在函数体内,将第一个元素更改为"金花婆婆" 3、调用change函数,并将array数组作为参数,调用完成后,打印array的值,观察结果 4、修改change函数,在函数体内,将传递进来的数组重新new一个Array("孙悟空","猪八戒") 5、重复步骤三,观察结果 3、null 表示的是让引用类型的对象不再指向任何空间.一般用于主动释放对象的空间 //让 array 指向数组的地址 var array = ["wuji.zhang","zhaomin"]; //释放 array空间,不再指向任何地址 array = null; 4、访问数组的元素 赋值,取值,都是使用 下标 来完成的 1、为数组的元素赋值 数组名[下标] = 值; var array = ["张无忌","赵敏"]; array[1] = "周芷若"; array[2] = "小昭";//增加一个新元素,在第3个位置处 array[5] = "灭绝师太"; 2、获取数组的值 数组名[下标]; 3、length属性 作用:获取数组的长度(数组元素的个数) 1、允许将数组的length属性值设置为0,来完成数组元素的清空操作 2、配合循环 做数组的循环遍历操作 var array = ["张无忌","赵敏"]; 输出: 第1名 :张无忌 第2名 :赵敏 for(var i=0;i"sanfeng.zhang"]; 用字符串做下标,就是关联数组 var array = []; //声明一个空数组 array["西游记"] = "古代神话故事"; array["红楼梦"] = ["贾宝玉","林黛玉","刘姥姥"]; Q : array.length 注意: 1、关联数组是不算做数组内容的,不记录到 length 2、关联数组只能通过 字符串 做下标取值 3、允许通过 for...in 来循环遍历关联数组的字符串下标(也能遍历出内容的数字下标) 3、冒泡排序 [23,9,78,6,45] -> [6,9,23,45,78] 冒泡排序:车轮战,两两比较,小的靠前 特点: 1、轮数 :共比较了 length - 1 轮 2、每轮比较的次数 :随着轮数的增加,次数反而减少 代码: 双层循环表示整个排序的过程 1、外层循环 :控制比较的轮数,从1开始,到length-1(能取到)结束 2、内层循环 :控制每轮比较的次数,并且也要表示参与比较的元素的下标,从0开始,到 length-1-i(轮数变量) [23,9,78,6,45] 共5个元素 外层:从 1 开始,到 4 结束 内层: 第一轮 第一次:从 0 开始 ,到 3 结束 第二轮 第一次:从 0 开始 ,到 2 结束 ... ... 2、数组的常用方法 1、toString() 将一个数组转换为字符串 语法:var str = 数组对象.toString(); 2、join() 作用:将数组的元素通过指定的分隔符连接到一起,并返回连接后的字符串 语法:var str = 数组对象.join("&"); 练习:(10分钟) 1、使用数组实现由 * 组成的三角形 直角三角形,等腰三角形 * ** *** **** ***** 2、使用数组实现 九九乘法表 3、连接数组 函数:concat() 作用:拼接两个或更多的数组,并返回拼接后的结果 语法:var result=arr1.concat(arr2,arr3,arr4); 注意: 1、concat不会改变现有数组,而是返回拼接后的 2、每个参数不一定非是数组,也可以是普通的数据 练习: 1、声明两个全局数组,分别保存一些国家的名称 数组1:选的国家 sel = ["国","美国","俄罗斯","日本"]; 数组2:备选国家 unsel=["朝鲜","越南","老挝","柬埔寨"]; 2、将 备选国家 全部移入到 入选国家,打印数据 结果: sel = ["国","美国","俄罗斯","日本","朝鲜","越南","老挝","柬埔寨"]; unsel=[]; 4、获取子数组 函数:slice() 作用:从指定数组,截取几个连续的元素组成的新数组 语法:arr.slice(start,[end]); start:从哪个下标处开始截取,取值为正,从前向后取,取值为负,从后向前算位置。 0 1 2 3 var arr=["国","美国","日本","英国"]; -4 -3 -2 -1 end : 指定结束处的下标(不包含),该参数可以省略,如果省略的话,就是从start一直截取到结束 注意: 1、该函数不会影响现有数组,会返回全新的数组 练习: var arr=["国","美国","日本","英国"]; 取出 美国 和 日本 组成的一个子数组 5、修改数组 函数:splice() 作用:允许从指定数组,删除一部分元素,同时再添加另一部分元素 语法: arr.splice(start,count,e1,e2,... ...); start:指定添加或删除元素的起始位置/下标 count:要删除的元素个数,取值为0表示不删除 e1,e2,... ... : 要增加的新元素,允许多个 返回值:返回一个由删除元素所组成的数组 注意:splice 会改变现有数组 练习: 在 sel 和 unsel 的基础上完成下列操作 1、从备选国家(unsel),选择一个国家移入到 选的国家(sel) 推荐步骤: 1、用户输入 :越南 从 unsel 将越南移除 再将 越南 sel 的末尾处 2、用户输入 :墨西哥 提示:国家不存在 6、数组的反转 作用:颠倒数组元素的顺序 语法:arr.reverse(); 注意:该函数会改变当前数组的内容 练习: 1、声明一个整数数组 2、倒序打印输出内容(使用reverse()) 7、数组排序 函数:sort(); 语法:arr.sort(排序函数); 作用:默认情况下,按照元素的Unicode码大小按升序排列 特殊:允许自己指定排序函数,从而实现对数字的升序和降序的排列 语法:arr.sort(排序函数); ex: var arr = [12,6,4,72,115,89]; //排序函数(升序) function sortAsc(a,b){ return a-b; } arr.sort(sortAsc); 原理: 1、指定排序函数 如 sortAsc,定义 两个参数,如 a 和 b。数组会自动传递数据到 sortAsc 里面去,如果返回值是>0的数,则交换两个数的位置,否则不变 使用匿名函数完成排序: arr.sort(function(a,b){return a-b;}); 练习: 1、声明一个整数数组,随意定义数字 2、页面上添加两个按钮,一个"升序"按钮,一个"降序"按钮 3、点击 升序 按钮时,数组按升序排序,并打印 4、点击 降序 按钮时,数组按降序排序,并打印 8、进出栈操作 JS是按照标准的"栈式操作"访问数组的 所有的"栈式操作"的特点都是"后进先出" "栈式操作"讲究的"入栈"和"出栈" 1、push() 入栈,在栈顶(数组的尾部)添加指定的元素,并返回新数组的长度 var arr = [10,20,30]; //向栈顶增加新数据 40 var len = arr.push(40); //len 保存的是 40 入栈后 arr 的长度,值是4 2、pop() 出栈,删除并返回栈顶的(数组尾部)元素 var arr = [10,20,30]; var r1 = arr.pop();//arr = [10,20] var r2 = arr.pop();//arr = [10] ============================== arr : r1 : 30 r2 : 20 3、shift() 删除数组头部的(第一个)元素并返回 var arr = [10,20,30]; var r1 = arr.shift(); ============================ arr : [20,30] r1 : 10 4、unshift() 作用:在数组的头部(第一个)元素位置处,增加新元素 var arr = [10,20,30]; arr.unshift(40); arr : [40,10,20,30] 3、二维数组 1、什么是二维数组 一个数组的元素又是一个数组,也可以称之为:数组的数组 2、创建二维数组 var names=[ ["孙悟空","猪八戒","沙悟净"], ["潘金莲","西门庆","武大郎"], ["贾宝玉","刘姥姥","林黛玉"], ["貂蝉","貂蝉","貂蝉"] ]; //获取猪八戒 console.log(names[0][1]); //获取林黛玉 console.log(names[2][2]); 4、作业 1、通过 进出栈 操作,完成 十进制对二进制的转换 1、创建一个函数 decode2bin,接收一个十进制的数字做参数 2、函数体,将参数 拆成二进制数字,保存在一个数组 3、将数组的元素使用""连接,并返回 十进制 转 二进制 :除2取余法 num : 5 num % 2 : 结果为 1 num = parseInt(num / 2) : 结果 2 num % 2 : 结果为 0 num = parseInt(num / 2) : 结果 1 num % 2 : 结果为 1 num = parseInt(num / 2) : 结果 0 结果为 0 时则不用再继续 %2 2、根据省份信息,查询城市列表 1、创建一个全局数组,保存几个省份名称 2、创建一个全局二维数组,保存不同省份的城市信息(数据要与省份数组的数据对应的上) 3、创建一个函数,接收一个省份名称做参数,打印该省份对应的所有的城市信息
在一小时内学会 C#。使用例程,简单却完整的探索 C# 语言的构造和特点。本文特别适合有 C++ 基础却没有太多精力学习 C# 的读者。 关于作者 Aisha Ikram 我现在在英国一家软件公司任技术带头人。我是计算机科学的硕士。我主要使用 .NET 1.1/2.0, C#, VB.NET, ASP.NET, VC++ 6, MFC, ATL, COM/DCOM, SQL Server 2000/2005等。最近我在学习 .NET 3.x 的全部内容。我的免费源代码和文章网站是 http://aishai.netfirms.com 职业:团队带头人 位置:英国 简介 C# 是一种具有 C++ 特性,Java 样式及 BASIC 快速建模特性的编程语言。如果你已经知晓 C++ 语言,本文将在不到一小时的时间内带你快速浏览 C# 的语法。如果熟悉 Java 语言,Java 的编程结构、打包和垃圾回收的概念肯定对你快速学习 C# 大有帮助。所以我在讨论 C# 语言构造的时候会假设你知道 C++。 本文通过一系列例程以简短但全面的方式讨论了 C# 语言构造和特性,所以你仅需略览代码片刻,即可了解其概念。 注意:本文不是为 C# 宗师而写。有很多初学者的 C# 文章,这只是其之一。 接下来关于 C# 的讨论主题: ? 编程结构 ? 命名空间 ? 数据类型 ? 变量 ? 运算符与表达式 ? 枚举 ? 语句 ? 类与结构 ? 修饰符 ? 属性 ? 接口 ? 函数参数 ? 数组 ? 索引器 ? 装箱与拆箱 ? 委托 ? 继承与多态 以下主题不会进行讨论: ? C++ 与 C# 的共同点 ? 诸如垃圾回收、线程、文件处理等概念 ? 数据类型转换 ? 异常处理 ? .NET 库 编程结构 和 C++ 一样,C# 是大小写敏感的。半角分号(;)是语句分隔符。和 C++ 有所区别的是,C# 没有单独的声明(头)和实现(CPP)文件。所有代码(类声明和实现)都放在扩展名为 cs 的单一文件。 看看 C# 的 Hello World 程序。 复制内容到剪贴板 代码: using System; namespace MyNameSpace { class HelloWorld { static void Main(string[] args) { Console.WriteLine ("Hello World"); } } } C# 所有内容都打包在类,而所有的类又打包在命名空间(正如文件存与文件夹)。和 C++ 一样,有一个主函数作为你程序的入口点。C++ 的主函数名为 main,而 C# 是大写 M 打头的 Main。 类块或结构定义之后没有必要再加一个半角分号。C++ 是这样,但 C# 不要求。 命名空间 每个类都打包于一个命名空间。命名空间的概念和 C++ 完全一样,但我们在 C# 比在 C++ 更加频繁的使用命名空间。你可以用点(.)定界符访问命名空间的类。上面的 Hello World 程序,MyNameSpace 是其命名空间。 现在思考当你要从其他命名空间的类访问 HelloWorld 类。 复制内容到剪贴板 代码: using System; namespace AnotherNameSpace { class AnotherClass { public void Func() { Console.WriteLine ("Hello World"); } } } 现在在你的 HelloWorld 类你可以这样访问: 复制内容到剪贴板 代码: using System; using AnotherNameSpace; // 你可以增加这条语句 namespace MyNameSpace { class HelloWorld { static void Main(string[] args) { AnotherClass obj = new AnotherClass(); obj.Func(); } } } 在 .NET 库,System 是包含其他命名空间的顶层命名空间。默认情况下存在一个全局命名空间,所以在命名空间外定义的类直接进到此全局命名空间,因而你可以不用定界符访问此类。 你同样可以定义嵌套命名空间。 Using #include 指示符被后跟命名空间名的 using 关键字代替了。正如上面的 using System。System 是最基层的命名空间,所有其他命名空间和类都包含于其。System 命名空间所有对象的基类是 Object。 变量 除了以下差异,C# 的变量几乎和 C++ 一样: 1. C# (不同于 C++)的变量,总是需要你在访问它们前先进行初始化,否则你将遇到编译时错误。故而,不可能访问未初始化的变量。 2. 你不能在 C# 访问一个“挂起”指针。 3. 超出数组边界的表达式索引值同样不可访问。 4. C# 没有全局变量或全局函数,取而代之的是通过静态函数和静态变量完成的。 数据类型 所有 C# 的类型都是从 object 类继承的。有两种数据类型: 1. 基本/内建类型 2. 用户定义类型 以下是 C# 内建类型的列表: 类型 字节 描述 byte 1 unsigned byte sbyte 1 signed byte short 2 signed short ushort 2 unsigned short int 4 signed integer uint 4 unsigned integer long 8 signed long ulong 8 unsigned long float 4 floating point number double 8 double precision number decimal 8 fixed precision number string - Unicode string char - Unicode char bool true, false boolean 注意:C# 的类型范围和 C++ 不同。例如:long 在 C++ 是 4 字节而在 C# 是 8 字节。bool 和 string 类型均和 C++ 不同。bool 仅接受真、假而非任意整数。 用户定义类型文件包含: 1. 类 (class) 2. 结构(struct) 3. 接口(interface) 以下类型继承时均分配内存: 1. 值类型 2. 参考类型 值类型 值类型是在分配的数据类型。它们包括了: ? 除字符串,所有基本和内建类型 ? 结构 ? 枚举类型 引用类型 引用类型在(heap)分配内存且当其不再使用时,将自动进行垃圾清理。和 C++ 要求用户显示创建 delete 运算符不一样,它们使用新运算符创建,且没有 delete 运算符。在 C# 它们自动由垃圾回收系统回收。 引用类型包括: ? 类 ? 接口 ? 集合类型如数组 ? 字符串 枚举 C# 的枚举和 C++ 完全一样。通过关键字 enum 定义。 例子: 复制内容到剪贴板 代码: enum Weekdays { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday } 类与结构 除了内存分配的不同外,类和结构就和 C++ 的情况一样。类的对象在分配,并使用 new 关键字创建。而结构是在栈(stack)进行分配。C# 的结构属于轻量级快速数据类型。当需要大型数据类型时,你应该创建类。 例子: 复制内容到剪贴板 代码: struct Date { int day; int month; int year; } class Date { int day; int month; int year; string weekday; string monthName; public int GetDay() { return day; } public int GetMonth() { return month; } public int GetYear() { return year; } public void SetDay(int Day) { day = Day ; } public void SetMonth(int Month) { month = Month; } public void SetYear(int Year) { year = Year; } public bool IsLeapYear() { return (year/4 == 0); } public void SetDate (int day, int month, int year) { } ... } 属性 如果你熟悉 C++ 面向对象的方法,你一定对属性有自己的认识。对 C++ 来说,前面例子 Date 类的属性就是 day、month 和 year,而你添加了 Get 和 Set 方法。C# 提供了一种更加便捷、简单而又直接的属性访问方式。 所以上面的类应该写成这样: 复制内容到剪贴板 代码: using System; class Date { public int Day{ get { return day; } set { day = value; } } int day; public int Month{ get { return month; } set { month = value; } } int month; public int Year{ get { return year; } set { year = value; } } int year; public bool IsLeapYear(int year) { return year%4== 0 ? true: false; } public void SetDate (int day, int month, int year) { this.day = day; this.month = month; this.year = year; } } 这里是你 get 和 set 属性的方法: 复制内容到剪贴板 代码: class User { public static void Main() { Date date = new Date(); date.Day = 27; date.Month = 6; date.Year = 2003; Console.WriteLine ("Date: {0}/{1}/{2}", date.Day, date.Month, date.Year); } } 修饰符 你必须知道 C++ 常用的 public、private 和 protected 修饰符。我将在这里讨论一些 C# 引入的新的修饰符。 readonly readonly 修饰符仅用于修饰类的数据成员。正如其名字说的,一旦它们已经进行了写操作、直接初始化或在构造函数对其进行了赋值,readonly 数据成员就只能对其进行读取。readonly 和 const 数据成员不同之处在于 const 要求你在声明时进行直接初始化。看下面的例程: 复制内容到剪贴板 代码: class MyClass { const int constInt = 100; //直接进行 readonly int myInt = 5; //直接进行 readonly int myInt2; public MyClass() { myInt2 = 8; //间接进行 } public Func() { myInt = 7; //非法 Console.WriteLine(myInt2.ToString()); } } sealed 带有 sealed 修饰符的类不允许你从它继承任何类。所以如果你不想一个类被继承,你可以对该类使用 sealed 关键字。 复制内容到剪贴板 代码: sealed class CanNotbeTheParent { int a = 5; } unsafe 你可以使用 unsafe 修饰符在 C# 定义一个不安全上下文。在不安全上下文,你可以插入不安全代码,如 C++ 的指针等。参见以下代码: 复制内容到剪贴板 代码: public unsafe MyFunction( int * pInt, double* pDouble) { int* pAnotherInt = new int; *pAnotherInt = 10; pInt = pAnotherInt; ... *pDouble = 8.9; } 接口 如果你有 COM 的思想,你马上就知道我在说什么了。接口是只包含函数签名而在子类实现的抽象基类。在 C# ,你可以用 interface 关键字声明这样的接口类。.NET 就是基于这样的接口的。C# 你不能对类进行多重继承——这在 C++ 是允许的。通过接口,多重继承的精髓得以实现。即你的子类可以实现多重接口。(译注:由此可以实现多重继承) 复制内容到剪贴板 代码: using System; interface myDrawing { int originx { get; set; } int originy { get; set; } void Draw(object shape); } class Shape: myDrawing { int OriX; int OriY; public int originx { get{ return OriX; } set{ OriX = value; } } public int originy { get{ return OriY; } set{ OriY = value; } } public void Draw(object shape) { ... // 做要做的事 } // 类自身的方法 public void MoveShape(int newX, int newY) { ..... } } 数组 数组在 C# C++ 要高级很多。数组分配于,所以是引用类型的。你不能访问数组边界外的元素。所以 C# 防止你引发那种 bug。同时也提供了迭代数组元素的帮助函数。foreach 是这样的迭代语句之一。C++ 和 C# 数组的语法差异在于: 方括号在类型后面而不是在变量名后面 创建元素使用 new 运算符 C# 支持一维、多维和交错数组(数组的数组) 例子: 复制内容到剪贴板 代码: int[] array = new int[10]; // int 型一维数组 for (int i = 0; i < array.Length; i++) array = i; int[,] array2 = new int[5,10]; // int 型二维数组 array2[1,2] = 5; int[,,] array3 = new int[5,10,5]; // int 型三维数组 array3[0,2,4] = 9; int[][] arrayOfarray = new int[2]; // int 型交错数组 - 数组的数组 arrayOfarray[0] = new int[4]; arrayOfarray[0] = new int[] {1,2,15}; 索引器 索引器用于书写一个可以通过使用 [] 像数组一样直接访问集合元素的方法。你所需要的只是指定待访问实例或元素的索引。索引器的语法和类属性语法相同,除了接受作为元素索引的输入参数外。 例子: 注意:CollectionBase 是用于建立集合的库类。List 是 CollectionBase 用于存放集合列表的受保护成员。 复制内容到剪贴板 代码: class Shapes: CollectionBase { public void add(Shape shp) { List.Add(shp); } //indexer public Shape this[int index] { get { return (Shape) List[index]; } set { List[index] = value ; } } } 装箱/拆箱 装箱的思想在 C# 是创新的。正如前面提到的,所有的数据类型,无论是内建的还是用户定义的,都是从 System 命名空间的基类 object 继承的。所以基础的或是原始的类型打包为一个对象称为装箱,相反的处理称为拆箱。 例子: 复制内容到剪贴板 代码: class Test { static void Main() { int myInt = 12; object obj = myInt ; // 装箱 int myInt2 = (int) obj; // 拆箱 } } 例程展示了装箱和拆箱两个过程。一个 int 值可以被转换为对象,并且能够再次转换回 int。当某种值类型的变量需要被转换为一个引用类型时,便会产生一个对象箱保存该值。拆箱则完全相反。当某个对象箱被转换回其原值类型时,该值从箱拷贝至适当的存储空间。 函数参数 C# 的参数有三种类型: 1. 按值传递/输入参数 2. 按引用传递/输入-输出参数 3. 输出参数 如果你有 COM 接口的思想,而且还是参数类型的,你会很容易理解 C# 的参数类型。 按值传递/输入参数 值参数的概念和 C++ 一样。传递的值复制到了新的地方并传递给函数。 例子: 复制内容到剪贴板 代码: SetDay(5); ... void SetDay(int day) { .... } 按引用传递/输入-输出参数 C++ 的引用参数是通过指针或引用运算符 & 传递的。C# 的引用参数更不易出错。你可以传递一个引用地址,你传递一个输入的值并通过函数得到一个输出的值。因此引用参数也被称为输入-输出参数。 你不能将未初始化的引用参数传递给函数。C# 使用关键字 ref 指定引用参数。你同时还必须在传递参数给要求引用参数的函数时使用关键字 ref。 例子: 复制内容到剪贴板 代码: int a= 5; FunctionA(ref a); // 使用 ref,否则将引发编译时错误 Console.WriteLine(a); // 打印 20 复制内容到剪贴板 代码: void FunctionA(ref int Val) { int x= Val; Val = x* 4; } 输出参数 输出参数是只从函数返回值的参数。输入值不要求。C# 使用关键字 out 表示输出参数。 例子: 复制内容到剪贴板 代码: int Val; GetNodeValue(Val); 复制内容到剪贴板 代码: bool GetNodeValue(out int Val) { Val = value; return true; } 参数和数组的数量变化 C# 的数组使用关键字 params 进行传递。一个数组类型的参数必须总是函数最右边的参数。只有一个参数可以是数组类型。你可以传送任意数量的元素作为数组类型的参数。看了下面的例子你可以更好的理解: 注意:使用数组是 C# 提供用于可选或可变数量参数的唯一途径。 例子: 复制内容到剪贴板 代码: void Func(params int[] array) { Console.WriteLine("number of elements {0}", array.Length); } 复制内容到剪贴板 代码: Func(); // 打印 0 Func(5); // 打印 1 Func(7,9); // 打印 2 Func(new int[] {3,8,10}); // 打印 3 int[] array = new int[8] {1,3,4,5,5,6,7,5}; Func(array); // 打印 8 运算符与表达式 运算符和表达式跟 C++ 完全一致。然而同时也添加了一些新的有用的运算符。有些在这里进行了讨论。 is 运算符 is 运算符是用于检查操作数类型是否相等或可以转换。is 运算符特别适合用于多态的情形。is 运算符使用两个操作数,其结果是布尔值。参考例子: 复制内容到剪贴板 代码: void function(object param) { if(param is ClassA) //做要做的事 else if(param is MyStruct) //做要做的事 } } as 运算符 as 运算符检查操作数的类型是否可转换或是相等(as 是由 is 运算符完成的),如果是,则处理结果是已转换或已装箱的对象(如果操作数可以装箱为目标类型,参考 装箱/拆箱)。如果对象不是可转换的或可装箱的,返回值为 null。看看下面的例子以更好的理解这个概念。 复制内容到剪贴板 代码: Shape shp = new Shape(); Vehicle veh = shp as Vehicle; // 返回 null,类型不可转换 Circle cir = new Circle(); Shape shp = cir; Circle cir2 = shp as Circle; //将进行转换 object[] objects = new object[2]; objects[0] = "Aisha"; object[1] = new Shape(); string str; for(int i=0; i&< objects.Length; i++) { str = objects as string; if(str == null) Console.WriteLine("can not be converted"); else Console.WriteLine("{0}",str); } 复制内容到剪贴板 代码: Output: Aisha can not be converted 语句 除了些许附加的新语句和修改外,C# 的语句和 C++ 的基本一致。 以下是新的语句: foreach 用于迭代数组等集合。 例子: 复制内容到剪贴板 代码: foreach (string s in array) Console.WriteLine(s); lock 在线程使代码块称为重点部分。 (译注:lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。) checked/unchecked 用于数字操作的溢出检查。 例子: 复制内容到剪贴板 代码: int x = Int32.MaxValue; x++; // 溢出检查 { x++; // 异常 } unchecked { x++; // 溢出 } 下面的语句已修改:(译注:原文如此,疑为作者笔误) Switch Switch 语句在 C# 修改过。 1.现在在执行一条 case 语句后,程序流不能跳至下一 case 语句。之前在 C++ 这是可以的。 例子: 复制内容到剪贴板 代码: int var = 100; switch (var) { case 100: Console.WriteLine(""); // 这里没有 break case 200: Console.WriteLine(""); break; } C++ 的输出: 复制内容到剪贴板 代码: 而在 C# 你将得到一个编译时错误: 复制内容到剪贴板 代码: error CS0163: Control cannot fall through from one case label ('case 100:') to another 2.然而你可以像在 C++ 一样这么用: 复制内容到剪贴板 代码: switch (var) { case 100: case 200: Console.WriteLine("100 or 200"); break; } 3.你还可以用常数变量作为 case 值: 例子: 复制内容到剪贴板 代码: const string WeekEnd = "Sunday"; const string WeekDay1 = "Monday"; .... string WeekDay = Console.ReadLine(); switch (WeekDay ) { case WeekEnd: Console.WriteLine("It's weekend!!"); break; case WeekDay1: Console.WriteLine("It's Monday"); break; } 委托 委托让我们可以把函数引用保存在变量。这就像在 C++ 使用 typedef 保存函数指针一样。 委托使用关键字 delegate 声明。看看这个例子,你就能理解什么是委托: 例子: 复制内容到剪贴板 代码: delegate int Operation(int val1, int val2); public int Add(int val1, int val2) { return val1 + val2; } public int Subtract (int val1, int val2) { return val1- val2; } public void Perform() { Operation Oper; Console.WriteLine("Enter + or - "); string optor = Console.ReadLine(); Console.WriteLine("Enter 2 operands"); string opnd1 = Console.ReadLine(); string opnd2 = Console.ReadLine(); int val1 = Convert.ToInt32 (opnd1); int val2 = Convert.ToInt32 (opnd2); if (optor == "+") Oper = new Operation(Add); else Oper = new Operation(Subtract); Console.WriteLine(" Result = {0}", Oper(val1, val2)); } 继承与多态 C# 只允许单一继承。多重继承可以通过接口达到。 例子: 复制内容到剪贴板 代码: class Parent{ } class Child : Parent 虚函数 虚函数在 C# 同样是用于实现多态的概念的,除了你要使用 override 关键字在子类实现虚函数外。父类使用同样的 virtual 关键字。每个重写虚函数的类都使用 override 关键字。(译注:作者所说的“同样”,“除……外”都是针对 C# 和 C++ 而言的) 复制内容到剪贴板 代码: class Shape { public virtual void Draw() { Console.WriteLine("Shape.Draw") ; } } class Rectangle : Shape { public override void Draw() { Console.WriteLine("Rectangle.Draw"); } } class Square : Rectangle { public override void Draw() { Console.WriteLine("Square.Draw"); } } class MainClass { static void Main(string[] args) { Shape[] shp = new Shape[3]; Rectangle rect = new Rectangle(); shp[0] = new Shape(); shp[1] = rect; shp[2] = new Square(); shp[0].Draw(); shp[1].Draw(); shp[2].Draw(); } } Output: Shape.Draw Rectangle.Draw Square.Draw 使用“new”隐藏父类函数 你可以隐藏基类的函数而在子类定义其新版本。关键字 new 用于声明新的版本。思考下面的例子,该例是上一例子的修改版本。注意输出,我用 关键字 new 替换了 Rectangle 类的关键字 override。 复制内容到剪贴板 代码: class Shape { public virtual void Draw() { Console.WriteLine("Shape.Draw") ; } } class Rectangle : Shape { public new void Draw() { Console.WriteLine("Rectangle.Draw"); } } class Square : Rectangle { //这里不用 override public new void Draw() { Console.WriteLine("Square.Draw"); } } class MainClass { static void Main(string[] args) { Console.WriteLine("Using Polymorphism:"); Shape[] shp = new Shape[3]; Rectangle rect = new Rectangle(); shp[0] = new Shape(); shp[1] = rect; shp[2] = new Square(); shp[0].Draw(); shp[1].Draw(); shp[2].Draw(); Console.WriteLine("Using without Polymorphism:"); rect.Draw(); Square sqr = new Square(); sqr.Draw(); } } Output: Using Polymorphism Shape.Draw Shape.Draw Shape.Draw Using without Polymorphism: Rectangle.Draw Square.Draw 多态性认为 Rectangle 类的 Draw 方法是和 Shape 类的 Draw 方法不同的另一个方法,而不是认为是其多态实现。所以为了防止父类和子类间的命名冲突,我们只有使用 new 修饰符。 注意:你不能在一个类使用一个方法的两个版本,一个用 new 修饰符,另一个用 override 或 virtual。就像在上面的例子,我不能在 Rectangle 类增加另一个名为 Draw 的方法,因为它是一个 virtual 或 override 的方法。同样在 Square 类,我也不能重写 Shape 类的虚方法 Draw。 调用基类成员 如果子类的数据成员和基类的有同样的名字,为了避免命名冲突,基类成员和函数使用 base 关键字进行访问。看看下面的例子,基类构造函数是如何调用的,而数据成员又是如何使用的。 复制内容到剪贴板 代码: public Child(int val) :base(val) { myVar = 5; base.myVar; } OR public Child(int val) { base(val); myVar = 5 ; base.myVar; } 前景展望 本文仅仅是作为 C# 语言的一个快速浏览,以便你可以熟悉该语言的一些特性。尽管我尝试用实例以一种简短而全面的方式讨论了 C# 几乎所有的主要概念,但我认为还是有很多内容需要增加和讨论的。 以后,我会增加更多的没有讨论过的命令和概念,包括事件等。我还想给初学者写一下怎么用 C# 进行 Windows 编程。 参考文献: 我们都知道的 MSDN Tom Archer 著,Inside C# Eric Gunnerson 著,A Programmer's Introduction to C# Karli Watson 著,Beginning C# O'Reilly(奥莱利出版),Programming C# 修改: 2003年6月12日:按引用传递/输入-输出参数一节增加了 ref 关键字 2003年6月20日:为可选参数增加了一条注意事项,纠正了交错数组例子赋值运算符的笔误 许可 本文及其任何关联的源代码和文件均以 The Code Project Open License (CPOL)执行。(译注:代码计划网站公开许可)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值