每一个二进制位称为一个比特位bit
8bit => 1byte
1024byte => 1KB
1024KB => 1MB
1024MB => 1GB
1024GB => 1TB
1024TB =>1PB
1024PB =>1EB
通常把一个二进制的最左边一位叫做最高位,如果需要表示负数则最高位是符号位,不表示一个数值大小,只表示数值正负性。0为正,1为负。为了计算负数引入了原反码补码的概念。
原码:直接由其他进制计算过来的得到的结果
反码:符号位不变,其他位按位取反
补码=反码+1
1.以上的规则只针对负数,正数的原码反码补码都一样。
2.所有的数据在计算机中存储与运算都是以补码的形式进行的。
3.如果计算的结果有溢出的情况,则溢出位全部舍去。
4.由负数的补码求原码:对计算出的数值再求一次补码。
!!!取值范围
无符号的八位二进制数的表示范围:[0~255]
有符号的八位二进制数的表示范围:[-128~127]
即:[2^(位数-1) ~ 2^(位数-1)-1]
0111 1111 ——> 127
1111 1111 ——> -1
1111 1110 ——> -2
1000 0001 ——> -127
1000 0000 ——> -0(-128)
0000 0000 ——> +0
因为有两个二进制数都表示0 因此规定:1000 0000 ——> -128
a、整型
默认值:0
有符号整型(八位二进制中第一位表示正负其余七位表示数值) 字节型 ——> sbyte ——> 1byte ——> [-128~127] 短整型 ——> short ——> 2byte ——> [2^(位数-1) ~ 2^(位数-1)-1] 整型 ——> int ——> 4byte ——> [2^(位数-1) ~ 2^(位数-1)-1] 长整型 ——> long ——> 8byte ——> [2^(位数-1) ~ 2^(位数-1)-1]无符号整型(八位二进制中各位数只表示数值不表示正负) 字节型 ——> byte ——> 1byte ——> [0~255] 短整型 ——> ushort ——> 2byte ——> [0~2^(位数-1)] 整型 ——> uint ——> 4byte ——> [0~2^(位数-1)] 长整型 ——> ulong ——> 8byte ——> [0~2^(位数-1)]b、浮点型
默认值:0.0(与整型的区别在于加了小数点)
在计算机中用科学计数法存储。eg:12.3 X 10 ^ 5 == 12.3e5 (e表示X10^)
单精度 ——> float ——>4byte ——> 3.14f(float需要在数字后面加f或者F)双精度 ——> double ——>8byte ——> 3.14d(double需要在数字后面加d或者D,也可以不加)高精度 ——> decimal ——>16byte ——> 3.14m(decimal需要在数字后面加m或者M)c、布尔型
默认值:false
布尔型 ——> bool ——> 1byte ——> [true,false]
d、字符型
默认值:用’\u0’或是’\u000’来表示(一般来说表示的意义是一个空格,然而不同的终端显示的以不同,也有可能是一个口)
结果应该为一个空格(’\0’),但有时结果会为a,这是因为控制台字体的影响
有可能在某种字体中显示’\0’时候就是a那种样子,那么就可以解释的通了
字符型 ——> char ——> 2byte
1、任何的字符在计算机底层存储中都是采用一个数字来存储的
2、一个字符到底要用什么样的数字来存储,由字符集(如Ascll码、Unicode码等)来决定。
3、一个字符和一个整型的数据是相通的,是可以相互转换的。
字符 ——> 整型:c + 1
整型 ——> 字符:(char)(c + 1)
eg:
//占用字符长度为1的符号,单引号赋值,只能赋值一个字符,并且不能为空。 char c = 'a'; Console.WriteLine(c);//结果为:a Console.WriteLine(c+1);//结果为:98 char d= '张'; Console.WriteLine(d);//结果为:张 Console.WriteLine((char)(d+ 1));//结果为:弡(jue) //自动类型转换 int a = 'a'; //强制类型转换 char a = (char)97;引用数据类型的默认值为NULL。(表示空 表示没有值,连地址都没有)
利用堆栈解释引用类型:
int[] array = {1, 2, 3, 4, 5 }; 等价于 int[] array = new int[5]{1, 2, 3, 4, 5 };
1、除了结构体外,其他的new都是在堆中开辟空间的
2、数组在堆中的第一个元素的地址是随机的,但是之后的元素地址都是连续的
数组array中有五个元素,在堆上开辟了5个连续的4字节空间(如下图),每个空间都有相对应的地址(int数据类型,一个数据占四字节,因此开辟4字节空间)。
其中0x1234是数组的首元素地址,当Main方法执行时被压入栈中,Main方法中声明了Array数组。此时打印数组Console.WruteLine(array)发现,打印出来的结果并不是元素,而是System.Int32[]。原因是c#语言中不显示地址,而原本应该显示的是地址才对,所以结果变成了数组元素的数据类型。
因此我们可以知道在Array数组中存储的不是元素而是数组中首元素在堆上的地址,所以在Main方法中Array称作是堆上数组的引用。因此是引用数据类型。
static void main(String[] args){ int[] Array = { 1, 2 , 3, 4, 5 }; Console.WriteLine(Array);//结果为Syetem.Int32[] }字符串:一个系列字符组成的一个有序序列,是一个引用数据类型,关键字String,一个字符串需要用双引号引起来。
//字符串可以长度为0 string name = "qwere"; Console.WriteLine(name);//结果为:qwere字符串拼接
字符串连接运算符“+” :当“+”前后有任意一个是字符串时,此时“+”被当做是连接运算符
//字符串拼接 String a = "hello"; String b = "world"; String c = a + b; Console.WriteLine(c);//结果为:helloworld //字符串连接运算符+ :当+前后有任意一个是字符串时,此时+被当做是连接运算符 Console.WriteLine("hello"+1);//结果为hello1 Console.WriteLine(1+"hello");//结果为:1hello Console.WriteLine(1+2+3+"hello");//结果为6hello Console.WriteLine(1+2+3+"hello"+3+2+1);//结果为:6hello321,原因如下: /* * 1、符号都为+时算同级运算,因此从前往后1+2+3被当做算术运算符执行得到6; * 2、第三个+后面是字符串所以被当做字符串连接符执行,得到6hello; * 3、后面的3+2+1在执行时,因为前面已经是字符串了,所以此时也由整型转换成了字符串,因此得到6hello321 */解析字符串变量
符号 $ :在字符串前方加一个$,表示解析字符串中的变量,字符串中的变量用{}括起来即可。
//符号$:在字符串前方加一个$,表示解析字符串中的变量,字符串中的变量用{}括起来即可。 int meter = 1; String str = $"小明跑了{meter}米"; Console.WriteLine(str);//结果为:小明跑了1米字符串原意输出
符号@:原意输出,不需要进行转义即不加转义字符“\”(弊端:@“”中不能再出现双引号了)
//C:\"Program Files(x86)"\ //通常的转义字符\:在C:\"Program Files(x86)"\字符串中第一个需要转义的从左到右分别是 :\ " " \ 这5个符号。 String path = "C:\\\"Program Files(x86)\"\\"; Console.WriteLine(path); //符号@ String Path = @"C:\Program Files(x86)\"; Console.WriteLine(Path);输出字符串语句
//输出语句用法 //Console.WriteLine();将括号中的内容输出到控制台,并且换行 //Console.Write();将括号中的内容输出到控制台,并且不换行 //输出语句小明跑了1米;三种写法: float time = 2; //第一种 Console.WriteLine("小明跑了"+meter+"米"); //第二种 Console.WriteLine($"小明跑了{meter}米"); //第三种 //{}中的值与逗号后面的标识符相对应,逗号后的标识符不可以随意变换位置。({}中的数值表示标识符的序号;{}的数量与标识符的数量要一致) Console.WriteLine("小明跑了{0}米,耗时{1}秒",meter,time);//{0}:meter;{1}:time数组:数组是一个容器,用来存储一系列相互兼容的数据类型的变量。
数组的声明和实例化
名字解释
数组长度:数组的容量,表示这个数组可以存储多少个数据
元素:数组中存储的数据,称作这个数组中的元素
注意事项
1、一个数组长度一旦确定,就不能修改
2、如果在实例化的时候没有指定数组中元素的初始值,那默认的是数据类型的默认值(例如:int[] array = {}; 默认的值为int数据类型的默认值,即0)
//数组 //实例化一个数组:声明一个数组,并且赋初值 //第一种方式:通过指定数组的长度来实例化一个数组(使用多) int[] array0 = new int[10];//表示实例化一个数组array,并且可以存储长度为10的数据 //第二种方式:通过指定数组长度与数组中的初始值来实例化数组 int[] array1 = new int[5] { 1, 2, 3, 4, 5 };//{}内的叫数组元素,且长度与元素个数必须保持一致 //第三种方式:通过指定数组中的元素来实例化数组 int[] array2 = new int[] { 1, 2, 3, 4, 5 }; //第四种方式:省略new int[],直接通过指定数组中的元素来实例化(使用多) int[] array3 = { 1, 2, 3, 4, 5 }; Console.WriteLine(array3);//结果为System.Int32[],并不能直接访问数组数组中元素的访问
访问数组中的元素需要通过元素中的下标,在程序中元素的下标从0开始
//数组中元素的访问 int[] array = { 2, 3, 5, 7, 11, 13, 17, 19 }; //获取array数组中的第3个元素 int number = array[3]; Console.WriteLine(number);//结果为:7遍历数组(两种方法)
遍历数组:将数组中的元素逐个获取到
a.遍历下标
//遍历数组 //遍历下标 int[] array = { 1,3,5,7,11,13,17,19 }; for(int i = 0; i < array.Length; i++)//注意!:i < array.Length不要写成<= { Console.Write(array[i]+",");//结果为:1, 3, 5, 7, 11, 13, 17, 19 } Console.WriteLine(); //修改数组中的元素 int[] array1 = { 1, 3, 5, 7, 11, 13, 17, 19 }; array1[3] = 100; array1[8] = 30;//会报错,因为下标越界,数组中元素有8个但下标是1~7. for(int i = 0;i < array.Length; i++) { Console.Write(array[i]+",");//结果为:1, 3, 5, 100, 11, 13, 17, 19 } Console.WriteLine();b.快速枚举
//快速枚举 //语法(枚举array数组) int[] array = { 1, 3, 5, 7, 11, 13, 17, 19 }; foreach (int item in array)//将array中的每一个元素都枚举到item中 { Console.Write(item + ",");//结果为:1, 3, 5, 7, 11, 13, 17, 19 }c.比较下标遍历与快速枚举 1.效率方面来说,foreach比下标遍历快一些 2.foreach有局限性,在foreach中不允许修改元素
params关键字
params:用来修饰形参 加上关键字params后,在调用方法时候,可以将数组中的元素直接写到参数列表中。
如果形参列表中既有params数组,也有可选参数,那么params数组需要放在最后面。
static void Main(string[] args) { //调用数组方法:(调用过程繁琐,需要写大量的new int[]过程,因此有了关键字params用来简便过程) //第一种 int[] a = new int[5]; Test(a); //第二种 Test(new int[5] { 1, 2, 3, 4, 5 }); //第三种 Test(new int[] { 1, 2, 3, 4, 5 }); //关键字params Test1(1, 2, 3, 4, 5);//当加上关键字params时,可以直接写数组元素,不用再写new int[] }{} Test2(1, 2, 3, 4, 5); Test3(1, 2, 3, 4, 5); } //数组 static void Test(int[] array) { foreach(int item in array){ Console.Write(item+",");//结果为:{1,2,3,4,5} } } // 关键字params static void Test1(params int[] array) { foreach (int item in array) { Console.Write(item + ",");//结果为:{1,2,3,4,5} } Console.WriteLine(); } //代参数,params方法调用 static void Test2(int a, params int[] array) { Console.WriteLine(a); foreach (int item in array) { Console.Write(item + ",");//结果为:a = 1,array = {2,3,4,5} } } //代可选参数,params方法调用 static void Test3(int a, int b = 10, params int[] array) { Console.WriteLine(a); Console.WriteLine(b); foreach (int item in array) { Console.Write(item + ",");//结果为:a = 1, b = 2, array = {3,4,5} } }堆栈解释交换数组变量
图解交换数组
1.在程序执行时,Main方法首先压入栈内,在堆上开辟了一个数组num(占用了两个连续的4字节空间),并将首元素的堆地址赋值给栈中的num变量
2.接着调用Swap方法(Swap压入栈内),并将num数组赋值给Swap中的形参列表array(此时是将num中的首元素地址赋值给了array)
3.然后Swap方法根据赋值的首元素地址找到在堆上的元素,完成数组元素交换,Swap执行完成出栈
4.最后在Main方法中foreach列举数组中的元素,此时找的还是在堆上的同一个地址,因此是已经被交换过得元素,打印出的结果为:{1,2}
//数组中元素交换 static void main(String[] args) { int[] num = { 1, 2 }; Swap(num); foreach(int item in num) { Console.WriteLine(item);//结果为:2,1 } } static void Swap(int[] array) { int temp = array[0]; array[0] = array[1]; array[1] = temp; }二维数组:数组中的元素还是数组的形式
//二维数组:也是数组,数组中的元素还是数组 int[][] array; //通过指定数组的长度来实例化数组 int[][] array0 = new int[10][]; //指定数组中的元素来实例化数组 int[][] array1 = { new int[] { 1, 2,3 ,4 }, new int[] { 2, 3, 4, 5 }, }; Console.WriteLine(array1.Length);//结果为:2 数组array1共有两个元素数组 //array1中的元素还是一个数组 array1[0] = new int[] { 6, 3, 5 }; Console.WriteLine(array1[0][0]);//结果为:6 数组array1中第[0]个元素数组中的[0]个元素为6 //c#中所谓的二维数组(但其本质还是一个一维数组) int[,] array3 = new int[3, 4]; Console.WriteLine(array3.Length);//结果为:12 表示数组中的元素变成了三行四列,形成了一个矩阵 array3[0, 0] = 1;//将第0行第0列赋值为1 array3[1, 3] = 3;//将第1行第3列赋值为3一个由字母数字下划线和@符号组成的有序序列。
作用:用来表示一个数据
1.只能由字母、数字、下划线和@符号组成(但因尽量避免使用@符号)
2.不能以数字开头
3.如果包含@,那么@必须放在首位(一个标识符中不能包含两个@)
4.不能与关键字重名(例如数据类型:bool,int等)
1.望文知义(看到标志符就知道含义)
2.遵循驼峰命名法
大驼峰——>如果一个标志符由多个单词组成,那么每一个单词的首字母都要大写
小驼峰——>如果一个标志符由多个单词组成,那么从第二个单词开始,后面的每一个单词首字母大写(无特殊强调默认使用小驼峰)
补充:其实在c#中,标识符的组成部分也可以是汉字或者中文字符,但是不推荐这样使用。
变量:如果一个标识符所表示的数据,在程序运行中是可以被修改的,那么这个数据被称作是一个变量。
常量:如果一个标识符所表示的数据,在程序运行中是不可以被修改的,那么这个数据被称作是一个变量。
变量:(三种表示方式)
1、数据类型 标志符(eg:int age)
2、数据类型 标志符 = 初始值(eg: int age = 10)
3、数据类型 标识符1,标识符2 = 初始值,标识符3(eg:int width, height = 10, length)
常量:(常量声明时必须附初始值)
const 数据类型 标志符 = 初始值(eg:const int birth = 1999)
给开发人员看的,不会被编译的部分。两种方式:
1、// 单行注释,只对后面的一行内容生效
2、/* */ 多行注释,中间的内容都会被注释掉
数据类型转换并不是把一个变量的类型直接转换成其他的类型,而是声明一个要转型的变量,然后将变量的值给这个新的类型的变量赋值。转换方式分两种:
由取值范围小的数据类型转换为取值范围大的数据类型
eg:
//自动类型转换 sbyte num0 = 10; //由sbyte型转型为int int num1 = num0;自动完成不需要额外操作,转型后的数据不会有丢失的情况。
由取值范围大的数据类型转换为取值范围小的数据类型
eg:
//强制类型转换 int num2 = 128; //由int转型为sbyte sbyte num3 = (sbyte)num2; //num3结果为-128 Console.WriteLine(num3);需要做强制类型转换(在被转换的数据之前加圆括号写明要转换的数据类型),转型后的数据可能和原数据不一样。
可能会产生溢出,将溢出的部分全部舍掉,剩下的数值为转换后的数值。
eg:由int 数据类型转换为sbyte数据类型
int num2 = 128 ——> sbyte num3 = (sbyte)num2
数据类型:(4byte)
0000 0000 | 0000 0000 | 0000 0000 | 1000 0000 ——> 128
sbyte数据类型:(1byte)
1000 0000 ——>-128(因为最高位为符号位所以将溢出位舍去后数值变为-128)
补充:在程序中,sbyte和short在参与运算时,会自动转换为int型。
eg:
sbyte n0 = 1, n1 = 2; !sbyte result = n0 + n1; /* 此时会报错,原因是n0,n1在运算时为int类型,而result为sbyte类型,无法将int类型隐式转换为sbyte类型。 因此将计算过程加上强制转换,才可运行成功。 */ sbyte result = (sbyte)(n0 + n1);//运行成功!结果为31.可以使某些具有特殊含义的字符变得没有特殊含义。(转义字符不占位数)
//打印单引号 char c = '\''; Console.WriteLine(c);//结果为:’ //打印\ char d = '\\'; Console.WriteLine(d);//结果为:\2.可以使某些本来没有特殊含义的字符变得具有特殊含义。(\n换行、\t tab的距离,\r回撤return)
算术运算符 ——> + - * / % ++ –
//算术运算符 int a = 10, b = 20; Console.WriteLine(a + b);//结果为:30 Console.WriteLine(a - b);//结果为:-10 Console.WriteLine(a * b);//结果为:200 Console.WriteLine(a / b);//整型与整型的运算得到的是整型,结果为:0 Console.WriteLine(a % b);//结果为:10 //自增运算(a在现有值的基础上加1),自增运算符可以放在变量前也可以放在变量后 //++a:自增运算符在变量前:表示先对这个变量值进行自增操作,然后再进行使用 //a++:自增运算符在变量后:表示先取这个变量的值进行使用,然后再对这个变量进行自增操作 Console.WriteLine(++a);//结果为:11 10+1=11 Console.WriteLine(a++);//结果为:11 先取值11,使用后计算11+1=12 Console.WriteLine(a--);//结果为:12 先取值12,使用后计算12-1=11 Console.WriteLine(--a);//结果为:10 11-1=10赋值运算符 ——> = *+= -= = /= %=
//赋值运算符 int a; //指的是给一个变量进行赋值的操作 //表示将等号右边的值给左边的变量进行赋值 //赋值运算有结果,结果是赋值完成后变量的值 a = 10; Console.WriteLine(a = 30); //组合赋值运算 a += 10; //等价于 a = a + 10; a -= 10; //等价于 a = a - 10; a *= 10; //等价于 a = a * 10; a /= 10; //等价于 a = a / 10; a %= 10; //等价于 a = a % 10;关系运算符 ——> > < >= <= == !=
//关系运算符 //用来比较两个数据的大小关系 //关系比较的结果肯定是bool类型的 int a = 10, b = 20; Console.WriteLine(a > b); //false Console.WriteLine(a < b); //true Console.WriteLine(a == b); //false Console.WriteLine(a != b); //true Console.WriteLine(a >= b); //false Console.WriteLine(a <= b); //true逻辑运算符 ——> & | ! ^ && ||
//逻辑运算符 //指的是将两个布尔型的数据进行逻辑运算 //&:逻辑与(只有当两个数据都是true时结果才是true,有任何一个false,结果都是false) Console.WriteLine(true & true | false);//结果为false //|:逻辑或(只有当两个数据都是false时,结果才是false,只要有true,结果都是true) Console.WriteLine(true | false);//结果为true //!:逻辑非(取反) Console.WriteLine(!true);//结果为false //^:逻辑异或(如果两个参与运算的数据不一样,结果为true;如果两个参与运算的数据一样,结果为false) Console.WriteLine(false ^ true);//结果为true //&&:短路与(如果某个数据已经可以决定整体的运算结果了,那么后面的表达式不参与运算) int a = 10, b = 20; bool result = a++ > --b && a++ < b++; Console.WriteLine(result);//结果为false Console.WriteLine(a);//结果为11 Console.WriteLine(b);//结果为19位运算符 ——> & | ^ ~ << >>
//位运算符 //参与位运算的都是整型的数据 //将两个参与运算的数字展开为补码的形式,将每一位进行运算 Console.WriteLine(8 | 7);//或 //0000 1000 //0000 0111 //0000 1111 =>15 Console.WriteLine(8 & 7);//与 //0000 1000 //0000 0111 //0000 0000 =>0 Console.WriteLine(8 ^ 7);//异或 //0000 1000 //0000 0111 //0000 1111 =>15 Console.WriteLine(~8);//取反 //0000 1000 //1111 0111(与反码不同,要连符号位一起取反) //1000 1001 =>-9(结果是负数所以求原码时需要再求一次补码) //位左移 Console.WriteLine(8 << 2); //0000 1000 //0010 0000 =>32(每左移一次乘2的倍数次方eg:8*(2^2)) //位右移 Console.WriteLine(8 >> 2); //0000 1000 //0000 0010 =>2(每左移一次除2的倍数次方eg: 8/(2^2))三目运算符 ——> ?: 语法格式: (条件) ? (满足条件的值) :(不满足条件的值)
在编写项目的时候,会经常用到 if-else / switch-case 判断语句,但有些简单的判断或赋值,可以通过三目运算符来完成!
//if-else判断语句: int sex=0; string sexText=""; if(sex==0){ sexText="女"; } else{ sexText="男"; //三目运算: int sex=0; string sexText=sex==0 ? "女" : "男"; //switch-case判断语句: int season = 3; switch (season) { case 0: Console.WriteLine("春困"); break; case 1: Console.WriteLine("夏打盹"); break; case 2: Console.WriteLine("秋乏"); break; case 3: Console.WriteLine("冬眠"); break; //三目运算: int season=0; string seasonalChange=season==0 ? "春困" : season==1 ? "夏打盹" : season==2 ? "秋乏" : season==3 ? "冬眠" ;