用《叩响C#之门》复习C#基础知识 第二章 变量
变量这章在很多入门教材里都是泛泛一谈,非常粗浅、简单,但在看到《叩响C#之门》的变量时,真的被作者的认真所打动,与其说是复习变量知识,倒不如说,我在本书的变量章节学到了更多有益的知识!并通过书中内容启发,又领悟和自学了不少扩展知识。
一、整型变量
1、C#默认整型变量为Int32,因为32位整数,按照一般8位一个字节来算,当声明整型变量时,系统当然会在内存中分配一块4字节空间,用来存储变量的值。整型变量的默认值为0。如:int a; 变量a所对应的内存空间被写入默认值0;a=7;a所对应的内存空间被修改为7;
2、int型变量的取值范围
int型变量4字节,32位,最高位定义为符号位,0表示正数,1表示负数。整数是以补码的形式表示的,正数的补码就是它本身,求负数的补码方法如下:
1)将该数的绝对值表示为二进制形式
2)按位取反
3)再加1
最小负数为符号位为1,其余各位为0,正好是-231的补码,所以最小的是-231
不难分析出int型变量的取值范围是-231~231-1
3、short型
对于只占用2个字节的整型,用short型可以避免内存过多占用造成的浪费。short型变量占用2个字节,最高位表示正负号,后15位表示数值大小,取值范围-215~215-1
4、long型
int型变量取值范围可达正负21亿,但如依然不够用,可用long型,改型变量占用了8个字节,范围可达-263~263-1,long型会占用更多的内存,因此不要随便使用。
5、无符号型
在年龄、人口等涉及的全部是正数的情况下,可以使用uint型变量(u是unsigned的缩写,无符号的),它的32位全部用来表示正整数。范围为0~232-1,相应也有ushort和ulong型变量。
6、整型小结
C#中工内置了8种整数类型
sbyte 有符号字节型
byte 无符号字节型
short 有符号短整型
ushort 无符号短整型
int 有符号整型
uint 无符号整型
long 有符号长整形
ulong 无符号长整形
7、溢出
一个short型变量只能容纳-215~215-1内的整数,无法表示超过这范围的数,如果在运算时,short变量所对应的内存数值超过了这个范围,内存无法存取该数值(三个以上字节的整数非要存在2个字节内),编译器会报错,此情况称为溢出。所以我们在选择数据类型时,首先要估算数据的大小,根据数据的大小选择恰当的数据类型,既不能太大浪费了空间,也不要太小而引发溢出。
二、实数型变量
实数也称浮点数,C#中有3种实数型变量类型:float、double、decimal,
float型变量(单精度实数)在内存中占4个字节,32位,实数型数据是按照指数形式存储的,在最高位依然是符号位,后面系统把一个实数型数据分成小数部分和指数部分,分别存放。单精度实数有效数字为7位。(范围不记了,需要时查即可)
双精度double变量在内存中占8个字节,有效数字为15位或16位。
在精度要求不是很高的运算中,可以采用float型以节约内存。实数常量默认情况下是双精度的,为了把实数常量赋给单精度变量,需要添加后缀f或F。否则系统会提示“不能隐式地将Double类型转换为“float”类型;请使用“F”后缀创建此类型”。
由于单精度的有效数字是7位,实数常量如果超过7位,赋值给当精度时,多余的有效数字会被四舍五入。
decimal型在内存中用16个字节表示,其精度(有效位)高达28位,然而取值范围比double型大为缩水。要将实数常量赋值给decimal变量时,需要添加后缀m或M。否则系统会提示“不能隐式地将 Double类型转换为“decimal”类型;请使用“M”后缀创建此类型”。当然如果在程序中大量使用decimal型实数,将会占用更多的内存。
三、字符
1、字符所占字节数
当在程序中声明一个字符型变量时,程序会在内存中分配2字节的空间(之所以是两个字节,是因为世界通用的Unicode编码是每个字符都为4个16进制数字组成的编码,4个16进制数字组合就是16位即2个字节)。字符变量值变化时,变量对应的内存中存储的编码也随之改变。这里需注意的是:字符串变量是指向一个内存,该内存存储着当前的字符串变量值,一但字符串变量发生变化,原内存值没有变,而是字符串变量不再指向该内存,而是又新划出一处内存,来存放新的字符串。可见字符变量是值类型,而字符串变量是引用类型。
(当从值类型变量和引用类型变量的角度去分析,我曾在《Visual C# .NET 程序设计》一书中看过,当时记忆和理解都不深,在看过《C#学习笔记》的相关内容和《扣响C#之门》的变量章节后,多少有了些新体会,还好,在《扣》一书中的第八章还有讲解,到时再细看对比下。)
读取一个字符用函数Console.Read(),该函数返回值是字符的ASCII编码,是一个int型值。(最多就2个字节的内容用4个字节来读取好像有点浪费)
2、Convert.To…()函数
Convert.ToByte (String, Int32) 将指定基数的数字的 String 表示形式转换为等效的 8 位无符号整数。 其他单参数的都表示将指定的值转换为 8 位无符号整数。如:Convert.ToByte(Char) Convett.ToByte(Decimal)
其他整型都可按照此方法转换,To后面的字符具体为Byte、SByte、Int16(短整型)、UInt16、Int32(整型)、UInt32、Int64(长整型)、UInt64
实数型转换大都用单参数,To后面的字符具体为Single(单精度float型)、Double、Decimal
字符转换,ToChar(单参数)
字符串转换。ToString(单参数)仅表示转换参数内容为字符串;两个参数时,一般为将整数的值以指定的基数转换为它的等效 String 表示形式。
3、ASCII编码和Unicode编码
1)ASCII简介
ASCII编码 美国信息交换标准码 American Standard Code for Information Interchange。使用7位二进制表示128个字符,包括英文字母、希腊字母、数字和常用符号及各种控制符号。,后来扩展的ASCII编码使用8位二进制数表示256个字符。(具体编码用到时可查)
存储一个字符,只需存储它的编码即可,实际上编码在内存中是以二进制形式存储,字符串在内存中就是一串连续的二进制编码,在编程中常用的是10进制或16进制形式。
2)Unicode编码
对于中国的GB-2312编码标准和日文、韩文、阿拉伯文、台湾地区繁体中文(BIG-5)等文字,即多字节字符集,人们制订了同意的Unicode编码,为全世界每一个字符提供统一的编码,同一个字符不论在什么平台、什么软件中,也不论在什么语言环境中,都有相同的编码,从而解决了兼容性问题。
Unicode编码完全兼容ASCII编码。如果想通过Unicode编码输出字符,需用格式为“\u”后面加相应的Unicode编码十六进制形式。如:
Console.WriteLine("\u5b66\u800c\u65f6\u4e60\u4e4b\u4e0d\u4ea6\u8bf4\u4e4e");//输出:学而时习之不亦说乎,十六进制中的字母大小写效果都一样。
3)强制显式转换
字符转换为整数 可以直接用(int)’a’的形式,如int a=(int)’a’;
整数转换为字符 可以直接用(char)97的形式,如char a=(char)97;
通过()强制显式转换的类型可以是所有整型和实数型如:Byte、SByte、Int16(短整型)、UInt16、Int32(整型)、UInt32、Int64(长整型)、UInt64、Single(单精度float型)、Double、Decimal;另外再加一个字符型,注意字符串不能通过该方式直接将其他类型转换为字符串类型,只能通过Covert.ToString()来实现。
需要注意的是,在转换所用的()里的类型表示与定义这些类型变量时所用的类型表示是不一样的!
另外,通过Convert.ToUInt16(‘一个字符’)感觉就可以实现将完整的Unicode编码读出来。其他的Convert.To…()也类似。
如:Console.WriteLine(Convert.ToString(Convert.ToUInt16('学'),16));//输出‘学’这个字符的Unicode编码(十六进制)
Console.WriteLine(Convert.ToChar(Convert.ToInt32("5b66",16)));//再按Unicode编码(十六进制)转换为汉字并输出
4、转义字符
C#中定义了大量的转义字符,这些字符都以反斜杠“\”开头
\'单引号
\''双引号
\\反斜杠
\0空 常放在字符串末尾
\a
与响铃(警报)\u0007 匹配。(试了不响,也不知我的硬件设置哪里开关关了或者有什么问题)
\b 退格实际就是光标向前移动一个位置,不会删除字符的
如果在 [] 字符类中,则与退格符 \u0008 匹配;如果不是这种情况,请参见本表后面的“注意”部分。
\t 水平制表符,跳到下一个tab位置
与 Tab 符 \u0009 匹配。
\r 回车是不换行的,只是将光标移到本行开头
与回车符 \u000D 匹配。
\v 把当前行移动到下一个垂直tab位置(没试成功,不知道是系统问题,还是没理解在哪里用这个转义符)
与垂直 Tab 符 \u000B 匹配。
Console.WriteLine("12345\r5678\v99999");
感觉应该输出的是:
56785
\f 将当前位置移动到下一页开头(还不知道在什么时候用,目前试了也是很奇怪的输出)
与换页符 \u000C 匹配。
\n 将当前位置移动到下一行开头
与换行符 \u000A 匹配。
\e
与 Esc 符 \u001B 匹配。
\x
使用十六进制表示形式(两位)与 ASCII 字符匹配。
也就是说如果要输出abcde五个字符,可以用如下语句
Console.WriteLine("\x61\x62\x63\x64\x65");//61~65分别是a~f五个字符对应的ASCII编码(十六进制)
\u
使用十六进制表示形式(四位)与 Unicode 字符匹配。
至于书中的练一练答案应该是:
Console.WriteLine("\\n \\t \\a \n\\\' \\\' \\0");
5、@控制符
前缀@可以使引号里的内容原样输出,而不理会转义字符,用@控制符可以提高代码的可读性。
练一练答案:
Console.WriteLine(@"
*
* *
* *
* *
*
");
6、运算符
“+”运算符不仅可以连接字符串,还可以将字符串和其他类型的变量连接,如:
Console.WriteLine("hello"+365+"UP");
int a = 365; Console.WriteLine("hello" + a + "UP");
都显示“hello365UP”,也就是说程序会先将其他类型的变量(或常量)转换为字符串,在与其他字符串进行连接。
7、变量的格式化输出
int a = 365; Console.WriteLine("hello{0}UP",a);
{0}是占位符,是从索引0开始计数的。用占位符方式的好处是可以在占位符{}中添加各种格式控制信息。
1)控制数值位数(也就是一个数值的宽度)
Console.WriteLine("{0,6}\n +{1,3}\n--------\n{2,6}",i,j,i+j);
{i,w} i是索引,w是宽度(位数)值,正数表示右对齐,负数表示左对齐。如果宽度不够实际的数字宽度,那么就按照实际数字输出,宽度不再作用了。
2)控制数值格式和小数位数
添加格式字符串和精度值可以控制数值的显示格式和小数位数,这里两个参数与前面参数可同时显示,前面参数在左边,这两个参数在右边,中间用“:”分开。
如:{1,7:c2}
索引1 宽度7:本地货币格式 小数位数为2
c是金融格式字符(大小写都一样),系统会根据计算机上的地区设置显示相应的金融符号。
C 或 c 本地货币格式 (Currency 货币 流通)
货币
数字转换为表示货币金额的字符串。转换由用于格式化数字的 NumberFormatInfo 对象的货币格式信息控制。精度说明符指示所需的小数位数。如果省略精度说明符,则使用 NumberFormatInfo 给定的默认货币精度。
D 或 d (Decimal) 把二进制、八进制等转化为十进制(不知道怎么试验,C#中有直接表示二进制或八进制的方法吗?)
十进制数
只有整型才支持此格式。数字十进制数字 (0-9)转换为 的字符串,如果数字为负,则前面加负号。精度说明符指示结果字符串中所需的最少数字个数。如果需要的话,则用零填充该数字的左侧,以产生精度说明符给定的数字个数。
E 或 e ()
科学计数法(指数)
数字转换为“-d.ddd…E+ddd”或“-d.ddd…e+ddd”形式的字符串,其中每个“d”表示一个数字 (0-9)。如果该数字为负,则该字符串以减号开头。小数点前总有一个数字。精度说明符指示小数点后所需的位数。如果省略精度说明符,则使用默认值,即小数点后六位数字。格式说明符的大小写指示在指数前加前缀“E”还是“e”。指数总是由正号或负号以及最少三位数字组成。如果需要,用零填充指数以满足最少三位数字的要求。
F 或 f
固定点
数字转换为“-ddd.ddd…”形式的字符串,其中每个“d”表示一个数字 (0-9)。如果该数字为负,则该字符串以减号开头。精度说明符指示所需的小数位数。如果忽略精度说明符,则使用 NumberFormatInfo 给定的默认数值精度。
G 或 g
常规
根据数字类型以及是否存在精度说明符,数字会转换为固定点或科学记数法的最紧凑形式。如果精度说明符被省略或为零,则数字的类型决定默认精度,如下表所示。
Byte 或 SByte:3
Int16 或 UInt16:5
Int32 或 UInt32:10
Int64 或 UInt64:19
Single:7
Double:15
Decimal:29
如果用科学记数法表示数字时指数大于 -5 而且小于精度说明符,则使用固定点表示法;否则使用科学记数法。如果要求有小数点,并且忽略尾部零,则结果包含小数点。如果精度说明符存在,并且结果的有效数字位数超过指定精度,则通过舍入删除多余的尾部数字。使用科学记数法时,如果格式说明符是“G”,结果的指数带前缀“E”;如果格式说明符是“g”,结果的指数带前缀“e”。
上述规则有一个例外:如果数字是 Decimal 而且省略精度说明符时。在这种情况下总使用固定点表示法并保留尾部零。
N 或 n
数字
数字转换为“-d,ddd,ddd.ddd…”形式的字符串,其中每个“d”表示一个数字 (0-9)。如果该数字为负,则该字符串以减号开头。小数点左边每三个数字之间插入一个千位分隔符。精度说明符指示所需的小数位数。如果忽略精度说明符,则使用 NumberFormatInfo 给定的默认数值精度。
P 或 p
百分比
数字转换为由 NumberFormatInfo.PercentNegativePattern 属性或 NumberFormatInfo.PercentPositivePattern 属性定义的、表示百分比的字符串。如果数字为负,则产生的字符串由 PercentNegativePattern 定义并以负号开头。已转换的数字乘以 100 以表示为百分比。精度说明符指示所需的小数位数。如果省略精度说明符,则使用 NumberFormatInfo 给定的默认数值精度。
R 或 r (不知道有啥用?)
往返过程
往返过程说明符保证转换为字符串的数值再次被分析为相同的数值。使用此说明符格式化数值时,首先使用常规格式对其进行测试:Double 使用 15 位精度,Single 使用 7 位精度。如果此值被成功地分析回相同的数值,则使用常规格式说明符对其进行格式化。但是,如果此值未被成功地分析为相同数值,则它这样格式化:Double 使用 17 位精度,Single 使用 9 位精度。虽然精度说明符可以追加到往返过程格式说明符,但它将被忽略。使用此说明符时,往返过程优先于精度。此格式仅受浮点型支持。
X 或 x
十六进制数
数字转换为十六进制数字的字符串。格式说明符的大小写指示对大于 9 的十六进制数字使用大写字符还是小写字符。例如,使用“X”产生“ABCDEF”,使用“x”产生“abcdef”。精度说明符指示结果字符串中所需的最少数字个数。如果需要的话,则用零填充该数字的左侧,以产生精度说明符给定的数字个数。只有整型才支持此格式。
譬如:上述的 Console.WriteLine(Convert.ToString(Convert.ToUInt16('学'),16));//输出‘学’这个字符的Unicode编码(十六进制)
可以改为:
Console.WriteLine("学对应的Unicode编码为{0,4:x4}",Convert.ToUInt16('学'));
3)占位符技巧
可以用占位符来代替格式字符串,如:
Console.WriteLine("{0:#.0}",Math.PI);//输出3.1
Console.WriteLine("{0:#.00}",Math.PI);//输出3.14
Console.WriteLine("{0:#.0000}",Math.PI);//输出3.1416
也就是说,{索引:#(0).#(0)} #和0都可以代表索引所关联变量或常量的数值中的每个数字,显示顺序依照实际数值,当有其他字符放入进来,就会在相应位置插入该字符,需要注意的是:整数部分如果其他字符开头,则该字符后的0或#代表原数值正数部分到下个格式字符处(不论是其他字符还是0或#)的全部整数数字;小数部分,是表明几位就会有几位小数,0或#代表原数字,其他字符如果出现,只是在该位置插入进来。比较难理解,例子如下:
Console.WriteLine("{0:哈0哈#.#0#哈0#}", 123456789.123456789);
8、变量命名规则
.NET推荐用camelCase(驼峰式)方式命名变量,变量名称的第一个单词小写,后面每个单词首字母大写
9、常量
1)其实日常在直接书写数字和字符等时,这些应该都属于常量。
2)const常量
const常量只能在声明的时候赋值,在程序运行过程中不能改变它的值,并由编译程序保证它的值不变,因此const常量不但使程序易于维护,而且大大增强了程序的健壮性。