学习笔记仅供参考
基础介绍
程序就是一组计算机能识别的指令,计算机的一切操作都是由程序控制的。
人和计算机都能识别的语言就是就是计算机语言,计算机工作是基于二进制的。计算机能直接识别的二进制代码就是机器指令,机器指令的集合就是机器语言。
机器语言与人们习惯使用的语言差别太大,所以人们创造出了符号语言,计算机不能直接识别符号语言的指令,需要汇编程序软件将符号语言指令转成机器指令(二进制代码)。机器语言与汇编语言是完全依赖具体机器特性的,是面向机器的语言(低级语言)。
高级语言与人们习惯使用的语言接近,且语言功能强,不依赖于具体的机器,被称为高级语言。计算机也不能直接识别高级语言写的程序,由编译程序将用高级语言写的程序(源程序)转换成机器指令(二进制代码)的程序(目标程序)。
高级语言发展阶段:1.非结构化语言,编程风格随意,流程随意跳转,程序难以阅读、维护。 2.结构化语言,规定了程序必须由良好特性的基本结构组成,流程不能随意跳转,程序总是由上而下执行。C语言就是结构化语言。 3.面向对象语言。python、java、c++...
C语言特点
1972年贝尔实验室的D.M.Ritchie在B语言的基础上设计出了C语言,最初C语言是为了描述和实现UNIX的。C语言是用途广泛、功能强大、使用灵活的过程性编程语言,既可以用于编写应用软件,又可以编写系统软件。
C语言特点:1.语言简洁、紧凑,使用方便灵活。 2.运算符丰富。 3.数据类型丰富 4.具有结构化控制语句,C语言是完全模块化和结构化的语言。 5.语法限制不太严格,程序设计自由度大。 6.C语言允许直接访问物理地址,能进行位操作。 7.C语言编写的程序可移植性好,因为C的编译系统相当简洁。 8.生成目标代码质量高,程序执行效率高。
每一个C程序都必须有一个main函数。整型函数(一般除了void型)的函数值需要用return语句带回到调用函数位置,main函数中return返回值一般指定为0。
//单行注释 /* */多行注释,注释如果嵌套了则最开始的注释符之后的全是注释内容,如果//和/*出现在字符串中,他就是字符串的一部分。 例:printf("//how! /* do */\n"); 按原样输出。
C语言结构
一个程序由一个或多个源程序文件组成。
一个源程序文件包括了:1.预处理指令#include<stdio.h>之类的(以#开头,),此命令在编译系统对源程序"翻译"前,由预处理器(预编译器)进行预处理,然后与其他部分一起组成可以被编译的源程序,#include<stdio.h>指令就是将stdio.h的头文件的内容读进程序替代#include<stdio.h>。 2.全局声明,声明变量。 3.函数定义,指定每个函数功能。
函数是C程序的主要组成部分(基本单位),一个C语言程序是由一个或多个函数组成的。可以使一个程序包含若干源程序文件,每个源程序文件又可以包含若干函数。一个源程序文件就是一个程序模块,即将一个程序分为若干个程序模块。在编译的时候是以源程序文件为单位进行的,在分别对各源程序文件进行编译并得到相应目标程序后,再将这些目标程序连接成一个统一的二进制可执行程序。在程序中被调用的函数可以是系统提供的库函数,也可以是用户自己设计的函数(自己设计的函数调用前需声明)。
函数组成:1.函数首部。即第一行包括 函数名、函数类型、函数属性、函数参数名、参数类型。一个函数名后必须跟一对(),void可以写在括号内。 2. 函数体,函数首部下用{}括起来部分, {}内包括声明部分(定义变量)和执行部分(C语句)。
C程序总是从main函数开始执行(无论main函数位置在哪里)。 程序中要求计算机完成的操作是由函数中C语句完成的。 每个数据声明和语句最后都要加 ; 符号。 C语言本身不提供输入输出语句(输入输出操作由库函数实现,这可以使C语言本身的规模较小、编译简单、容易在各种机器上实现,程序具有可移植性)。 程序应当包含注释。
C程序运行过程
1.上机输入和编辑源程序,.c后缀文件。 2.对源程序进行编译,C编译系统先用预处理器对预处理指令进行预处理,再进行编译。编译首先先对源程序进行检查,有错就进行调试,直至无错。然后编译程序将源程序转化为二进制形式的目标程序(.obj后缀文件)。 3.将所有编译后的目标文件连接,再与库函数相连接成一个整体,生成一个计算机可执行的程序,可执行程序(.exe后缀文件)。 4.运行可执行程序,得到运行结果。如果结果不是想要的,应检查源程序。
为了编译、连接和运行C程序,必须要有相应的编译系统。
程序设计任务
1.问题分析 2.设计算法 3.编写程序 4.对源程序进行编辑、编译和连接 5.运行程序,分析结果 6.编写程序文档
算法
算法是解决“做什么”和“怎么做”的问题。广义上讲,为了解决一个问题而采取的方法和步骤就称为“算法”。计算机的算法分为数值运算算法和非数值运算算法。
算法具有:有穷性,确定性,有零个或多个输入,有一个或多个输出,有效性。
算法表示方式:自然语言,流程图(菱形的判断框有一个入口,两个出口),N-S流程图,伪代码,计算机语言。
三种基本结构:顺序结构,选择结构,循环结构。三种结构都只有一个入口和一个出口。结构内的每一部分都有机会被执行到,且结构内不存在“死循环”。由三种基本结构构成的算法属于"结构化"算法,他只在基本结构内才允许跳转。基本结构可以由人们自己定义,用自己定义的基本结构可组成结构化程序。
结构化程序设计方法
基本思路是将一个复杂问题的求解过程分阶段进行,每个阶段处理的问题控制在人们容易解决的范围内。 方法:1.自顶向下 2.逐步细化 3.模块化设计 4.结构化编码
在程序设计中常采用模块化设计,即将一个程序模块后根据其功能将它划分为若干个子模块,如子模块还过于复杂,可以将其划分为更小的模块。程序中子模块在C语言中常用函数实现(一般不超过50行,即大概一页),划分模块时应注意模块的独立性,即一个模块完成一项功能,耦合性越少越好。
数据类型
常量,其值不能被改变。 1.整型常量(100、123、0、-123.... 12L 12l) 2.实型常量,十进制小数形式(1.23、0.231、-1.12、12.0 .....) 指数形式(12.e3、-234.12e-25、0.12E-25...),e/E表示以十为底的指数,e/E前后必须得有数字,且后面的数字必须为整数。 3.字符常量,''括起来的单个字符('a'、'A'、'1'、'?'...),以ASCII码存储在存储单元。 用' '括起来的转义字符,转义字符 \ 将后面的字符转化成另外的意义(\' \" \? \\ \a \b \f \n \r \t \v \0(0代表一个八进制位数字) \xh(h代表一个十六进制位数字)) 4.字符串常量," "括起来的若干个字符。 5.符号常量,#define PI 12 以这种方式定义的PI就是符号常量,符号常量只是一个值,不占内存,预编译后符号就被值替换了。
0167,0开头,表八进制数, 0x2ff,0x/0X开头表十六进制数
变量,必须先定义后使用,变量名其实是一个存储地址,这个地址对应的内存单元存放着变量值。每一个变量都有一个确定的类型(变量的一个重要属性),变量是占用存储单元的。而类型是变量的共性,不占存储单元。
常变量,const int a=3; 以此方式定义的就是常变量。常变量是有名字的不变量,有类型,占存储单元。常量是没有名字的不变量。
标识符:由大小写字母、数字和 _ 下划线组成,且第一个字符必须是字母或下划线。标识符如果与关键字同名,会覆盖其原有意义。
整型数据,int(4),short(2),long(4),long long(8),系统用补码形式存储,正数的补码就是他的二进制,负数的补码要先将此数的绝对值写成二进制,然后对这个二进制按位取反,再加1。存储单元最左面一位是用来存放符号的(0正,1负)。 一个字节(8bit)最多存放255个数 sizeof(short);测量类型或变量的长度。 unsigned 类型符号,此方式定义无符号整数类型(无法存储负数,他将最左侧一位也用来存放数据,用%u格式输出)
字符型数据,字符是按ASCII码存储在内存中(在C语言中一个字节存储一个字符,最左侧为0,所以一个字节一般可以表示127个字符), 字符变量用 char a;a='A'; 定义并赋值,字符变量实质上是一个整型变量(0~127),可以是%d十进制整数输出,也可以%c字符输出。
浮点型数据,就是有小数点的实数。C语言将实数以指数形式存储(1.321*10的零次方、13.21*10一次方....都表示同一个数1.321) float(4字节得出6位有效数) double(8字节得出15位有效数) long double(8字节) 由于存储单元长度是有限的,不可能得到完全精确的值,小数位越多,有效数越多;指数位越多,表示数值范围越大。 C语言自动在浮点算数运算时将float转为double。
凡是以小数形式和指数形式出现的实数都是浮点型常量,以指数形式存储。强制指定常量类型(在数后加f/F,float型;L/l,long double型)
赋值要注意数据类型能存放的最大范围
强制类型转换:(double)a (int)(x+y) (float)(5%3) 转换只是在运算时得到一个需要类型的中间值,不影响变量原本类型。
运算符优先级:
1:( ) [ ] -> .
2:! ~ ++ -- -负号运算符 (类型)类型转换运算符 *指针 &取地址 sizeof()长度运算符
3:*乘 / %
4:+ -
5:<< >>
6:< <= > >=
7:== !=
8:&按位与
9:^
10:|
11:&&
12:||
13:(? :)条件运算符
14:= += -= *= /= %= >>= <<= &= ^=
15:,
除了2、13、14是自右向左,别的都是自左向右。
+加,-减, *乘, /除, %取余(两边必须是整数),a++/--(先用a,再使a加/减1),++/--a(先使a加/减1,再用a)
不同类型数据运算,结果为字节最多的类型(注意最后赋值变量类型)。int型运算结果为int型,int型取值为向0靠拢,直接舍去不需要部分。float型直接在需要的数后加上.0,double变float是将6~7位有效数取出并存储到float型变量中,如果超出float存储范围,报错。字符型变为整型变量直接按ASCII码赋值。多字节赋给少字节,直接取出(先变二进制形式)低字节赋值到被赋值变量(将二进制转换就是变量的值)。
C语句
控制语句:if()...else... for()... while()... do...while() continue break switch return goto
函数调用语句:函数首部;
表达式语句:一个表达式最后加 ; 组成
空语句: ;
复合语句: { }内的声明和语句组成
赋值语句
赋值运算符= += -= *= /= %= >>= <<= &= ^=,a+=1 等于 a=a+1, a*=1+2 等于 a*(1+2)。使用=进行赋值运算时左侧应该是一个可以修改的"左值(左值应为存储空间并可以被赋值)",算术表达式与常量,常变量都不可作为左值。左值可以出现在右侧进行运算
变量赋初值,int a=1,b=2; 不能写成 int a=b=2;
数据的输入输出
没有输出的程序是没有意义的,输入输出是程序的基本操作之一。输入输出是以计算机为主体而言的。输入输出语句在stdio.h文件内。#include<stdio.h>是将文件调用到程序取代本命令位置(<>是直接在C编译系统子目录寻找文件,""是先在用户当前目录寻找,再去C编译系统子目录找)。
printf(“格式”,输出列表); 输出函数,格式包括格式声明(%开头,加格式字符),普通字符(""内按原样输出的字符) 输出列表,可以是常量、变量、表达式
格式字符:d有符号的十进制数, i与d相同, o以八进制整数输出(不带符号), x/X以十六进制整数输出(不带符号), u输出无符号型数据, c输出一个字符, s输出一个字符串, f以小数形式输出实数(小数默认6位), e/E以指数形式输出实数(默认指数5位小数6位,e与指数符号各占一位,printf("%e",123.456);结果为1.234560e+002), g/G输出浮点数,系统自动选择f或g,选长度短的那个,不输出无意义的0。 附加字符:l(用在d、o、x、u前,输出长整型), m(数据最小宽度), n(对实数表输出n位小数,对字符串表截取n个字符), -输出的数或字符串向左靠。 %4d、%ld、%4c、%m.nf %7.2f %-7.2f、%13.2e... 格式控制能包含的转义字符:\n \t \b \r \f \101(输出A)... 如果想输出%需要“%%”才能输出一个%
scanf("格式",地址列表); 输入函数,格式与printf相差不多,地址列表就是&加变量名。
格式附加字符。l输入长整型数据(后面跟d o x u f e), h输入短整型数据(后跟d o x), *本输入项在读入后不赋给相应变量, 域宽 指定输入数据所占列数。
地址列表一定是&a、&b...这种类型。如果格式内有格式声明以外的字符,要按原样输入(格式声明处替换为相应数据)。在用%c时,空格字符与"转义字符"都是有效字符。输入数值时,两个数值间需要空格,如果输入数值时输入不属于数值的字符,默认此数值输入结束。
putchar(变量); 输出一个字符。可以输出转义字符(\与\x是输出其十进制数字对应的ASCII字符)
getchar(); 输入一个字符,此函数和会返回一个输入字符对应函数值,a=getchar(); printf("%c",getchar()); 此函数可以获取控制字符。PS:回车等于换行符(\n),键盘输入信息是先将输入的字符存储到键盘的缓冲器,只有按了Enter才会把这些字符送入计算机。
PS:int型变char型在0~127内任意转换,在255内超过127的按补码形式,超过一个字节,直接取最低字节位二进制数。 char占1字节
选择结构
基本结构: 判断条件为真执行语句。if语句结束条件是先执行内嵌语句,再检查有无else,如无else,则整个if语句结束,如有else则继续执行else后语句(else子句不能作为语句单独使用,它与if配套使用(else与它上面第一个if组成一整个if语句。))。
if(判断表达式)
{
语句;
...;
}
-----------------------
if()语句;
else
{
语句;
...;
}
-----------------------
if()语句;
---------------------
if()语句;
else if()语句;
....
else 语句;
关系运算与逻辑运算、条件运算
关系运算符:< <= > >= == !=
用关系运算符把两个数值或数值表达式连起来就是关系表达式,关系表达式的结果只有"真""假",在C逻辑运算中,以"1"代表"真"(非0数),"0"代表"假"。
逻辑运算符:&&(两边都为真,结果为真) ||(两边一个为真,结果为真) !(取反)
逻辑表达式得到的结果是一个逻辑量"真""假"。在C语言表示逻辑运算结果是以"1"代表"真"(非0数),"0"代表"假",判断一个量是否为“真”,0代表假,非0代表真。
条件运算:表达式1?表达式2:表达式3; 1为真输出2,1为假输出3
选择嵌套
if()
if() 语句;
else 语句;
else语句;
---------------
if()语句;
else
if() 语句;
else 语句;
----------------
if()
if() 语句;
else 语句;
else
if() 语句;
else 语句;
switch多分支选择
switch(表达式)
{
case 常量: 语句;break;
case 常量: 语句;break;
...
default: 语句;
}
switch表达式一般为一个整数类型,将它与case逐个比较,满足条件就执行语句,都不满足就执行default语句并退出。default位置任意,可以没有default,没有default且不符合任意case程序直接退出switch。
break作用是跳出switch,如果case语句执行后没有break。那么程序会自动执行下一个case后语句,直至遇见break或执行完最后一条语句,如果switch内无break,执行default后退出(default后 没有break也会退出,default不是switch最后一个语句且语句后没有break,也会继续执行下条语句,直至遇见break或最后一条语句。)
循环结构
循环结构就是解决需要重复处理的问题。
while,do...while,for
while(循环条件) do for(变量赋值;循环条件;变量增值)
{ { {
语句; 语句; 语句;
... .... ....
语句; 语句; 语句;
} }while(循环条件); }
while 当循环条件为真执行循环体
do...while 先执行循环体,再判断条件是否为真,如为真继续执行
//while与do...while都需要在执行循环语句之前将变量赋值,再判断变量是否符合循环条件
for 循环条件为真,执行循环体----(;;)内可以这样写
***当循环变量的值超过循环条件时,循环结束。
//for与while无条件等价
//有多个条件,可以用','表达式表示,逗号表达式自左至右求解。
//for可以给变量赋初值,但此变量只能在本循环内用
循环可以互相嵌套,三种循环结构狗可以用break跳出循环,continue结束本次循环。
break,提前中止循环,跳出循环体,接着执行循环体下面的语句。
continue,跳出本次循环,跳过循环体内,此命令下的语句,重新判定条件
数组
数组中每个元素都是同一数据类型。
一维数组
定义: 类型符 数组名[常量表达式]; int a[2]; float[3+1]; 定义数组时需要指定数组元素个数。数组长度=元素个数*类型 数组元素下标从0开始。
引用数组:数组名[下标]; a=sz[4]; 将数组第4个元素赋值给a。 数组只能一个元素一个元素的赋值和调用。
初始化:int a[3]={0,1,2}; 或者 a[0]=0;a[1]=1;a[2]=2; int a[10]={0,1,2,3};此时数组内十个元素也全被赋值了,后6个默认赋值为0。int a[]={1,2,3,4,5};此数组长度为4,如果没指定数组长度,数组会根据花括号中个数确定数组长度。
二维数组
定义:类型符 数组名[常量表达式][常量表达式]; 可以将二维数看成一个一维数组,但是这个一维数组每个元素都是一个数组(第一个[ ]指定行数,第二个[ ]指定每行元素个数)。二维数组元素是按行方的,例:a[2][2]; 它的存放顺序是a[0][0],a[0][1],a[1][0],a[1][1]。
引用:数组名[下标][下标]; a[2][3]; 就是数组中第二行下标为3的元素。
初始化:int a[2][2]={{0,1},{2,3}}; int a[2][2]={0,1,2,3}; int a[][2]={1,2} 默认赋值为0,如果没指定数组行,数组会根据花括号中个数确定数组行数。即第二维长度不可省略
字符数组
C语言中没有字符串类型,也没有字符串变量,字符串是存放在字符数组中的。
定义:char c[10]; 定义了一个包含十个元素的字符数组。
初始化:char c[3]={'a','b','c'}; char c[]={'a','b','c'}; char c[2][2]={{'a','b'},{'c','d'}}; char c[][2]={{'a','b'},{'c','d'}}; 如果花括号内个数大于数组长度,会出现语法错误。如果花括号内个数小于数组长度,默认赋值'\0'。 由于字符型数据是根据ASCII码存储的,整型数组也可存放字符数据:int c[10]; c[0]='a';
引用:c[1]; c[1][1];
字符串和字符串结束符: C语言将字符串作为字符数组处理,C语言以字符 '\0’ 作为结束标志。如果字符数组存有若干字符,前9个都不是空字符,第十个为'\0',则认定数组为一个字符串,其有效字符数为9。即遇见'\0',表示字符串结束,将它前面的字符组成一个字符串。C系统在用字符数组存储字符串常量会自动加一个'\0'作为结束符。在程序中往往靠检测'\0'的位置来判断字符串是否结束,"C program"一共9个字符,在数组中他站10个字节,'\0‘是系统自动加上的。 char c[]={"this is a C"}; char c[]=“this is a C”; 此时字符数组c占12个字节,而不是11个。它与char c[]={'t','h','i','s',' ','i','s',' ','a',' ','C','\0',}; 相等。 如果从键盘读入新字符到c内,而最后不加'\0',那么新字符串串将于老字符串连成一片。在输出字符串时,只有遇见'\0'才会结束。
字符数组的输入输出: %c输出单个字符,%s输出字符串,直至遇见'\0'停止;用此格式时,输出项是数组名(c);若字符数组长度大于字符串长度,遇到'\0'也结束。可以用scanf(“%s”,c);输入一个字符串,回车后系统默认在后面加一个'\0';scanf("%s%s%s",a,b,c);可以以空格分隔输入三个字符串,他们将分别存储在三个字符数组内。如果是scanf("%s",a); 输入了以空格分隔的三个字符串,那么系统在识别第一个空格时会将其自动转化为'\0',后面的字符将不会被存储到字符数组内。
字符串处理函数:进行字符串处理时需要 #include<string.h>
puts(字符数组); 将一个以'\0'为结束符的字符串输出到终端。此函数会将'\0’自动转化为'\n'。
gets(字符数组); 从终端输入一个字符串到字符数组,并得到一个函数值(字符数组起始地址)
strcat(字符数组,字符数组); 将后面字符数组连接到前面字符数组后,函数返回前面字符数组的地址。 前面字符数组必须足够大,连接时将前面字符数组后的'\0'取消。
strcpy(字符数组,字符数组); 将后面字符数组复制到前面字符数组中,前面字符数组不得小于后面字符数组长度。后面字符数组可以是一个字符串常量(“ ”)。复制时直接将后面字符串与'\0'一起复制到前面字符数组中,所以前面字符数组大于后面字符串的元素的存储内容是原有的。不能用赋值语句将字符串常量或字符数组直接给一个字符数组赋值,只能赋值单个字符。
strcnpy(字符数组,字符数组,n); 将后面字符串的前n个字符复制到前面字符数组中取代前n个字符,字符个数n不得多余前面字符数组中原有字符(不包括'\0')。
strcmp(字符串,字符串); 比较两个字符串或字符数组大小(从左至右按ASCII码值比较),结果以第一对不同的字符为准(英文字母后面比前面大,小写比大写大),前大于后,返回正整数;前小于后,返回负整数;相等,返回0。
strlen(字符数组); 测量字符数组长度(不包括'\0')
strlwr(字符串); 将字符串中大写字母换成小写。
strupr(字符串); 将字符串中小写字母换成大写。
函数
函数就是功能,每一个函数都用来实现一个特定的功能,函数名应反应其功能。
设计一个较大的程序时往往需要将其分成若干个模块,每个模块都包括一个或多个函数。一个C程序可有一个主函数和若干个其他函数构成。有主函数调用其他函数,其他函数也可以互相调用,同一个函数可以被一个或多个函数调用任意次数。所有函数都是平行的,在定义函数时是分别进行的,实相互独立的。一个函数并不属于另一个函数,即函数不能嵌套定义。函数可以互相调用,但是不能调用main函数,main是被操作系统调用的。无参函数:在调用此函数时,主调函数不需要向被调函数传递数据,无参函数是用来执行一组操作的。有参函数:再调用函数时,需要通过参数向被调函数传递数据。
定义函数:C语言要求,程序总用到的所有函数必须先定义后调用。 定义规范:1.指定函数名字。 2.指定函数类型(函数返回值类型)。 3.指定函数的参数名字和类型(无参函数不需要定义)。 4.指定函数应该完成什么操作(定义函数功能)。
定义无参函数 定义有参函数 空函数
类型名 函数名() 类型名 函数名(参数列表) 类型名 函数名()
{ { { }
函数体 函数体
} }
类型名 函数名(void)
{
函数体
}
函数体包括:声明部分和语句部分
void类型表示函数没有返回值。
定义空函数是为了以后扩充新功能方便。
调用函数
函数名(实参列表) 调用无参函数不需要参数但()不能省,多个参数用,隔开。 函
数表达式:c=max(x,y); 将函数的返回值赋给c,max(x,y)可以出现在表达式中,也可以被其他函数调用。
函数参数:调用时写在()内是实参,即函数用来运算的数据。函数定义时()内是形参,即用来接收实参。在调用函数过程中系统会把实参的值传递给形参(这个过程叫"虚实结合"),该值在调用函数期间有效。实参可以是常量、变量、表达式,但一定要有确定的值,在被调用时传递。形参与实参的类型应该相同或赋值兼容。
函数调用过程
1.在定义函数中指定的形参,没有调用函数时,他们是不占内存的,在发生函数调用时,函数形参才会被临时分配内存单元。
2.实参的值传递给对应的形参(调用函数时( )内参数位置)。
3.形参获取到数据后后进行运算。
4.通过return语句将函数值带回到主调函数中(返回值类型与函数类型一致)。
5.函数调用结束释放形参单元(此时实参任然是原来的值,没有改变)。
PS:实参向形参传递数据是"值传递",单向传递,只能由实参传给形参,不能由形参传给实参,实参和形参在内存中占有不同的存储单元,实参无法得到形参的值。
函数返回值
就是通过调用函数后,希望得到的一个确定的值。
函数的返回值通过函数中的return语句获得的(需要从函数中获取返回值才需要此语句,一个函数中可以有多个return,执行到那个return哪一个return就起作用)。
return (表达式); 在定义函数时指定的类型一般应和return中表达式类型一致。如果不一致,以函数类型为准(数值型数据,可以自动转换类型)。
函数的声明
被调用函数必须是已经定义的函数(库函数或自定义)。如果是库函数,应该在本文件开头用#include指令将有关库函数文件"包含"到本文件中。如果是自定义函数,被调函数应该与主调函数在同一文件,主调函数中还要对被调函数进行声明。声明的作用是把函数名、函数参数的个数、参数类型等信息通知编译系统,以便在调用函数时,编译系统能正确识别并检查函数。 函数声明,可以写成函数的首部加个分号,也可以省略形参名。(int max(int a,int b); int max(int,int);) 函数的声明与定义不是一样的,函数定义是函数功能的确定,函数声明只是把函数的基本信息通知编译系统(声明无函数体)。
函数递归调用:在调用一个函数的过程中又直接或间接的调用该函数本身。
数组作为函数参数:数组元素可以做函数实参,但不能做型参。 x=max(a[1],a[2]);
数组名可以做实参也可以做形参。实参数组与形参数组类型应一致。 x=max(数组名); 形参数组可以不指定大小,应为C语言编译系统并不检查形参数组大小,只是将实参数组的首元素地址传递给形参组名,值传递后形参数组首元素就与实参数组首元素具有同一地址。 多维数组元素,多维数组名也可以作为函数参数,但是第二维大小不可省略。
内部函数:函数只能被本文件中其他函数调用。 static 类型名 函数名(形参列表)..... 内部函数又称静态函数,它的使用范围只局限在所在文件。
外部函数:extern 类型名 函数名(形参列表)..... 这就定义了外部函数,可以被其他文件调用(可以省略extern,系统默认函数为外部函数)。在其他文件中调用此函数声明时,需要在声明语句前面加上extern,表示该函数“是在其他文件中定义的外部函数”。
局部变量和全局变量
定义变量的3种情况:1.在函数的开头定义。2.在函数内复合语句内定义。3.在函数外部定义。 在一个函数内部定义的变量只在此函数范围内有效,在复合语句内定义的变量只在复合语句内有效,这两种都是局部变量(不同函数中可以使用同名变量,他们代表不同的对象,互不干扰。形式参数也是局部变量)。在使用时开辟单元存储,使用结束释放。
在函数外部定义的就是全局变量(外部变量),全局变量定义变量的位置开始直到本源文件结束都有效。 在一个函数中它既可以使用在本函数内定义的局部变量,也可以使用全局变量(在一个函数内改变了全局变量的值,可以影响到其他函数中全局变量的值)。全局变量在程序的全部执行过程中都占存储单元(默认赋值0)。
PS:变量同名,局部变量范围内,全局变量"无效",局部变量有效。
变量的存储方式与生存周期
从变量作用域来看,变量分为全局变量和局部变量。从变量存在时间(生存期)来看,变量分为静态存储方式(系统分配固定的存储空间)和动态存储方式(根据需求动态分配存储空间)。
存储空间分为:程序区、静态存储区、动态存储区。全局变量全部存放在静态存储区。
动态存储区存放:函数形参、函数中没有用static声明的变量、函数调用时的现场保护和返回地址...(每次调用函数,局部变量的地址都是不同的) 每个变量和函数拥有数据类型和数据的存储类别。
C存储类别包括:自动的、静态的、寄存器的、外部的
自动变量:默认类别,存储在动态存储区,使用结束自动释放。
静态局部变量:用static声明的局部变量,在静态存储区,在整个程序运行期间不释放。静态局部变量只在编译时赋初值(只赋值一次,下次调用函数使用此变量时,变量的值保留上次函数调用结束时的值,不赋值默认为 0或'\0’)。
寄存器变量:用register声明的变量。 使用频繁的变量,存储在CPU寄存器中,此区域存取速度远高于内存存取速度。
在一个文件内扩展外部变量的作用域:用extern对变量进行“外部变量声明”,表明将一个变量的作用域扩展到此外位置(就是将外部变量重新声明一次,不过要加上extern)。
将外部变量作用域扩展到其他文件: 方法同上,不过是在两个在同目录下不同文件中进行重新声明。系统遇到extern时,会现在本文件内寻找外部变量的定义,找不到,就在连接时从其他文件中寻找;再找不到,报错。
将外部变量作用域限制在本文件:用static对变量进行“外部变量声明,声明过后的外部变量叫静态外部变量。
auto、register、static定义时必须以此种方法:static int a;
指针
内存区的每一个字节有一个编号,这就是”地址“,由于通过地址可以找到所需要的变量单元,可以说地址指向该变量单元,因此可以将地址形象地称为”指针“。因为C语言中数据是分类型的,对于不同类型的数据,在内存中所分配的存储单元大小(即字节数)和存储方式是不同的,所以为了有效地存储一个数据,除了需要位置信息外,还需要该数据的类型信息。C语言中的地址包括位置信息和他所指向的数据的类型信息,即它是”带类型的地址“。int a; &a就是整型变量a的地址。
通过变量名找到相应的地址,再根据数据类型读出相应字节,最后转换成需要的格式,叫”直接访问“。将变量的地址存放在另一个变量中,然后通过该变量来访问,叫”间接访问“。
例:scanf("%d",&a); int *p; p=&a; printf("%d",*p); *表示该变量为指针变量
指向就是通过地址来体现的,如果有一个变量专门用来存放另一个变量的地址,它就是指针变量。指针变量的值就是地址。
指针优点:1.提高程序效率。 2.再调用函数时,通过改变指针指向的变量的值,可以从函数调用中得到多个可改变的值。 3.可以实现动态存储分配。
定义指针变量: int *p;
类型名 *指针变量名; 类型名为定义指针变量时必须指定的”基类型“,指针变量的基类型用来指定此指针可以指向的变量的类型。 int *p=&a;
如果一个指针指向一个整型变量,那使指针移动一个位置就是移动4个字节。
int * 可以叫做 指向int的指针或int指针
指针变量只能存放地址,不能存放整数。
引用指针
定义 int *p,a; 赋值 p=&a; 引用 printf("%d",*p); 输出变量a的值
*p=1; 相当于 a=1; printf("%d",p); 输出变量a的地址,即&a
*p代表指针变量p指向的对象
指针变量作为函数参数
int max(int *p1,int *p2).....
作用是将一个变量的地址传递到另一个函数中。在函数调用时,将实参(变量地址)传递给形参,此时对应一对实参和形参指向同一个地址,此时如果我们将*p1与*p2的值互换,我们就能实现实参的值互换(通过交换*p1,*p2实现)。函数使用指针变量作为参数,可以得到多个变化的值。实参与形参指针基类型必须一致。
指针引用数组
一个数组的每个元素都有相应的地址,指向这些元素地址的指针就是数组元素的指针。
在C语言中数组名代表数组首元素地址(即&a[0]),它是一个指针常量,所以a++无法实现,数组名只代表首元素地址。
int a[3]={1,2,3}; int *p; p=&a[0]; // p=&a[0] 等于 p=a
数组中的指针运算
指针已指向一个数组元素时可进行: + - ++ -- 运算,它的作用是让指针指向上一个(或多个)或下一个(或多个)元素。 *(p+i) *(a+i)都指向a[i]元素(在编译时,a[i]是按照*(a+i)处理的,[ ]是变址运算符a[i]按a+i计算地址)。 如果指针p1,p2指向同一数组,p2-p1就是两个元素之间所隔元素位数(地址不能相加)。
指针引用数组元素
*(p+i)、*(a+i)、a[i] 可以通过改变以指向数组元素指针变量的值使其指向不同元素,此时指针变量可以带下标p[i](系统将其处理成*(p+i))
数组名作为函数参数: C编译将形参数组名当作指针变量处理,数组名作为函数参数就是将指定数组的首元素地址传递给形参。
多维数组
注意数组名所指向的地址。 a[3][3]; 中a是a[0]地址,a[0],*(a+0) ,*a 是a[0][0]地址,a+1,&[a+1] 是a[1]地址,a[1] , *(a+1) 是a[1][0]地址,a[1]+2 ,*(a+1)+2 , &a[1][2] 是a[1][2]地址,*(a[1]+2) , *(*(a+1)+2) , a[1][2]是a[1][2]的值。a[1]等于*(a+1),指向a[1][0]地址。在二维数组中a[0]就是一维数组名,他是列元素a[0][0]的地址,所以a[0]+1中1是一个元素;a是二维数组名,它指向每行数组的起始地址,所以a+1中1相当于一行元素(一个一维数组)。 所以*a,*(a+1)就指向列元素,&a[0]指向行,&a[0]等于&*a。在二维数组中a+i,a[i],*(a+i),&a[i],&a[i][0] 的值相等,但是他们基类型不同。
int (*p)[4]; 表示p指向由4个整形元素组成的一维数组,p的基类型为由4个整形元素组成的一维数组。
指向多维数组指针变量
指向多维数组元素指针变量:int *p;int a[2][2];p=a[0]; a[1][1]等于*(p+3)
指向多个元素组成的一列一维数组的指针变量:int (*p)[2];
指针指向字符串
char *p=”abc“; 指向一个字符串。 char *p; p="abc"; 输出: printf("%s",*p);
字符指针可以作为函数参数
字符数组存放若干元素,每个元素是一个字符;字符指针变量存放的是地址
可以对字符指针变量赋值(只能赋值字符串),但不可以对字符数组名赋值。
编译时对字符数组分配若干存储单元存放各元素的值,而对字符指针变量只分配了一个存储单元。定义字符指针变量后应及时将一个字符变量地址或字符串地址赋值给它,未赋值指针不会指向具体的对象。
字符指针变量的值是可以改变的,而字符数组名代表一个固定的值。
字符指针变量指向的内容是不可以改变的,而字符数组中各元素的值可以改变。
字符指针变量可以指向格式符。
指向函数的指针
函数名代表函数的起始地址。 int (*p)(int,int); p是一个指向函数类型为int且有两个int型参数的函数。 定义:int (*p)(int,int); 赋值:p=max; 调用: a=(*p)(b,c); 定义后的函数指针变量只能指向定义时指定的类型的函数(指针可以指向多个同类型的不同的函数,引用指针时是最后指定的函数)。指向函数的指针变量不能进行运算。
指向函数的指针可以作为函数参数(就是将函数起始地址作为参数传递到其他函数)。 函数首部定义(声明):int fun(int a,int b,int (*p)(int,int)) 调用:fun(x,y,max) 一般 fun 内会有 jieguo=(*p)(a,b);
返回指针值的函数
定义函数: int *函数名(参数列表); 调用:指针变量=函数名(参数列表);
指针数组
一个数组,内部的元素是指针类型,就是指针数组。
定义: int *p[4]; p数组有四个元素,*表示此数组是指针类型,p[1],*p表示下标为1的字符串的地址,*p[1]表示下标为1的字符串的第0个元素。
赋值:char *p[4]={"abc","def","ghi","jkl"};
指针数组适合指向若干个字符串。
指向指针数据的指针
定义:char **p; 一般指向指针数组
指针数组作为main函数参数
例:int main(int argc,char *argv[]) 他们接收的是命令行参数,如果是带参数的main,第一个函数必须是int型,它的作用是接收形参个数,第二个必须是字符指针数组,用来接收命令行每个字符串首字符地址(可以实现参数回送)。简单说就是,在字符操作系统中,可以向程序传送命令行参数。
动态内存分配与指向它的指针变量
非静态局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区叫栈。C语言允许建立内存动态分配区,这些数据随时开辟随时释放,这个临时的区域叫队区。
以下函数存放在<stdlib.h>头文件中
malloc开辟动态存储区
(类型 *)malloc(unsigned int size); 分配一个长度为size的连续空间。函数返回值为所分配区域第一个字节地址,返回值类型为定义时指定的基类型。分配不成功返回NULL。
calloc开辟动态存储分区
(类型 *)calloc(unsigned n,unsigned int size); 分配n个长度为size的连续空间,足以保存一个数组。函数返回值为所分配区域第一个字节的指针,返回值类型为定义时指定的基类型。分配不成功返回NULL。
realloc重新分配动态存储区
(类型 *)realloc(类型 *p,unsigned int size); 将p指向的动态空间大小改为size,p值不变,失败返回NULL。
free释放动态存储区
类型 free(类型 *p); 释放p指向的动态空间,无返回值。
例: int *p; p=(int *)malloc(100); 开辟一个100字节的可以存放整型数据动态存储区。
void指针类型
void是”空类型“或”不指向确定的类型“,将它的值赋给另一指针变量时系统会自定进行类型转换。
结构体变量
结构体变量所占内存长度是各成员占内存长度之和。
有不同数据类型组成的数据结构。
定义结构体: struct 结构体名
{
类型名 成员名;
...
};
定义结构体变量: struct 结构体名 变量名{};
也可以这样定义:
struct 结构体名
{
类型名 成员名;
...
}变量名={数据,...},变量名{数据...}...;
定义结构体变量时{}内数据是依次赋给结构体各个成员的
int型与float型不需要''或""
对单个结构体成员赋值:struct St b={.name="zhengsan"};
引用: 结构体变量名.成员名
如果结构体成员又属于一个结构体:结构体变量名.成员名.成员名 (且只能对最低级的成员进行运算)
同类型的结构体变量可以互相赋值, 可以引用结构体变量成员地址 &s.name 也可以引用结构体变量地址 &s 。但是 结构体成员的值只能逐个输出。结构体成员名可以与程序中变量面名相同,他们是不同的概念。
结构体数组
在一个结构体变量中存放一组相关的数据(类似二维数组)。
struct 结构体名
{
类型名 成员名;
...
}数组名[ ]={ };
struct 结构体名 数组名[ ]={ };
结构体指针
struct 结构体名 *指针变量名; struct St *p; p=&St;
St.成员名、(*p).成员名、p->成员名 都指向同一成员
指向结构体数组的指针
struct St sz[4]={{...},{...}...}; struct St *p; p=sz;
(++p)->成员名 就指向各个数组的此成员名
p=(struct St *)sz[0].naem; 此时p指向sz[0]的name成员的起始地址
结构体变量和结构体变量的指针做函数参数
结构体变量的成员做参数:Sz[1].name...做实参可以进行值传递(类型应保持一致)。
结构体变量做实参:此方法占内存空间很大,执行函数改变的值也不能传递回主调函数。
指向结构体变量(或数组元素)的指针作为实参:将指向的地址传递给形参。
链表
链表有一个”头指针“变量,他存放一个地址,这个地址指向下一个元素,链表中每个元素被称为”结点“,每个节点包括用户需要用的实际数据和下一节点的地址。最后一个元素称为“表尾”,它的地址指向”NULL“,链表到此结束。
链表各元素在内存中的地址是随机的,要找到某元素,必须先找到上一个元素,然后依次查找。
链表必须用指针变量才能实现,即一个结点中包含一个指针变量,它指向下一节点的地址。
链表用结构体创建最合适,用指针类型成员存放下一节点地址。
动态链表的建立:
流程:定义head,p1,p2三个指针指向存储链表的结构体,用malloc开辟第一个节点,使p1,p2指向它,然后接收需要的数据,判断输入的数据是否为空,不为空就将head=p1就是将第一个节点赋给表头,使p2也指向p1(此时head与p2指向同一节点),再用malloc开辟第一个节点,使p1指向它,然后接收需要的数据,判断输入的数据是否为空,不为空就使p2->next=p1(应为此时head与p2指向同一节点,即头节点,此命令就是将下一节点地址给头节点指向下一结点指针变量),使p2=p1,再用malloc开辟第一个节点,使p1指向它,然后接收需要的数据,判断输入的数据是否为空,不为空就使p2->next=p1(此时p2指向第二个节点,此命令就是将第三个节点地址给第二个节点点指向下一结点指针变量),再用malloc开辟第一个节点,使p1指向它,然后接收需要的数据,判断输入的数据.......最后直至输入为空时,使p2指向下一节点的指针变量指向NULL(即表尾)。
输出链表
令一个指向链表结构体的指针变量指向表头,然后依次输出。
共用体
union 共用体名
{
成员列表;
}变量列表;
也可以这样定义变量: union 共用体名 变量名;
共用体变量所占内存长度等于最长成员的长度。
引用:共用体变量名.成员名
共用体类型特点:同一内存段可以存放几种不同类型的成员,但是每瞬间只能存其中放一个成员,即共用体只能存放一个值。共用体变量每次赋值只能对其中一种成员赋值,共用体变量起作用的成员是最后一次被赋值的成员。共用体变量所有成员地址相同。不能对共用体变量名赋值,也不能应用变量名获取一个值。共用体可以作为函数参数(C99)。共用体类型可以出现在结构体类型中,也可以定义共用体数组,数组也可以做共用体成员,结构体也可以出现在共用体中。
枚举类型
一个变量只有几种可能的值,就可以定义成枚举类型,枚举就是将变量所有可能的值一一列出来。
声明: enum 枚举类型名{所有可能的值}; 定义:enum 枚举类型名 枚举变量名;
enum {所有可能的值}枚举变量名; 不能直接对枚举元素赋值
C编译对枚举类型的枚举元素按常量处理,所以叫枚举常量。
每一个枚举元素都代表一个整数,从左到右默认为0,1,2,...
也可以认为指定数枚举元素值:enum W{a=7,b=2,c,d,e}qw,qe;
枚举元素可以用来做判断比较。
typedef声明新类型名
就是用一个新名字代替原本的类型名。定义的方式就是定义变量的方式,将变量名换成新类型名,并在最前面加上typedef。 也可以直接 typedef int INT;
文件
程序文件:源程序文件、目标文件、可执行文件...都是程序文件。
数据文件:文件内容不是程序,而是程序运行时读写的数据,就是存放数据的文件。
文件一般指存储在外部介质上数据的集合。
C语言将文件看作一个字符(或字节)序列,由一个个字符(或字节)的数据顺序组成。一个输入输出流就是一个字符流或字节流(二进制数据),流表示了信息从源到目的端的流动。
再输入操作时,数据从文件流向计算机内存,在输出操作时,数据从计算机流向文件。
C的数据文件由一连串的字符(或字节)组成,不考虑界限,两行数据不会自动加分隔符,对文件的存取是以字符(或字节)为单位的。输入输出数据流的开始和结束仅受程序控制而不受物理符号控制(回车换行符),这种文件称为流式文件。
文件名
一个文件要有一个唯一标识符,以便识别和引用。文件标识包括:文件路经、文件名主干、文件后缀
文件分类
根据数据的组织形式,数据文件分为ASCII文件和二进制文件。数据再内存中以二进制形式存储,不转换直接输出到外存就是二进制文件(可以说他是存储再内存上数据的映象,又叫映像文件)。如果在外存上以ASCII形式存储,则要在存储前进行转换,又叫文本文件(每一个字节存放一个字符的ASCII代码)。
字符一律按照ASCII形式存储,数值型数据既可以用ASCII形式存储也可以用二进制形式存储。用ASCII码形式输出时字节与字符一一对应,一个字节代表一个字符(便于处理和输出字符,但占存储空间多且需要转换时间);用二进制形式输出数值,可以节省外存空间和转换时间,将内存中存储单元的内容原封不动的输出到磁盘上,每个字节并不一定代表一个字符。
文件缓冲区
ANSI C标准采用“缓冲文件系统”处理数据文件,缓冲文件系统就是系统自动在内存区中为程序每一个正在使用的文件开辟一个文件缓冲区。从内存向磁盘输出数据必须先送到文件缓冲区,装满缓冲区后才一起送到磁盘去。如果是从磁盘向计算机读入数据,则是一次性从磁盘文件将一批数据输入到内存缓冲区,然后从缓冲区逐个将数据送到程序区(给程序变量)。这样是为了节省存取时间,提高效率,缓冲区大小由C编译系统确定。每个文件在内存中只能有一个缓冲区,在向文件输出数据时,它就是输出缓冲区;再从文件输入数据时,它就是输入缓冲区。
文件类型指针
每个被使用的文件都在内存中开辟一个相应的文件信息区,存放文件相关信息(文件名、文件状态、文件当前位置...)。这些信息保存在一个结构体变量中,此结构体类型是由系统声明的,名字是FILE。
定义:FILE *p; 定义一个指向文件类型的指针变量。通过文件指针变量可以找到其指向的文件。文件指针变量指向的是内存中文件信息区的开头。
打开与关闭文件
“打开”是为文件建立相应的信息区和文件缓冲区,“关闭”就是撤销文件信息去和文件缓冲区。
fopen打开数据文件
p=fopen("文件名",“使用方式”); 用指定方式打开文件,函数返回文件信息区开头地址,将其赋值给文件指针变量。再打开一个文件时会通知系统:需要打开的文件名、使用文件的方式、让哪一个指针变量指向被打开的文件。
文件使用方式:
r 打开一个已存在的文件(并存有数据),此方式只能从文件读出数据。
w 只能向文件写入数据,文件不存在就创建一个新的,已存在会将其删除重新创建一个新的。
a 打开一个已存在的文件,向其追加数据。
r+ w+ a+打开的文件既可以读又可以写(r 已存在,w新建,a追加)
rb wb ab ab+ wb+ ab+就是打开二进制文件,其实加不加b的区别就是对换行的处理,使用w遇到换行(\n)时系统将其转换为'\r'和'\n'两个字符,否则在Windows中查看文件时各行会连成一片,用r遇到'\r'和'\n'时系统将其转换为'\n'字符。如果使用的是二进制文件就不需要进行这种转换。 使用打开方式并不会在输入输出时将内存中ASCII码形式数据自动转化成二进制形式存储,输入输出的格式是由读写语句决定的。
一般使用此方式打开文件:
FILE *p;
if((p=fopen("filename","r"))==NULL)
{
printf("cannot open this file\n");
exit(0);
}
exit作用是使程序终止,此函数在<stdlib.h>
fopen失败返回NULL
程序中可使用标准流文件:标准输入流stdin(从终端的输入)、标准输出流stdout(向终端的输出)、标准出错输出流stder(出错时将错误信息发送到终端)。程序运行时自动打开这三个标准流文件。
fclose关闭文件
在使用完一个文件后应立马关闭。如果不关闭文件就结束程序运行将丢失数据(文件缓冲区的文件将丢失)。 函数执行成功返回0,失败返回EOF(-1)
fclose(文件指针变量);
顺序读写数据文件
顺序读写时先写入的数据存放在文件中前面,然后顺序存放。
fgetc(文件指针变量); 从指定文件中读取一个字符。成功带回字符,失败EOF。
fputc(字符,文件指针变量); 将指定字符写入指定文件,成功返回字符,失败EOF。
在文件所有有效字符后有一个文件尾标志,读完全部字符后,文件读写位置标记就指向最后一个字符后面,即文件尾标志。(继续读取会读出-1,但是这只是处理方式,文件尾标志不是-1)。可以用feof函数检测文件尾标志是否被读取,读取函数返回1,反之0。
fgets(字符数组,长度,文件指针变量); 从指定文件中读取一个指定长度减1的字符串存放到字符数组中。成功带字符数组地址,失败NULL。
fputs(字符串,文件指针变量); 将指定字符串写入指定文件,成功返回0,失败非0。
函数原型: char *fgets(char *str,int n,FILE *fp);
如果在读完指定长度减1的字符前遇见'\n'和EOF,读入结束,但是'\n’作为字符读入。(如果一开始就遇见文件尾标志或读数据出错返回NULL)
函数原型:char *fputs(char *str,FILE *fp);
第一个参数可以是字符串常量、字符数组名、字符指针。字符末尾'\0'不输出。
格式化方式读写文本文件
fprintf(文件指针,"格式字符串",输出列表); 将指定输出数据,输入到文件。
fscanf(文件指针,"格式字符串",输入列表(地址)); 从文件中读取指定数据
此方式读写需要进行二进制与ASCII码转换,要花费时间多。
二进制方式向文件读写
fread(数组,字节大小,读取个数,文件指针); 从指定文件读入指定个数个指定大小的数据存入数组。 返回值为读取个数。
fwrite(数组,字节大小,读取个数,文件指针); 向指定数组读出指定个数个指定大小的数据存入指定文件。 返回值为读取个数。
随机读写
C语言为每个文件设置了一个文件读写位置标记用来指定"接下来要读写的下一个字符位置",顺序读写时,文件位置标记指向文件开头,这是读文件就会读第一个字符,然后顺序读出(没读写一次,文件位置标记后移一个位置)。 可以根据需要进行顺序读写,也可以进行随机读写。随机读写就是读完上一个字符后并不一定要读写其后续的字符,而是可以读写任意位置上所需要的字符。
rewind(文件指针); 使文件位置标记指向文件开头,无返回值。(使用此函数后feof返回值会恢复成0)
fseek(文件指针,位移量,起始点); 改变文件位置,起始点(0代表文件开头(SEEK_SET),1为当前位置(SEEK_CUR),2为文件末尾(SEEK_END)),位移量是以起始点向前移动字节数。位移量使long型数据(数字末尾加L). fseek(p,100L,0); fseek(p,-20L,0);
ftell(文件指针); 函数返回值为文件位置标记当前位置。
文件读写出错检查
ferror(文件指针); 未出错返回值为0,出错返回非0。(对同一文件每次调用输入输出函数都会产生一个新的ferror数值,需要立即检查否则信息会丢失。执行fopen时ferror初值自动为0)
clearerr(文件指针); 使文件出错标志和文件结束标志位置为0。文件出错时ferror为非0,此时使用clearerr使ferror为0才能进行下一次检测。只要出现文件读写出错标志,他一直保留,直到下一次对同一文件进行clearerr或rewind,或任意输入输出函数。
位运算符
& 按位与,将两个数据的二进制按位进行比较,都为1,这个二进制位为1,否则为0.
| 按位或,将两个数据的二进制按位进行比较,有一个为1,这个二进制位为1,否则为0.
^ 按位异或,将两个数据的二进制按位进行比较,相反,这个二进制位为1,否则为0.
~ 取反,对一个二进制数按位取反(结果为一个数的二进制全部取反即1变0,0变1)。
<< 左位移运算,a=a<<2。将a的二进制位向左移动2个二进制位,高位溢出舍弃,低位补0。
>> 右位移运算,a=a>>2。将a的二进制位向右移动2个二进制位,高位补0(负数补的数取决于计算机系统,补0叫逻辑右移,补1叫算数右移),低位溢出舍弃。
位段
可以人为的将一个字节分成多个位段(以bit为单位)
声明: 类型名 成员名:宽度;
一个位段必须存储在统一存储单元,不能跨两个单元
可以定义无名位段
位段的长度不能大于存储单元的长度,也不能定义为段数组
位段中的数可以以%d %u %o %x等格式输出
位段可以在数值表达式中引用,它会被自动转换为整型数
预处理指令
#define 标识符 字符串
预编译会将程序中标识符替换为字符串。 #undef 标识符 可以结束宏定义作用范围
#define 名(参数表) 表达式
此定义类似函数,但是宏定义表达式中 宏名 每个都需要用()括起来。
条件编译
#ifdef 标识符
程序段
#else
程序段
#endif
防止重复宏定义。