一、C语言概述
1、C语言发展历程
2、C语言程序设计方法
(1)分析问题,明确功能需求
(2)设计解决问题的方案
(3)编程实现
(4)程序的测试与维护
3、C语言基本框架
(1)程序注释
(2)预处理指令和函数声明
(3)主函数
(4)子函数
4、上机编写C程序
集成开发环境是用于程序开发与调试的软件包。
编写和执行C语言过程
(1) 上机编写C语言源程序
(2)通过编译形成二进制形式的目标程序
(3)通过连接形成二进制形式的可执行程序
(4)执行程序并查看结果
二、数据
1、内存分配
地址由低到高分别为:代码段、数据段、BBS段、堆、堆栈
(1)BBS段
用于存储未初始化的全局变量和静态变量
(2)数据段
用于存储已初始化的全局变量和静态变量
(3)代码段
用于存储可执行程序的代码,通常是只读形式。
(4)堆
动态分配,由程序员分配和释放,系统不自动回收。申请后必须释放,防止溢出造成内存泄露。
(5)栈
用于存储局部变量、形参和返回值
静态分配,由程序编译执行后自动分配和释放,不持久。
2、数据的分类
(1)基本数据类型
数值型、字符型
(2)构造数据类型
即复合数据类型,一般不整体使用,而是用成员变量
数组通过具体名字和下标访问,结构通过名称和成员运算符访问
(3)指针类型
(4)空类型
3、变量和常量
定义常量的两种方式:
(1)使用预处理#define
(2)使用const关键字
变量与常量的区别:
(1)变量通过命名的存储空间存储,常量通过匿名的存储空间存储
(2)变量不是固定值,可以根据程序需要随时修改变量的值,在程序执行的不同时期,一个变量保存的值可能不同;常量是固定值,在程序执行的期间不会改变。
4、数据的格式化输入和输出
主要使用函数scanf和printf进行输入输出
具体语法为:
printf("格式控制符",实参1,实参2,实参3......)
scanf("格式控制符",参数地址1,参数地址2,参数地址3......)
格式控制符与参数列表应满足:
(1)数量相同
(2)类型匹配
(3)顺序一致
printf后参数列表通常为变量名或具体数值,scanf后参数列表为变量地址
5、数据的使用
使用数据主要需要注意定义、存储、值域和操作域、格式控制符几方面
(1)整型
整型 int %d 4字节32位
(2)浮点型
单精度浮点型 float %f 4字节32位
双精度浮点型 double %f/%lf 8字节64位
系统默认按照double型进行存储,通过在浮点型常量后加f可强制要求系统按照float型进行存储。
可以通过%(-)m.nf实现对小数点输出的控制。“-”左对齐,m数据输出最大宽度,n小数部分的位数。
(3)字符型
字符型 char %c(字符串%s) 1字节8位
计算机在储存字符时,将字符数据对应的ASCⅡ码以二进制形式存储到存储单元中(存储单元只能存储二进制信息)。
ps:'A'=65,'a'=97,'1'=49,大小写字母ASCⅡ码差32
(4)指针型
通过指针变量来保存内存的地址值,简称指针。通过变量名称访问是直接访问,通过地址(指针)访问是间接访问。
char ch;//定义了一个char型变量ch
char *pCh;//定义了一个char型指针变量pCh
pCh=&ch;//将char型变量ch的地址赋值给pCh,使pCh指向ch
*pCh='a';通过指针pCh间接访问ch,并将其赋值为a
注意:
①指针是有类型的,每个指针只能保存该类型的变量的地址
②当一个指针保存了某个变量的地址,则这个指针指向了这个变量
③对于int *p,p是指针的名字 ,星号*表示p为指针类型的变量
(5)符号常量
在C语言中,用标识符来表示常量(即用符号代替具体数值),称之为符号常量。
符号常量在使用前需要定义,一般形式为:#define 标识符 常量
6、数据类型转换
(1)隐式类型转换
程序运算过程中自动转换类型,一般是把一个值域较小的操作数的类型转换为另一个操作数的类型
(2)显式类型转换
即强制类型转换,形式是在被转换表达式前写一对括号,括号内写出转换的目标类型。
如(int)(2.1+3.5);
类型转换并不改变原有变量的值,而是产生一个新值。
三、程序设计初步
1、算法的概念和常见算法举例
算法是完成一个任务的具体步骤和方法。
算法具有:有限性、明确性、输入项、输出项、可行性。
算法的表示方式有:自然语言、流程图、伪代码。
主要算法有:递推和递归、穷举法、分治法、迭代法
2、程序设计的三种基本结构
顺序结构、选择结构、循环结构
3、结构化程序设计
核心是采用“自顶向下、逐步求精”和“分而治之”的方法进行程序设计。
四、选择结构
1、条件的表达
1真0假
&&(与)、||(或)、!(非)
2、使用语句实现选择结构if
(1)单选择方案
if(条件表达句)
{
语句1;
语句2;
......
}
(2)双选择方案i
if(条件表达句)
{
语句1;
.....
}
else
{
语句2;
......
}
(3)多选择方案
if(条件表达句)
{
语句1;
.....
}
else if(条件表达句)
{
语句2;
.....
}
else
{
语句3;
......
}
(4)嵌套的if语句
if(条件表达句)
{
语句1;
if(条件表达句)
{
语句2;
.....
}
}
3、选择结构的其他表达方式
(1)switch结构
适用于某些特定形式的多分支条件判断,尤其是根据单个变量(或表达式)的值进行判断的情况。
switch结构只能处理条件判断结果可以转换成int型常量集合的情况。
switch(控制表达式)
{
case c_1:
语句1;
break;
/* if(控制表达式=c_1)
语句1;*/
case c_2:
语句2;
break;
case c_3:
语句3;
break;
......
default:
语句n;
break;
}
控制表达式必须是int型或char型,不能是浮点型。case关键字后的数值必须是常量表达式。
(2)条件运算符
<条件表达式>?<结果表达式1>:<结果表达式2>
当条件表达式成立时返回结果表达式1,不成立时返回结果表达式2
五、循环结构
构成循环的三要素是循环条件、循环变量和循环体
1、基本循环类型
(1)计数循环
适用for循环
(2)无限循环
适用while(1)或for(;;)
(3)有限循环
①当型循环
适用while循环
②直到型循环
适用do-while循环
③中途退出型循环
使用无限循环+break或调整结构使用while
2、三种循环结构描述语句
(1)for循环
for(循环变量初始化;循环条件表达式;更新循环变量)
{
循环体;
}
for(;;)
{
循环体;
}//无限循环
(2)while循环
while(条件控制表达式)
{
循环体;
循环条件更新;
}
while(1)
{
循环体;
}//无限循环
(3)do-while循环
do
{
循环体;
}while(循环条件表达式)
注意:for循环和while循环在每次重复操作前都要先判断条件是否成立,再确定是否执行循环体;
do-while循环先执行循环体,再判断条件是否成立。
3、break和continue语句
break用于跳出当前循环,continue用于跳过本次循环直接进入下一次循环
int day;
for(day=1;day<7;day++)
{
if(day==4)
continue;
其他语句块;
}
day从1到3时执行其他语句块,day=4时跳过本次循环,执行day++,day=5
int day;
for(day=1;day<7;day++)
{
if(day==4)
break;
其他语句块;
}
语句1;
day从1到3时执行其他语句块,day=4时跳出循环,执行语句1
六、函数
1、程序的函数分解和两种函数
程序的函数分解是指对长程序按照功能用函数机制进行分装数据模块
函数分为:
标准库函数(由系统提供)
自定义函数(自己编写)
2、函数声明
result-type name(argument-specifies)
result-type:返回值类型
name:函数名
argument-specifies:一组由逗号分开的参数类型说明列表
被声明的东西必须在其他地方已经定义,否则声明是无效的。
3、写函数
(1)编写函数定义的一般步骤
①根据问题描述找出问题的输入输出
②根据输入输出的特点和函数定义原则确定函数头
③设计问题解决方案
④编写函数体代码
(2)函数七大定义原则
函数定义原则1:通常情况下,问题的输入固定对应到参数列表中的输入型形参。
函数定义原则2:如果问题的输出结果为单一数值,适合通过返回值将问题的输出返回给调用程序。
函数定义原则3:如果问题的输出结果为屏幕显示而无数值型结果返回,适合将返回值设置为空类型。
函数定义原则4:如果问题的输出结果为多个数值,适合通过返回型形参将问题的输出返回给调用程序。
函数定义原则5:如果需要将问题的输入修改并返回,适合通过指针型参数来进行数据传输。
函数定义原则6:数组型数据无论做输入还是输出都只能使用数组型形参,在发生函数调用时传入待处理数组的名称作为实参,借助地址型参数间接访问实现数据共享。
函数定义原则7:结构型数组支持整体赋值,可以通过形参直接输入,通过形参或返回值直接输出,但占内存较大时建议使用结构指针完成参数传递。
4、函数调用过程
(1) 参数的值传递
(2)进入子函数的函数体内完整的执行函数体
(3)通过return或输出型形参返回主函数
5、函数中的变量
(1)变量的作用域和生存期
①作用域
作用域是变量定义的作用范围,即在哪段代码可以使用这个变量名去访问对应的存储空间,对应于源程序的一段代码。作用域由变量定义的位置确定。
②生存期
生存期指变量在程序执行过程中存在的那段时间,即程序执行过程中这块内存与这个变量对应的那段时间。变量的生存期与其存储性质有关。
(2)局部变量和全局变量
局部变量是在函数体内部定义的变量(包括形参),作用域为函数内部。
全局变量是在函数体外部定义的变量,直接在程序中定义而非在函数(包括main函数)中定义的变量。
(3)静态变量
静态变量包括全局静态变量和局部静态变量,在定义时使用static修饰。
static关键字不改变变量的作用域,但当函数调用结束时,静态局部变量不会被删除,对应的存储空间会一直有效。
6、主函数与子函数
主函数(main())是程序的入口,在程序执行时,主函数中的代码会被首先执行。主函数主要作用是接受用户的输入、调用其他函数和输出结果。
子函数是除主函数外的其他函数,被设计用来完成特定的操作或任务。可以用来完成主函数中复杂、重复的功能,使主函数保持简洁,提高主函数可读性。
主函数与子函数的区别
①数据来源:主函数通常接收用户输入和全局变量作为数据来源;子函数通过参数传递和全局变量获取数据。
②返回值:主函数一般返回一个整数值,通常用于表示程序退出状态。0为正常结束,非零为异常终止;子函数可以返回任何数据类型,可根据需要进行定义。
③调用方式:主函数在程序开始时自动开始执行;子函数需要在主函数或其他函数中进行显式调用。如果一个子函数没有被调用,则它的代码不会被执行。
④执行过程:主程序从程序开始处按顺序执行;子函数的执行取决于被调用的次数与位置,可能与主函数交叉执行。
⑤返回值:主函数一般返回一个整数值,通常用于表示程序退出状态。0为正常结束,非零为异常终止;子函数可以返回任何数据类型,可根据需要进行定义。
⑥目的:主函数提供与用户交互的接口,控制程序总体流程;子函数使程序模块化,提高程序的可重用性和可维护性。
七、数组
1、数组的定义与使用
(1)数组的概念
数组属于构造数据,有三个显著特征:类型相同、存储连续、长度固定。
(2)数组的定义
例:int a[3]
a为数组名称,3为数组长度。数组名称代表数组首地址。数组长度可以用整型常量和符号常量,但不能使用变量来定义数组大小。
(3)数组的初始化
①在定义数组的同时把各元素的初始数值表达式顺序列在一对花括号内,表达式之间用逗号分隔
②如在定时数组时没有进行初始化,则后续可以用赋值语句完成对数组中元素的赋值,但只能对单一数组元素进行操作。适合用for循环实现。
2、字符数组和字符串
(1)字符数组和字符串的区别与联系
字符数组是以字符为元素的数组。字符串用字符数组的方式储存。
空字符“\0”是字符串的结束标记。普通字符数组和字符串的区别在于数组顺序存放完所需字符后是否存放一个空字符\0,有空字符就是字符串。
(2)字符串
定义字符串的两种方式:
①在初始化时直接定义
②通过strcpy进行赋值
(3)字符串的输入输出
①通过printf和scanf实现字符串的整体输入和输出
char a[10],b[7]="abc";
scanf("%s",a);//a等价于&a[0]
printf("%s",a);//输出数组a中的字符串
printf("%s",&b[1]);//输出数组b中从b[1]开始的字符串
注意:
1)a是数组的名字,也是数组的首地址。在scanf中代表数组变量的首地址,在printf中代表数组名。
2)用%s进行输出时,在遇到第一个\0时停止输出
3)使用scanf时,空格、回车、跳格会被识别为输入结束的标志
②用gets()和puts()函数输入输出字符串
gets()和puts()函数保存在库函数stdio.h中
调用形式:gets(sadr)、puts(sadr)
sadr为首地址,如gets(a)
(4)用标准函数库处理字符串
计算字符长度strlen(str1)
复制字符串strcpy(str2,str1)
连接字符串strcat(str2,str1)
比较字符串strcmp(str2,str1)
3、数组与函数
函数头:result-type name(元素类型 数组名称[ ],int n)
或result-type name(元素类型 *数组名称,int n)
4、多维数组
(1)二维数组的定义
二维数组有两个维度,通常第一个维度叫行,第二个维度叫列,构成第一位数组的每个元素又是一个一维数组。
(2)二维数组的初始化
支持在定义数组的同时进行初始化,如:
int a[2][3]={{1,2,3},{4,5,6}};
如果在定义二维数组的同时对其进行初始化,则二维数组行的维度可以忽略
int a[ ][3]={{1,2,3},{4,5,6}};
八、结构
1、结构类型定义和变量声明
遵守变量先定义后使用的原则 ,连续存储。
有两种定义方式:①直接定义②通过typedef给定义的结构取别名
struct 结构类型名称
{
成员列表;
};
typedef struct
{
成员列表;
} 结构类型名称;
2、结构变量初始化和使用
1)若在定义的同时进行初始化,可整体赋值
2)通过圆点运算符访问结构变量成员,进行赋值
3、结构变量的储存
采用连续的存储空间依次存储所有成员变量,正常情况下结构变量所占内存长度应是其所有成员所占存储空间的总和。
字节对齐
在统计以结构变量总存储空间时,计算机以结构变量中的最小变量的存储空间为单位进行寻址,结果为最小变量存储空间的倍数。
为处理字节对齐问题,C语言提供了两个宏:#pragma pack(n)和#pragma pack(),用来强制计算机以设置的字节长度寻址。
4、结构类型数组
结构里可包含数组成员。以结构类型为元素的数组称为结构类型数组
typedef struct
{
char name[20];
int count;
}STUDENT;//定义包含数组成员的结构体STUDENT,注意花括号外也有分号
STUDENT list[N];//STUDENT类型的数组list
5、结构指针
指向结构的指针称为结构指针,结构指针的声明方式与其他指针相同。
struct_student stu={"zhangsan","001"};
struct_student *pStu;
pStu=&stu;
printf("The name is %s\n",stu.name};
printf("The name is %s\n",(*pStu).name};
通过专用运算符“->”将间接访问(*)和成员访问 (.)两个操作结合在一起,即pStu->name等价于(*pStu).name
printf("The name is %s\n",(*pStu).name};
printf("The name is %s\n",pStu->name};
注意,圆点运算符(.)优先级高于间接访问运算符(*),“->”优先级高于++和-- 。
6、结构与函数
因结构可进行整体使用,所以虽然为构造数据类型,但C语言处理结构类型的方式与普通类型更为相似,而与数组的处理方式大相径庭。子函数不能返回数组类型的结果,只能通过间接访问使子函数与主函数共享调用程序传入的实参数组。而结构看可以像简单类型的变量一样直接将子函数中定义的结构变量函数调用的结果返回(通过结构类型的返回值或返回型形参返回)。
九、指针
1、指针的四种用途
①通过指针实现程序不同部分之间的数据共享
②通过指针引用存储空间较大的数据,避免重复申请存储空间
③通过指针在程序执行的过程中根据需要临时申请内存空间,实现动态分配
④通过指针记录数据项之间的联系,实现链式结构
2、通过指针实现函数间数据共享
根据C语言函数调用的值传递原理,发生函数调用时,子程序的形参调用的是调用程序传入的实参变量的副本。因此在子函数中对形参做出的任何修改都是在实参的副本上进行的,对实参无任何影响。
为解决此问题,我们给函数传递指向实参的指针,使形参获得程序中变量的地址副本,进而在子函数体内通过这一地址副本间接访问其指向的变量,实现子函数与调用函数之间的数据共享。
3、指针与数组
数组可以看做同类型的普通变量,只要类型匹配,就可以让指针指向数组元素。
如果指针p指向数组arr的第1个元素arr[0],且存在合理的整型数据j(不会造成访问越界),则p+j=p[j]
p=&a[0];
*(p+j)=a[j];
因数组名亦为数组元素的首地址,故p1=&a[0]与p1=a等价。
我们将指针加法描述为向下标变大的方向移动(向右移动), 将指针减法描述为向下标变小的方向移动(向左移动)
如果指针指向了数组,则可以通过间接访问,用指针变量名代替数组名的位置
pn=&a[i];
pn[j]=&a[i+j];//n为常数
4、C语言动态存储管理机制
当数据存储空间(如数组长度)无法静态确定时,可以进行动态空间分配。动态空间分配是在程序执行过程中,根据需要向动态存储管理系统申请任意大小的存储块。注意:使用过后必须进行释放,否则将产生幽灵空间。
C语言的动态存储管理通过一组标准库函数实现,其原型在标准库文件<stdilb.h>中描述。
分配:void *malloc(size_t n);
分配一块不小于n的存储空间,并返回该存储空间的起始地址。分配失败时返回空指针NULL。
释放:void free(void*p)
释放指针p指向的存储空间,如果p为空指针,free就什么都不需要做。
free()和malloc()成对配合使用。
int *p;
p=(int*)malloc(n*sizeof(double));
//通过强制转换为int型以符合需要(不必须),使用sizeof能够保证获得适合当前数据类型进行存储的空间
if *p==NULL
{
printf("malloc failed!\n");
return -1;
}//判断是否申请成功,若申请失败,结束程序
for(i=0;i<N;i++)
{
scanf("%d",p+i);
}//将数据存入对应的存储空间
free(p);//释放p所指的存储空间
5、链式结构初步
使用自引用的结构类型,通过在单个数据项中保存其后续结点地址的方式相互连接。
这种类型的结构包含两部分:①实际需要保存的数据
②指向同类结构的指针(后续结点的地址)
通过指针链接,由多个自引用结构类型的数据项形成线性链式结构(链表),将整个链表的最后一个结点的指针设为空指针,表示链表的结束。用一个指针指向链表的第一个结点,则这个指针为表头指针。
typedef struct student
{
char name[20];
int count;
struct student *pNext;
}NODE;//定义为NODE类型结构