文章目录
第1章 程序设计和C语言
- 程序:是一组计算机能识别和执行的指令。
计算机的本质:程序的机器。 - 计算机语言发展历史
机器语言(二进制指令)->汇编语言(符号代替二进制)->高级语言(源程序经过编译转换为目标程序执行)。 - C语言特点:
- 用途广泛,功能强大,使用灵活的面向过程的编程语言。
- C语言是一个很小的内核语言,不直接提供输入输出语句,文件操作语句等,这些操作由编译系统提供的库函数来实现。
- 语言限制不严格,书写灵活,例如对数组下标越界不进行检查。
- C语言允许直接访问物理地址,能进行位操作,可直接对硬件进行操作。
- C语言程序结构
- 一个程序由一个或多个源程序文件组成。
- 一个源程序文件包括3个部分:预处理指令,全局声明,函数定义。函数是C程序的主要组成部分
- 运行C程序的步骤
对源文件.c进行编译前,先对程序中的预处理指令进行预处理,(将头文件中的内容读取进来,取代预处理指令那一行)由预处理结果与其他程序组成一个完整的源程序进行编译(检查语法错误,无误后把源程序转化为二进制形式的目标程序.obj),一个源文件编译后对应一个目标文件,将一个程序中所有的目标文件和库函数连接起来后,生成一个可执行程序.exe - 软件是计算机程序和程序文档的总称
第2章 算法
- 程序=算法+数据结构
数据结构:对数据的描述;
算法:对操作的描述; - 传统流程图 弊端
- 三种基本结构
- N-S流程图
第3章 顺序程序设计
- 数据的两种表现形式:常量和变量。
- 区分输出
"\\"
和"//"
的结果。前者输出一个\ ,后者输出//。 - 符号常量:用#define指令,指定用一个符号名称代表一个常量。
#define PI 3.14
预处理时将所有的PI 替换为3.14。符号常量不占内存,只是一个临时符号 ,预编译后就不存在了,不能对符号常量赋新值。 - 变量:是一个有名字的,具有特定属性的存储单元,变量名实际上是以一个名字 代表的一个存储单元。变量必须先定义后使用。
- 常变量:定义变量时,前面加一个const关键字,表示变量存在期间其值不能改变。
- 区分常变量和符号常量:
eg:常变量 const double pi=3.14; 符号变量 #define PI 3.14
#define 指令是预编译指令,仅预编译时进行一个简单的字符串替换,预编译后符号常量PI就不存在了,对符号常量是不分配存储单元的;
而常变量要占用存储单元,有变量值,只是该值不能被改变。 - 标识符:用来对变量,符号常量,函数,数组,类型等命名的有效字符序列。规定标识符只能用字母数字下划线组成,且一个个字符不能为数字。标识符会区分大小写。
- 整型数据存储方式:整数的补码形式(整数的补码与原码相同,负数的补码是将其绝对值的二进制形式按位取反加一)。
- int的取值范围 -(2 ^31 ) ~ ( 2 ^31)-1;unsigned int取值范围0~(2 ^32)-1。一般默认有符号
- 只有整型(含字符型)数据可加signed或unsigned修饰符,浮点型不能加。
- 无符号整型数据用 %u 输出
- 字符型数据存储方式:按其ASCII码形式存储。
区分字符’1’和整数1:字符’1’按其对应ASCII码49存储,占1个字节;整数1按其补码形式存储,占4个字节。 - 浮点型数据存储方式:以指数形式存储;
- 运算符和表达式
- 算术运算符中,%要求参加的运算对象是整数 ,其他不要求(整型,浮点,枚举均可)。
- 不同类型间混合运算,会自动类型转换,小->大。当自动类型转换不能实现目的时,可使用强制类型转换,不过可能丢失精度。
- 数据的输入输出:
scanf("%lf%f",&a,&b);
%lf表示输入的是double型数据,%f表示输入的是float型数据。变量前面要用 &。- 输入数值时,数据间需要用空格,tab键或回车间隔开,表示一个数值已输入完毕。但是在输入字符时,即输入字符格式为%c时,输入的数据应是连续的,不能空格,否则会将空格也作为一个字符输入。
printf("%7.2f\n",x);
%7.2f表示输出数据x的数据长度占7列,其中小数占2列,且右对齐,如果数据长度超出7列,则按原来的数据长度输出。 %f既可输出float类型,也可输出double型
- 格式字符还有%d,%c(可以将整数按此格式输出,如果整数过大时,只输出最后一个字节),%s…
- 输入数值时,数据间需要用空格,tab键或回车间隔开,表示一个数值已输入完毕。但是在输入字符时,即输入字符格式为%c时,输入的数据应是连续的,不能空格,否则会将空格也作为一个字符输入。
- 字符输入输出函数
- putchar© 输出自变量c的值,c可以是字符常量,字符变量,整型常量,整型变量(整数不能超过0~127);也可输出转义字符。eg:
putchar(‘\'’);输出一个'
- getchar() 输入一个字符,用getchar函数得到的字符可以赋给一个字符变量或整型变量。
putchar(getchar()); 将输入得到的数据输出
- putchar© 输出自变量c的值,c可以是字符常量,字符变量,整型常量,整型变量(整数不能超过0~127);也可输出转义字符。eg:
第4章 选择结构程序设计
-
两种选择语句
- if语句 注意if else语句大括号匹配正确
- switch语句
- switch(表达式); 表达式的值应为整数(包括字符型);
- 可以没有default语句,如果没有符合条件的情况时,就跳到switch语句的下一条语句;default语句可以不必写break。
- case和default的书写顺序随便。
- 每一个case常量必须互不相同,否则互相矛盾。
- case标号只起标记的作用,没有条件检查判断的作用,如果没有break语句的话,则执行完某一条case语句后,会顺序执行下面的case语句,直到遇到break。
- 可多个case标号共用一条语句,表示多种情况下执行同一条指令。
-
算术运算符:+ - * / %
关系运算符:< > <= >= == !=
逻辑运算符: && || ! 逻辑表达式的结果为“真(1)”“假(0)”,非零数值表示为“真”
优先级顺序:!> 算术运算符 > 关系运算符 > &&,|| > 赋值运算符
第5章 循环结构程序设计
- 一般情况下,3种循环(while,do …while,for循环)可以互相代替。
- 不等价情况:
当循环体内有continue时,for循环执行到continue后,后面的语句不执行,表达式3仍会执行;for(表达式1;表达式2;表达式3) 语句 转换为while形式: 表达式1; while(表达式2) { 语句 表达式3 }
while循环执行到continue时,后面的语句,包括表达式3均不执行。 - for循环特点
- 表达式1可省略,但要使程序正常运行,需要在循环前初始化循环变量
- 表达式2可省略,无限循环
- 表达式3可省略,需要在循环体内使变量自增或自减
- 表达式1和3可以是几个简单的表达式,中间用逗号隔开
- 表达式2可以是关系表达式,也可逻辑表达式,也可数值表达式或字符表达式,只要其值非0,就执行循环体
- break语句:跳出当前循环体,只能用在3个循环语句和switch语句中。
- continue语句:跳出本次循环
第6章 数组
一维数组
- 定义:int a[4]; int b[3+1]; 错误示例:int n=4; int c[n];
- 初始化
- 定义时对全部元素赋初值:
int a[4]={1,2,3,4};
此时[ ]中的数组长度可省略 - 定义时对部分元素赋值:
int a[4]={1,2};
未赋值的部分自动赋值为0(字符型数组,初始化为’\0’,指针型数组,初始化为NULL),int a[4]={};
表示对全部元素赋值为0;
- 定义时对全部元素赋初值:
二维数组
- 定义:
int a[2][3];
(二维数组几行几列是逻辑上的概念,内存中各元素是连续存放的,是线性的)。 - 初始化:
- 分行赋值
int a[2][3]={{1,2,3},{4,5,6}};
- 按内存排列顺序赋值:
int a[2][3]={1,2,3,4,5,6};
- 对部分元素赋值:
int a[2][3]={{1},{4}};
只对每一行第一个元素赋值;也可int a[2][3]={{1,3},{4}};
- 如果对全部元素赋初值,定义数组的第一维的长度可以不指定,第2维长度不能省略;如果只对部分元素赋初值,但又省略了第1维的长度,应该分行赋值。
- 分行赋值
字符数组
C语言中没有字符串类型,也没有字符串变量,字符串是存放在字符型数组中的。
- 初始化:
char c[10]={'I','a','w','c'};
未被初始化的元素 自动设为空字符(’\0’);数组长度可省略char c[ ]={"afaajoifj"};
实际字符串长度为有效长度+1,因为用字符串对字符数组初始化时,会自动加一个’\0’。也可把大括号去掉,效果一样。
- C语言将字符串作为字符数组处理,C语言规定了字符串结束标志’\0’(‘\0’代表ASCII码为0的字符,是一个空操作符)
- 字符数组的输入输出:逐个字符输出用"%c",整个字符串输出"%s"。
- 用scanf输入一个字符串时,scanf会自动在字符串后面加一个’\0’。
- 处理字符串的函数:
- puts(字符数组名); 将字符串输出到终端,puts输出的字符串中可以包括转义字符,同printf;用puts输出时,将结束标志’\0’转换为’\n’,即输出完字符后换行。参数也可为字符串
- gets(字符数组名); 从终端输入一个字符串到字符数组,并得到一个函数值,该函数值是字符数组的起始地址。参数也可为字符串
- puts和gets函数只能输出或输入一个字符串,即只能有一个参数
- strcat(字符数组1,字符数组2); 连接字符串:把字符数组2接到字符数组1的后面(字符数组1的长度必须足够长),返回字符数组1的地址。参数2可为字符串,但参数1不行
- strcpy(字符数组1,字符串2); 将字符串2复制到字符数组1中,连同’\0’也一起过去同strcat,参数1必须是字符数组名,参数2可字符串也可字符数组
- strncpy(字符数组1,字符数组2,n); 将参数2的前n个字符复制到参数1中去。
- strcmp(字符串1,字符串2); 比较字符串(将两个字符串从左到右逐个字符按照ASCII码大小相比较,直到出现不同的字符或遇到’\0’,若相同返回0,若参数1>参数2,返回一个正整数,若<,返回一个负整数)。
- strlen(字符数组名); 测字符串长度,不包括’\0’在内
- strlwr(字符数组名); 转化为小写的 ;strupr(字符数组名); 转化为大写
- 不要忘记 #include<string.h>
第7章 函数
- 函数调用时,实参和形参之间可自动类型转换。
- 形参在函数被调用时才被临时分配内存空间,调用结束,形参被释放
- 执行被调用函数时,形参的值发生改变,并不能影响实参的值,因为他们之间是值传递,值传递是单向传递,他们在内存中占用的是不同的存储单元
- 当被调函数写在main函数之后时,须在main函数中先写函数声明,才可以调用,函数声明的参数个数和参数类型必须与被调函数一致,参数名可不写或写成不一样的。
函数的递归调用
- 求n!
int fac(int n)
{
int f;
if(n<0)
printf("n<0,data error");
else if(n==0||n==1) //0和1的阶乘是1
f=1;
else f=fac(n-1)*n;
return f;
}
- 汉诺塔问题
hanoi(m,'A','B','C');
void hanoi(int n,char one ,char two,char three) //将n个盘从one座移到three座,借助two座
{
void move(char x,char y);
if(n==1)
move(one,three);
else
{
hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
void move(char x,char y)
{
printf("%c->%c\n",x,y);
}
数组元素做函数参数
- 不能做形参, 因为形参是在函数被调用时,临时分配存储单元的,而数组是一个整体,不可能为数组元素单独再分配空间。
- 数组元素做形参是值传递,数组名做形参传的是数组首元素地址
- 数组名做形参时,数组大小没有任何意义,可接受任意长度的数组,形参数组和实参数组指的是同一个地址
- 编译时,系统把形参数组
float array[]
转换为指针变量float * array
。
变量的作用域:局部变量和全局变量
不同函数中可以定义同名的变量,因为他们在内存中占不同的存储单元,不会混淆
变量的生存期
- 对静态局部变量是编译时赋初值,所以每次调用时都保留了上次函数调用时的值。
- 在定义局部变量时不赋初值的话,对于静态局部变量,自动赋值为0,或’\0’;对于自动变量,值是不可知的。
- 静态局部变量在生存期同全局变量一样,但作用域同局部变量一样。
- 外部变量声明 extern extern可扩展变量的作用域,也可用于扩展函数的作用域,扩展函数作用域时,可不写extern关键字,即可直接声明。
- static声明局部变量和全局变量的区别:static修饰局部变量是将局部变量的生存期扩大到整个程序,存储在静态存储区中;static修饰全局变量时,是将全局变量的作用域限制在了本文件中,其他文件不可通过extern来扩展。
- 区分声明和定义:需要建立存储空间的称为定义 ,int a; 不需要建立存储空间的称为声明,extern a; 外部变量的定义只有一次(在所有函数之外),但可对同一外部变量多次声明(函数内外都可)。
- 内部外部函数;根据函数能否被其他源文件调用划分。内部函数又叫静态函数,前面用static修饰。
- extern的使用所有函数前面都默认有一个extern(可省略不写),表示可被其他文件引用,若不想的话,加static;在其他文件中引用这个函数时,需要在函数声明前面加一个extern(也可省略)。所以在本文件还是其他文件中引用函数时都一样,声明即可。
第8章 指针
- 指针即地址,C语言中地址包括位置信息和所指向数据的类型信息,是“带类型的地址”。
- 数据的直接访问和间接访问:
printf("%d\n",i);
- 直接访问:从变量名与地址对应表中,根据变量名找到地址,再按照其类型读出i,再按十进制格式输出
- 间接访问:将变量i的地址存放在另一变量中,然后根据该变量找到i的地址,从而访问i。
&i_pointer=&i;
此时 &pointer就是一个指针变量,专门用来存放地址。
- 表示指针地址:指向整型数据的指针类型表示为int *,简称int指针
通过指针引用数组
- 指针指向一个数组元素时,允许对指针进行加减运算:+,-,+=,-=,p++,p–,p1-p2(只有p1和p2均指向同一数组时有意义。)
- p+1是指向该数组中下一个元素,不是将p的值+1,而是加上一个数组元素所占用的字节数
- 引用数组中各元素的值3种方法:
- 下标法,如a[3]
- 通过数组名引用,如*(a+3) a的值是不会变的,a是指针型常量
- 用指针变量指向数组元素,如 int * p=a; *(p+3)
- 如果给指针变量p加下标,如p[2],代表的是p指向的当前值的下两个元素
- 区分 p++; *p; 和 *p++ :前者是先将p自增指向下一个元素,再引用该元素;后者是先引用当前元素,即先执行*p,再使p++,注意不是引用的元素自增,而是使指针变量p自增
- 用数组名做函数参数:实参数组名代表该数组首元素的地址,所以形参数组应定义为指针变量,用来接收地址;形参数组各元素值发生变化,实参也随之改变。但实参数组名是指针常量,形参数组名是指针变量。
- a[i]与*(a+i)无条件等价
- 要想在函数调用中修改数组中元素的值,数组名与指针变量都既可做形参,也可做实参。如果用指针变量做实参,则必须先将指针变量指向一个已定义的对象。
通过指针引用多维数组
- 二维数组指针在不同位置上的含义
- a+i和&a[i]等价,指向行,基类型是一维数组;a[i]和*(a+i)等价,指向列,基类型是元素类型
- 区别 int * p[4]和 int (* p)[4]: 前者表示定义了一个基类型为int的指针数组,长度为4,p是数组名;后者表示定义了一个指针变量p指向长度为4的int型的一维数组。
- 实参与形参如果是指针类型,应当注意他们的基类型必须一致
通过指针引用字符串
- 引用字符串方法:
- 将字符串存放在字符数组中,可通过 %s 和 %c分别输出字符串或其中某个字符
- 用字符指针变量指向一个字符串常量(该字符串常量依然是放在一个字符数组中的,只不过没有名字,只能通过指针来访问)
- 用指针变量指向字符串时,可以先定义,再赋值,还可对指针变量进行再次赋值,但是不可对指针指向的字符串常量进行修改;字符数组则不行,因为字符数组名是常量,不可修改,字符数组中的某一元素可被修改。
程序改进
要求:指针变量from指向字符数组a,指针变量to指向字符数组b,将a的内容复制到b中。程序设计如下:
void copyString(char * from,char * to)
{
for(;*from!='\0';from++,to++)
{
*to=*from;
}
*to='\0';
}
改进一:将赋值语句与循环条件合并
void copyString(char * from,char * to)
{
while((*to=*from)!='\0') //先赋值后判断,所以不需要再补充*to='\0';了
{
from++;to++;
}
}
改进二:将自增语句再与他们合并
void copyString(char * from,char * to)
{
while((*to++=*from++)!='\0') ; //where后面不需要大括号了,直接分号
}
改进三:字符'\0'可用其对应ASCII码0来代替,所以*from!='\0' 等价于 *from!=0 等价于 *from (非零数值表示为真)
void copyString(char * from,char * to)
{
while(*to++=*from++') ;
}
也可用for循环:for(;*to++=*from++;);
指向函数的指针
- 函数名代表函数的起始地址,定义一个函数 int max(int a,int b);
- 定义指向函数的指针变量 int (*p) (int ,int ); 将指针变量指向函数:p=max; 调用函数c=(*p)(a,b); 等价于 c=max(a,b);
- 指向函数的指针变量的一个重要用途:将函数入口地址作为参数传递到其他函数中,这样就可以在其他函数中调用该函数了。当其他函数内部每次需要调用不同的函数时,用这种方法就比较方便
- 区别:返回值为指针的函数 :int * max(int a,int b); 返回值为 int *类型的max函数
…
- 指针数组
int * p[4];
数组p是指针类型,每一个元素都是指针,指向一个int型的数据 - 指向一维数组的指针变量
int (*p) [4];
指针变量p指向int型的长度为4的一维数组 - 指向指针数据的指针变量
int * * p;
p是一个指针变量,这个指针变量又指向一个int型的数据
动态内存分配与指向它的指针变量
- 对内存的动态分配通过系统提供的库函数来实现,有malloc,calloc,free,realloc4个。
- 开辟动态存储区
- void * malloc(unsigned int size); 在动态存储区分配一个长度为size的连续空间;unsigned int表示无符号整型,不能为负数。
p=malloc(100);
- void * calloc(unsigned n,unsigned size); 分配n个长度为size的连续空间
- void * malloc(unsigned int size); 在动态存储区分配一个长度为size的连续空间;unsigned int表示无符号整型,不能为负数。
- 重新分配动态存储区。
p=calloc(4,100);
- void * realloc(void * p,unsigned int size); 已经通过malloc或者calloc分配动态存储区后,需要改变其大小,用realloc重新分配。在原来的空间上修改其长度,空间的首地址不变。
realloc(p,50);
- void * realloc(void * p,unsigned int size); 已经通过malloc或者calloc分配动态存储区后,需要改变其大小,用realloc重新分配。在原来的空间上修改其长度,空间的首地址不变。
- 释放动态存储区
- void free(void * p); 释放指针p所指向的动态空间。
free(p);
- void free(void * p); 释放指针p所指向的动态空间。
- 这4个函数声明在stdlib.h头文件中。