C语言基本语法(上)
3.1 什么是变量
变量是指其值可以变化的量。计算机中,指令代码、数据都存储与内存中。变量也需要存储在内存中。在计算机中,每个变量都被分配了一块内存空间,在这些空间里存储的就是变量的值。变量之所以可以变化,就是这个存储空间可以存储不同的数值。存储空间里的值变化,则变量对应的值也变化。同一时间,内存空间里只能保存一份值,新值冲掉原来的值,每个内存单元都有编号,这些是内存的地址。
3.1.1 什么是常量与变量
常量就是计算机内存里面不变的数据。
变量就是计算机内存里需要并且经常改变的数据
举例说明:
变量就是租房子。因为会经常变动
常量就是买房子。基本不会变
3.1.2 变量的概念与命名规则
- 变量名定义:
定义:
程序中用于标识常量、变量、函数的字符序列
组成:
只能由字母、数字、下划线组成,第一个字符必须是字母或下划线
大小写有区别不能使用C语言的关键字
举例说明:给变量起名,就好比开房
- 变量的语法
变量类型变量名;
变量名 = 值;
简写:变量类型变量名 = 值;
- 变量名要有意义:
首先要保证的就是这个变量的名字要有意义。
什么叫意义?
举例:学生,年龄,身高,姓名
现阶段给变量起名字的时候都是以字母开头后面可以跟任意字母、数字、下划线。
- 变量的使用规则
先声明,后赋值,再使用。
注意:
起的变量名不要与C语言系统中的关键字重复。
在C语言中,大小写是敏感的。
最好给变量赋予初值。
同一个变量名在同一函数夏不允许重复定义。
变量的命名规范
- Camel 驼峰命名法:要求变量名首单词的首字母要小写,其余每个单词的首字母要大写。多用于给变量命名。
- Pascal 帕斯卡命名法:要求每个单词首字母要大写,其余字母小写。多用于类或者方法命名。
3.1.3 C语言中变量一定要初始化
变量如果不初始化,可以编译成功,但是执行的时候,很可能报错。
操作系统是如何管理内存的?
每当一个应用程序打开时,操作系统会为其分配内存,内存有内存地址与内存单元,当应用程序初始化时,就会往内存单元里面写入数据,当操作系统回收的时候,并不清楚内存单元,所以存在大量的垃圾数据。
如果变量不初始化,就会默认读取垃圾数据,有些数据会导致系统崩溃。
VC++2010 的编译器可以感知变量没有初始化,调试的时候就会出错。所以变量使用之前,必须初始化。3.1.4 定义常量的两种方式
定义常量PI的两种方式:
#define N 10
Const float N10;
区别:
第一种方式:是将N定义成一种符号,此时N只是10的别名,在编译期间用10去取代N的值,Define相当于替换。
第二种方式:是将N定义称为变量,但是告诉编译器它的值是固定不变的,如果在程序中试图去修改它的值,在编译时会报错。
#define 定义常量有什么好处
- 通过有意义的单词符号,可以指明该常量的意思,使得程序员在阅读代码时,减少迷惑。
- 需要修改常量的时候,可以只需要修改一次,实现批量修改,效率高而且准确。
如中需要将PI修改成3.14的话,只需要更改代码行:
#define N 10 为 #define N 11
3.1.5 转义字符
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃 | 007 |
\b | 退格(BS),将当前位置移到前一列 | 008 |
\f | 换页(BS),将当前位置移到下页开头 | 012 |
\n | 换行(FF),将当前位置移到下行开头 | 010 |
\r | 回车(CR),将当前位置移到本行开头 | 013 |
\t | 水平制表符(HT),跳到下一个TAB位置 | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符”\‘ | 092 |
\` | 代表一个单引号字符 | 039 |
\“ | 代表一个双引号字符 | 034 |
\? | 代表一个问号 | 063 |
\0 | 空字符(NULL) | 000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
3.2.1 二进制、八进制、十六进制转化成十进制
方法:按权相加
例1:(111011)2 = 12^5+12^4+12^302^2+1^2^1+12^0=59(10)
例2:(136)8 = 18^2+38^1+8^0=94(10)
例3:(1F2A)16 = 116^3+1516^2+216^1+1016^0=7978(10)
演示:输出012 和0x12分别打印值,使用%d解析。
结论1:0 开头代表八进制
结论2:0x开头代表十六进制。一般用于地址
注意:
任何进制,不允许出现大于等于进制的数字
3.2.2 二进制和八进制互相转换
二进制转换成八进制:从右向左,每三位一组(不足三位左边补零),转换成八进制。
八进制转换成二进制:用三位二进制数代替每一位八进制数
000 | 0 |
---|---|
001 | 1 |
010 | 2 |
011 | 3 |
100 | 4 |
101 | 5 |
110 | 6 |
111 | 7 |
例:(1101001)2 = (001,101,001)2 = (151)8
例:(666)8 = (110,110,110)2 = (110110110)2
3.2.3 二进制十六进制相互转换
二进制转换成十六进制:从右向左,每四位一组(不足四位左补零),转换成十六进制
十六进制转换成二进制:用四位二进制数代替每一位十六进制数
0000 | 0 |
---|---|
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
1010 | A |
1011 | B |
1100 | C |
1101 | D |
1110 | E |
1111 | F |
例:(11010101111101)2 = (0011,0101,0111,1101)2 = (357D)16
例:(4B9E)16 = (0100,1011,1001,1110)2 = (100101110011110)2
3.2.4 十进制二进制转换
十进制转换二进制
- 十进制整数转换为二进制:方法是除以2取余,逆序排列,以(89)10为例,如下:
89 / 2 余1
44 / 2 余0
22 / 2 余0
11 / 2 余1
5 / 2 余1
2 / 2 余0
1 余1
(89) 10 = (1011001)2
(5) 10 = (101)2
(2) 10 = (10)2
3.2.5 二进制十进制转换
十进制是逢十进一,由数字符号 0,1,2,3,4,5,6,7,8,9组成,可以这样分拆十进制数:
(1234)10 = 110^3+210^2+310^1+410^0 = 1000 + 200 + 30 + 4 = (1234)10
采用同样的方式转换二进制到十进制:
(1101)2 = 12^3+12^2+02^1+12^0 = 8 + 4 + 0 + 1 = (13)10
(10.01)2 = 12^1+02^0+02^-1+12^-2 = 2 + 0 + 0 + 0.25 = (2.25)10
3.2.6 十进制小数转换二进制
方法是乘以2取整,顺序排列,以(0.625)10为例:如下:
0.6252 = 1.25 取整1
0.252=0.5 取整0
0.5*2=1 取整1
(0.625)10 = (0.101)2
(0.25)10 = (0.01)2
(0.5)10 = (0.1)2
3.2.7 计算机存储数据
程序员编写的程序以及所使用的数据在计算机的内存中是以二进制位序列的方式存放的。
典型的计算机内存段二进制位序如下:
···0001000 10101010
1000101010111011001010010100100010010010010···
上面的二进制位序里,每一位上的数字,要么是0,要么是1。在计算机中,位(bit)是含有0或者1值的一个单元。在物理上,它的值是一个负或者是一个正电荷。也就是计算机中可以通过电压的高低来表示一位所含有的值。如果是0,则用低电压表示,如果是1,则用高电压表示。
在上面的二进制位序这个层次上,位的集合没有结构,很难来解释这些系列的意义。为了能够从整体上考虑这些位,于是给这些位序强加上结构的概念,这样的结构被称作为字节(byte)和字(word)。通常,一个字节由8位构成,而一个字由32位构成。或者说是4个字节构成。
3.2.8 内存中的位,字节,字
计算机中的内存是以位为最小存储单位的。通过对内存进行组织,可以引用特定的位集合。把计算机的内存起始位编号为1,每隔8位编号增1,也就是以字节为单位,每隔一个字节编号向上加一,可以对计算机所有内存进行编号。
地址编号 | 位7 | 位6 | 位5 | 位4 | 位3 | 位2 | 位1 | 位0 | 字节内容 |
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 10010000 |
2 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 01100111 |
3 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 11101100 |
4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00000000 |
5 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 01110010 |
··· |
3.2.8.1 计算机32位与64位的真正差别
在于计算机的寻址能力
什么是寻址能力?
寻址能力一般指的是CPU对于内存寻址的能力,通俗来说,就是能用到的多少内存的应该问题,数据在存储器(RAM)中存放是有规律的,CPU在运算的时候需要把数据提取出来就需要知道数据存放在哪里,这个时候就需要挨家挨户的找,这就叫做寻址,但是如果地址太多了超出了CPU的能力范围,CPU就找不到了,CPU最大能查询多大范围的能力叫做寻址能力。
CPU的寻址能力是以字节为单位的,如32位寻址的CPU可以寻址2^32次方大小的地址也就是4G,这也是为什么32位的CPU最大能搭配4G内存的原因,再多内存就找不到了。
4G内存怎么得来的:2^32 = 4G
2^32 = 2^22^102^102^10 = 41024
32位内存代表的是内存最大地址为32位。
一个16进制代表4个2进制。
64位代表内存地址最大为64位。
32位CPU支持最大内存为4G
3.2.9 printf类型与数据类型
声明变量的时候需要指明数据类型,声明函数的时候,也需要指明函数的返回值数据类型。数据类型是对程序所处理的数据的”抽象”,将计算机中可能出现的数据进行一个分类,哪些数据可以归结为一类,哪些数据又可以归结为另一类。比如整数1,2,3,-1,-2,0,1000归结为整数类型;带小数点的数据,比如:12.1,2343.34,-23434.33归结为实数类型。
C语言规定,在程序中使用的每一个数据,必须指定其数据类型。在C语言中,提供了非常丰富的数据类型,如列出了C语言提供的所有类型:
数据类型:
简单类型:
- 基础类型:
- 整形(int)
- 浮点(float\double)
- 字符型(char)
- 空类型(void)
- 用户定义类型:
- 枚举类型(enum)
结构类型:
- 数组([])
- 结构(struct)
- 联合(union)
- 类(class)
指针类型:(*)
注意:
必须按照对应的数据类型去解析数据,不然会出错。
Printf不会执行自动类型转换。
输出字符表
字符 | 输出的数据类型 | 含义 |
---|---|---|
%d | 整形 | 整形输出 |
%ld | 长整型 | 长整型输出 |
%o | 整数 | 以八进制形式输出整数 |
%x | 整数或字符串地址 | 以十六进制数形式输出整数,或输出字符串地址 |
%u | 输出unsigned型数据(无符号整形) | 以十进制输出unsigned型数据。注意:%d和%u有无符号的数值范围不一致,也就是极限值,不然会出错 |
%c | 字符 | 输出一个字符 |
%s | 字符串 | 输出一个字符串 |
%f | 实数 | 以小数形式输出,默认保留6位 |
%.100 | 实数 | 保留小数点后100位 |
%e | 实数 | 以指数形式输出实数 |
%g | 不输出0 | 根据大小自动选f格式或e格式,且不输出无意义的0 |
3.3.0 sizeof 运算符
sizeof是一个单目运算符,用来计算操作数在内存中占据的字节数,其操作数既可以是括在圆括号中的类型标识符,其返回值是size_t类型,即无符号整数,如:
Sizeof(short) | 返回2 |
---|---|
Sizeof(long) | 返回4 |
Sizeof(int) | 取决系统也可以是下面的表达式 |
Short x; | |
Sizeof(x) | 返回2 |
3.3.1 符号的作用
整数的正负–有符号和无符号。
前面论述的只关注了正整数,负数在计算机中该如何表示?正数和负数是两种情况,计算机又是一个很笨的东西,要区分两种情况,就必须有一个标记来表示是哪种情况。两种情况,用二进制的一位刚好可以表示,比如用0表示正数,用1表示负数,刚好可以区分清楚。那么一个4字节也就是32位存储单元的整数,应该用哪一位表示数据的正负符号呢?最简单的方法就是用最高位(就是最左边那一位)了。C语言中确实就是用最高位来表示应该整数的正负号。0表示为正,1表示为负。
3.4.0 基本数据类型
注意:数据的运算必须建立在数据的极限范围内。
类型 | 类型关键字 | 长度(位) | 取值范围 |
---|---|---|---|
有符号字符型 | [signed]char | 8 | -127 — +127 |
无符号字符型 | unsigned char | 8 | 0 — 255 |
有符号短整型 | [signed] short [int] | 16 | -32767 — +32767 |
无符号短整型 | unsigned short [int] | 16 | 0 —65535 |
有符号长整型 | [signed] long [int] | 32 | -2147483647 — +2147483647 |
无符号长整形 | unsigned long [int] | 32 | 0 — 4294967295 |
单精度实型 | float | 32 | 约(3.410^-38 — 3.410^-38) |
双精度实型 | double | 64 | 约(1.710^-308 — 1.710^-308) |
3.4.1 数值范围
Int 型最大:2147483647
Int 型最小:-2147483647
unsingned int 型最大:0xffffffff 也就是 4294967295
3.4.2 整型常量
三种表示形式:
十进制整数:由数字 0 - 9 和正负号表示。如:123,-456,0
八进制整数:由数字 0 开头,后跟数字 0 - 7。如:0123,011
十六进制整数:由 0x 或 0X 开头,后跟 0 - 9,a - f,A - F表示。如:0x123,0Xff
长整形常量:789L,017L,0x12aL(用 l 或 L 引出)
3.4.3 整型变量
整型变量用于存放整型数据。根据数值的表示范围整形可以为整形(int)、短整型(short)、长整型(long)三种。
这三种整型都默认为有符号型(signed),有符号即可以是正数、负数和0。也可以根据需要,将整型指定为无符号型(unsigned),此时整型变量只能存放非负数。
上面提到的 Short、long、signed、unsigned 都是一些类型修饰符,用于补充说明变量的特性。3.4.4 浮点型数据
两种表示形式:
十进制小数形式:必须带小数点。如:0.123,.123,123.0,0.0,123.
指数形式:e或E之前必须有数字;指数必须为整数。如:12.3e3,123E2,1.23e4,e-5,1.2E-3.5
在C语言中,则以”E”或”e”后跟一个整数来表示”10”为底数的幂数。(e代表10,3代表3次方)
12.3e3 == 12.3*10^3
e代表基数10 3代表3次方
1.2345e+002==
e+002表示10的2次方。科学计数法,用e表示10,加号表示正整数次方;减后,表示负整数次方,这里就是等于123.456实型常量的类型细分:
- 默认为double型,例如3.14就是double类型。
- 后面加f或F认为是Float型,例如:3.14f就是Float类型。
%8.2f表示:
整数8表示宽度为8,也就是8个字符,而.2表示精度为2,也就是小数点后有2位。
3.4.5 浮点型变量
浮点型分为单精度型(float)和双精度型(double)两种。
Float 型数据占用4个字节(32bit)存储单元,提供的有效数字是6 - 7位。
Double 型数据占用8个字节(64bit)存储单元,提供的有效数字是15 - 16位。
3.4.6 字符型数据
字符”1”和整数1是不同的概念:
字符”1”只是代表一个形状为”1”的符号,在需要时按原样输出,在内存中以ASCII码形式存储,占1个字符。
整数1是以整数存储方式(二进制补码方式)存储的,占2个或4个字节。
注意:
- 字符型常量和字符串常量长度不一致,sizeof(‘A’)和sizeof(“A”);长度不一致,前者为4个字节,后者为2个字节。
- 一个汉字占两个字节,只能由宽字符存储,字符串存储一个汉字输出是3个字节,因为字是两个加个”\0”的一个字节。
- sizeof数据类型和sizeof字符型常量不是一个东西例如:
Char a = ‘A’;sizeof(ch)是一个字节,但sizeof(‘A’)是四个字节
- Wchar_t wch = “” 宽字符 可以存储汉字;’’是窄字符。
3.4.7 字符型变量
字符型变量在内存中占一个字节,由于存储的是字符的二进制ASCII码,与整型数据存储方式类似,字符型数据和整数数据可以相互运算。
注意:
字符串常量不允许赋值给字符型变量,C语言也没有专门的字符串变量。要在内存中存取字符串,只能使用数组或指针。
设置默认控制台前景和背景颜色:
COLOR [attr] attr 指定控制台输出的颜色属性
颜色属性由两个十六进制数学指定,第一个为背景,第二个则为前景。每个数字可以为以下如何值之一(在CMD输入Color ?也可查询):
0 = 黑色 | 8 = 灰色 |
---|---|
1 = 蓝色 | 9 = 淡蓝色 |
2 = 绿色 | A = 淡绿色 |
3 = 浅绿色 | B = 淡浅绿色 |
4 = 红色 | C = 淡红色 |
5 = 紫色 | D = 淡紫色 |
6 = 黄色 | E =淡黄色 |
7 = 白色 | F = 亮白色 |
如果没有给定任何参数,该命令会将颜色还原到CMD.exe 启动时的颜色。这个值来自当前控制台窗口、/T 命令行开关或 DefaultColor 注册表值。
如果用相同的前景和背景颜色来执行 COLOR命令,COLOR命令会将ERRORLEVEL设置为1。
例如:COLOR fc 在亮白色上产生亮红色
3.4.8 ASCII码
实际就是将我们所有字符都标号了。他们可以相互转换
ASCII值 | 控制字符 | ASCII值 | 字符 | ASCII值 | 字符 | ASCII值 | 字符 |
---|---|---|---|---|---|---|---|
000 | NUL | 032 | (space) | 064 | @ | 096 | ` |
001 | SOH | 033 | ! | 065 | A | 097 | a |
002 | STX | 034 | “ | 066 | B | 098 | b |
003 | ETX | 035 | # | 067 | C | 099 | c |
004 | EOT | 036 | $ | 068 | D | 100 | d |
005 | END | 037 | % | 069 | E | 101 | e |
006 | ACK | 038 | & | 070 | F | 102 | f |
007 | BEL | 039 | ‘ | 071 | G | 103 | g |
008 | BS | 040 | ( | 072 | H | 104 | h |
009 | HT | 041 | ) | 073 | I | 105 | i |
010 | LF | 042 | * | 074 | J | 106 | j |
011 | VT | 043 | + | 075 | K | 107 | k |
012 | FF | 044 | , | 076 | L | 108 | l |
013 | CR | 045 | - | 077 | M | 109 | m |
014 | SO | 046 | 。 | 078 | N | 110 | n |
015 | SI | 047 | / | 079 | O | 111 | o |
016 | DLE | 048 | 0 | 080 | P | 112 | p |
017 | DC1 | 049 | 1 | 081 | Q | 113 | q |
018 | DC2 | 050 | 2 | 082 | R | 114 | r |
019 | DC3 | 051 | 3 | 083 | S | 115 | s |
020 | DC4 | 052 | 4 | 084 | T | 116 | t |
021 | NAK | 053 | 5 | 085 | U | 117 | u |
022 | SYN | 054 | 6 | 086 | V | 118 | v |
023 | ETB | 055 | 7 | 087 | W | 119 | w |
024 | CAN | 056 | 8 | 088 | X | 120 | x |
025 | EM | 057 | 9 | 089 | Y | 121 | y |
026 | SUB | 058 | : | 090 | Z | 122 | z |
027 | ESC | 059 | ; | 091 | [ | 123 | { |
028 | FS | 060 | < | 092 | \ | 124 | | |
029 | GS | 061 | = | 093 | ] | 125 | } |
030 | RS | 062 | > | 094 | ^ | 126 | ~ |
031 | US | 063 | ? | 095 | _ | 127 | ▢ |
3.4.9 隐式类型转换(小转大)
整型、实型、字符型数据之间可以混合运算。例如:
10 + ‘a’ + 1.5 - 8765.1234*’b’
不同数据类型之间运算会进行自动类型转换,规则如下:
转换步骤:简单而言就是小的转大的。
INT -> unsigned -> long -> double。
运算过程:
- 进行 10 + ‘a’ 的运算:将’a’转换成整数97,得到和为107.
- 进行 107+1.5 的运算:将107转换成 double 型,得到和为 108.500000.
- 进行 8765.1234*’b’的运算:将’b’转换为double型数 98.000000,再和8765.1234相乘。
- 把之前得到的108.500000减去8765.1234*’b’的值,就完成了整个表达式的求值。
3.4.10 强制类型转换(大转小)
一般形式:(类型名)(表达式)
例如:
(int)(x+y)
(int)x+y
(double)(3/2)
(int)3.6
|
强制转换得到所需类型的中间变量,原变量类型、变量值保持不变。
注意:
- 当赋值给一个已经声明的类型的时候会发生类型转换。
- Char类型转变其它类型是ASCII码的相加或相减。
精度损失问题:较高类型向较低类型转换时可能发生精度损失。
类型间转换:
不同类型的整型数据所占的字节数不同,他们在相互转换时需要格外小心,不要把过大的数据放在过小的数据类型中,在把占字节较大的数据赋值给占字节较小的数据时,防止出现以下的情况:
int a = 2147483648; |
这样赋值后,输出变量a的值并非预期的 2147483648,而是 -2147483648,原因是2147483648超出了int类型能装载的最大值,数据产生了溢出。如果换一种输出格式控制符,代码如下所示:
printf("%u",a); |
输出的结果就是变量a的值,原因是%u是按照无符号整型输出的数据,而无符号整型的数据范围上限大于2147483648这个值。