算法
算法的基本概念
算法的特征
- 有穷性
一个算法必须在执行又穷步之后结束,并且每一步都得以在有穷时间内完成,而不是无限制地执行下去。
- 确定性
算法的每一个步骤都有明确的定义,并且具有相对应的意义,对于每个过程不能有二义性,将要执行的每个动作必须严格而清楚的规定
- 可行性
算法中的每一个步骤都应当能有效地运行,算法是可行的,并且要求最终得到正确的结果
- 输入
一个算法应有零个或多个输入
- 输出
一个算法应有一个或多个输出
算法的优劣
- 正确性
对任何合法地输入,算法都会得出正确的结果
- 可读性
如果算法比较抽象,难于理解,那么这个算法不易交流和推广,对于修改、扩展、维护都十分不便,所以在编写算法的时候应该做到算法写的简单易懂
- 健壮性
当输入的数据非法时,算法可以做出相应的判断,而不会因为输入的错误造成瘫痪
- 时间复杂度
算法运行所需要的时间,如何写出更高速的算法一直是算法不断改进的目标
- 空间复杂度
算法运行所需要的存储空间
描述算法
三种基本结构
- 顺序结构
是一种线性结构,从上往下执行
- 选择结构
选择结构也叫分支结构,选择结构中必须包含一个判断框
- 循环结构
循环结构就是程序反复执行一系列的操作,知道条件不成立才停止循环
流程图
- 起止框
用来标识算法的开始和结束
- 输入输出框
输入输出框用来标识程序的输入或输出常量、变量或表达式
- 判断框
对一个给定的条件进行判断,根据给定的条件是否成立来决定如何执行后面的操作
- 处理框
处理框里大多是表达式,常用来处理或比较式子
- 流程线
连接图框用的,是流程图中不可缺少的组成部分
N-S流程图
N-S流程图是另一种算法表示法,其依据是:既然任何算法都是由前面的三种循环结构组成,所以各基本结构之间的流程线
那就是多余的,因此去掉所有的流程先,将全部的算法写在一个矩形框内
- 顺序结构
- 选择结构
-
循环结构
-
当型循环
- 直到型循环
伪代码
伪代码就是介于自然语言和机器语言之间用文字和符号来描述算法的
常量和变量
了解数据类型
-
基本类型
基本类型包括***整形***、字符型、实型(浮点型) -
结构类型
使用基本类型的数据,或者使用已经构造好的数据类型,进行添加、设计构造出新的数据类型,使其设计的新构造类型满足待解决问题所需要的数据类型 -
指针类型
C语言的精华-指针,指针的值表示的是某个内存地址 -
空类型
空类型的关键字是***void***,主要作用俩点:
- 对函数返回的限定
- 对函数参数的限定
常量
整形常量和实型常量可以统称为数值型常量
-
数值型常量
- 整形常量
- 实型常量
- 字符型常量
- 字符串常量
- 符号常量
整形常量
整形常量可以是长整型、短整型、符号整形和无符号整型
- 无符号短整型的取值范围是***0 ~ 65536***,符号短整型的取值范围是-32768 ~ +32767。都是16位整形常量的范围
- 无符号长整型的取值范围是***0 ~ 4294967295***,符号长整型的取值范围是-2147483648 ~ +2147483647。都是32位整形常量的范围
-
八进制数
在赋值的时候常数前面必须有一个数字***0*** -
十六进制数
在赋值的时候常数前必须有***0x***或***0X***,其中字母***A ~ F***大小写都可以 -
十进制数
在赋值的时候常数前不用有任何前缀
实型常量
实型常量也成为浮点型常量或者实行常数,由整数部分和小数部分组成
-
指数方式
当实型非常大或非常小的时候可以用指数方式来标识。45e2就是4500,e2表示10的2次方,45e-2就是0.45,e-2表示10的-2次方
字符型常量
- 字符常量
- 字符常量只能包括一个字符,不是字符串
- 字符常量区分大小写
- 使用的’'代表界定符,不属于字符常量的一部分
- 字符常量可以是任何字符,但数字被定义成字符型后不能进行运算
-
字符串常量
字符串常量用一组双引号括起来的诺干字符序列,其中双引号是界定符不属于字符串常量的一部分。
字符串“Hello”在内存中的存储形式如下:
H | e | l | l | o | \ 0 |
---|
转义字符
转义字符 | 解释 | ASCII码 |
---|---|---|
\ a | 鸣笛(BEL) | 7 |
\ b | 退格(BS),将当前位置移到前一列 | 8 |
\ f | 换页(FF),将当前位置移到下页开头 | 12 |
\ n | 换行(LF),将当前位置移到下一行开头 | 10 |
\ r | 回车(CR),将当行位置移到本行开头 | 13 |
\ t | 水平制表(HT),跳到下一个Tab位置 | 9 |
\ v | 垂直制表(VT),竖向跳格 | 11 |
\ ’ | 一个单引号字符 | 39 |
\ " | 一个双引号字符 | 34 |
\ \ | 一个反斜杠字符“" | 92 |
\ ? | 一个问号字符 | 63 |
\ 0 | 一个空字符(NULL) | 0 |
\ ddd | 任意字符 | 1~3位八进制数 |
\ xdd | 任意字符 | 1~2位十六进制数 |
符号常量
用一个符号名代替固定的常量值,符号名字就是符号常量。在使用符号常量前要先定义
大多情况下符号常量用大写英文字母,定义格式如下:
#define 符号常量名 常量值
符号常量的使用(求圆的面积):
#include "stdio.h"
#define PAI 3.14
int main(){
double r,s=0;
printf("输入圆的半径:");
scanf("%f",&r);
s=r*r*PAI;
printf("圆的面积是:%f",s);
return 0;
}
变量
变量就是在程序运行的过程中,值可以被改变的量。每个变量都是一种类型,每一种类型都定义了变量的格式和行为
每个变量都有自己的名字,并且在内存占有存储空间,变量的大小取决于类型
定义变量的一般形式如下:
类型说明符 变量名;
例如:
int a;
double b;
char c;
整形变量
整形变量中基础类型使用***int***关键字,可以在其基础上加一些符号进行修饰,如***short***或***long***。
类型名称 | 关键字 | 范围 |
---|---|---|
有符号基本整形 | [signed] int | -32768 ~ +32767 |
无符号基本整形 | unsigned [int] | 0 ~ 65535 |
有符号短整型 | [signed] short [int] | -32768 ~ +32767 |
无符号短整形 | unsigned short [int] | 0 ~ 65535 |
有符号长整型 | [signed] long [int] | -2147483648 ~ +2147483647 |
无符号长整型 | unsigned long [int] | 0 ~ 4294967295 |
-
有符号基本整形
有符号基本整形是***signed int***型,编写时常常省略***signed***
定义一个有符号整形变量的方法是用关键字***int***定义
有符号基本整形的变量使用例子:
#include "stdio.h:
int main(){
int a,b,c;
a=10,b=20;
c=a+b;
printf("a+b=%d",c);
return 0;
}
-
无符号基本整形
无符号基本整形是***unsigned int***型,编写是常常省略***int***
定义一个无符号基本整形变量的方法是用关键字***unsigned***定义
无符号整形变量使用例子:
unsigned i;
i=10;
-
有符号短整形
有符号短整形是***signed short int***型,编写时常常省略***signed***和***int***
定义一个有符号短整形变量的方法是用关键字***short***定义
有符号短整形变量使用例子:
short i;
i=10;
-
无符号短整形
无符号短整形是***unsigned short int***型,编写是常常省略***int***
定义一个无符号短整形的使用方法是用关键字***unsigned short***定义
无符号短整形变量使用例子:
unsigned short i;
i=10;
-
有符号长整型
有符号长整型是***long int***型,编写时常常省略***int***
定义一个有符号长整型的使用方法是用关键字***long***定义
有符号长整型变量使用例子:
long i;
i=10;
-
无符号长整型
无符号长整型是***unsigned long int***型,编写时常常省略***int***
定义一个无符号长整型的使用方法是用关键字***unsigned long***定义
无符号长整型变量使用例子:
unsigned long i;
i=10;
实型变量
实型变量也叫浮点型变量,用来存储实型数值的变量,实型数值由整数和小数俩部分组成。
实型变量分为单精度类型、双精度类型和长双精度类型,一般编程时只用到单精度和双精度
-
单精度类型
关键字是***float*** -
双精度类型
关键字是***double***
字符型变量
字符型变量是用来存储字符常量的变量,实际上是将该字符的ASCII码值存储到内存单元中,一个字符串变量里只能存放一个字符
字符串变量的关键字是char
表达式与运算符
赋值运算符和赋值表达式
在C语言中赋值符号是”=“,赋值表达式的作用是将一个数据赋给一个变量,属于***双目运算符***
变量赋初值
一般形式为:类型 变量名 = 常数;
也可以将一个表达式的值赋给一个变量,一般形式为:类型 变量名 = 表达式;
强制类型转换
强制类型转换的一般形式为:***(类型名) (表达式)***
强制转换代码如下:
float i=10.123;
int j=(int)i;
在C语言程序中,如果一个高级别的变量向低级别的变量进行强制类型转换,可能会出现***数据丢失***,在使用强制转换时要注意这个问题
算术运算符和表达式
C语言中由2个单目运算符、5个双目运算符和一个三目运算符
- 单目运算符是只需要一个操作数的运算符
- 双目运算符是需要两个操作数的运算符,即运算符俩边都要有一个操作数
- 三目运算符是需要三个操作数的运算符
算术运算符
符号 | 名称 | 功能 |
---|---|---|
+ | 正值运算符 | 单目正 |
- | 负值运算符 | 单目负 |
* | 乘法运算符 | 乘法 |
/ | 除法运算符 | 除法 |
% | 求余运算符 | 取模 |
+ | 加法运算符 | 加法 |
- | 减法运算符 | 减法 |
“-“作为减法运算符的时候是双目运算符,”-“作为负值运算符的时候是单目运算符
算术表达式
在表达式中如果使用了算数运算符,那这个表达式就称为算数表达式,例子如下:
num=(3+5)/r;
hei=top-bot+1;
are=hei*wid;
优先级和结合性
算数运算符中,*、/、%的优先级大于+、-
自增、自减运算符
自增运算符是:++
自减运算符是:–
自增自减运算符可以放在变量的前面或后面,使用方法如下:
++i; /*先自增i,在使用i*/
i++; /*先使用i,再自增i*/
--i: /*先自减i,在使用i*/
i--; /*先使用i,再自减i*/
需要注意的一点是:自增自减运算符只能用于变量不能用于常量
关系运算符和表达式
再C语言中关系运算符可以说是比较运算符,一个比较两个操作数大小的符号
关系运算符
符号 | 功能 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
关系表达式
关系运算符是对两个表达式的值惊醒比较,并返回一个真值或假值
- 真值:不为0的值
- 假值:0
优先级和结合性
关系运算符的结合性都是从左到右的
如果要先对变量赋值再进行比较的话,由于关系运算符的优先级大于赋值运算符,所以要用括号把赋值表达式括起来
逻辑运算符
符号 | 功能 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 单目逻辑非 |
位逻辑运算符
符号 | 功能 |
---|---|
& | 逻辑与 |
| | 逻辑或 |
^ | 单目逻辑非 |
~ | 取补 |
输入输出函数
语句解释
表达式语句
在表达式后面加一个分号,就是表达式语句
一般形式为:表达式;
空语句
空语句就是只有一个分号的语句,在C语言中时空云这样的,空语句就是不会执行任何操作的语句
复合语句
符合语句是可以不带分号的,用一对大括号把很多条语句括起来形成的语句
一般形式如下:
{
语句1
语句2
语句3
......
语句n
}
输入输出字符
输出字符数据
输出字符数据用***putcher***函数,作用是输出一个字符
输入字符数据
输入字符数据用***getchar***函数,作用是输入一个字符
输入输出字符串
输出字符串
输出字符串使用***puts***函数,作用是输出一个字符串
输入字符串
输入字符串使用***gets***函数,作用是输入一个字符串
格式输出函数
格式输出函数是输出若干任意类型的数据
一般格式为:printf(格式控制,输出列表)
格式控制中的有两种字符,一个是格式字符一个是普通字符
- 格式字符以**%**字符开头
普通字符就是就是要原样输出的字符
格式符号 | 功能说明 |
---|---|
d | 以带符号的十进制形式输出整数 |
O | 以八进制无符号形式输出整数 |
x | 以十六进制无符号形式输出整数 |
u | 以无符号十进制形式输出整数 |
c | 以字符形式输出,只输出一个字符 |
s | 输出字符串 |
f | 以实数形式输出 |
e | 以指数形式输出实型 |
格式输入函数
格式输入函数是将从终端输入的数据赋给地址列表的各个变量
一般格式为:scanf(格式控制,地址列表)
scanf的格式控制跟printf函数的格式控制相同,而地址列表中应该给用来接受数据的变量的地址
因为不知道变量的地址就在变量前加字符**&**表示来获取变量的地址
格式字符 | 功能说明 |
---|---|
d | 用来输入有符号的十进制整数 |
u | 用来输入无符号的十进制整数 |
o | 用来输入无符号的八进制整数 |
x | 用来输入无符号的十六进制整数 |
c | 用来输入单个字符 |
s | 用来输入字符串 |
f | 用来输入实型,可以用小数形式或者指数形式输入 |
程序设计
例一、计算园的面积
#include "stdio.h"
#define PAI 3.14
int main(){
double r,s=0;
printf("输入圆的半径:");
scanf("%f",&r);
s=r*r*PAI;
printf("圆的面积是:%f",s);
return 0;
}
例二、大写字母转小写字母
#include "stdio.h"
int main(){
char cB;
char cS;
puts("请输入一个大写字母:");
cB=getchar();
pus("转换为小写字母为:);
cS=cB+32;
printf("%c",cS);
return 0;
}
例三、交换两个变量的值
#include "stdio.h"
int main(){
int a,b,t;
printf("输入俩个值:);
scanf("%d,%d",&a,&b);
t=a;
a=b;
b=t;
printf("a=%d,b=%d",a,b);
return 0;
}
选择结构程序设计
if选择结构
编写C语言时,如果要判断某个条件并执行不同的语句就用到if语句
if语句的不同形式
单条件单分支if语句
if(条件){
条件为真执行的语句
}
只要括号内的条件为真就执行下面的语句
单条件双分支if语句
if(条件){
条件为真执行的语句
} else{
条件为假执行的语句
}
括号内的条件不管为真为假都可以执行语句
多条件多分支if语句
if(条件1){
条件1为真执行的语句
} else{
if(条件2){
条件2为真执行的语句
} else{
if(条件3){
条件3为真执行的语句
}
......
else{
if(条件n){
条件n为真执行的语句
}
}
}
}
只要括号内的条件为假就可以编写出一直判断下去的语句直到条件为真
if语句常见错误
分号使用错误
在C语言中,分号";“意味着一条语句的结尾,就像说话中的句号”。“,但是if语句中后面就不需要加分号,如果加了分号大概率会报错
条件设置不规范
在编写if语句的时候应该把,大概率判断为真的条件放在前面优先判断,概率越小越在后面进行判断
if和else的配对
在if语句嵌套中else总是和前面最近未配对的if组合
大括号
如果不想让else和前面为配对的if组合可以用大括号把if括起来
if(条件1){
if(条件2){
语句1
}
} else{
语句2
}
判断分支选择switch语句
switch语句可以在处理多分支问题时使用
基本形式
switch(表达式){
case 情况1:
语句1;
break;
case 情况2:
语句2;
break;
......
case 情况n:
语句n;
break;
default:
默认情况语句;
}
switch语句功能是,计算switch后括号内表达式的值,并逐个和case后面的常量表达式的值比较,当表达式的值和一个case后面常量表达式的值相等是,执行case后面的语句
如何case后面语句中没有break语句,则顺序执行下一个case语句,不进行比较知道遇到break语句或者switch语句执行完毕为止
switch语句是一个复合语句大括号一定要加上
循环语句
while型循环
while语句是用来实现”当型“循环的循环语句,”当型“循环就是先判断条件的真假再决定是否执行循环中的语句,即判断条件为真时(值不是0)时,执行循环体里面的语句,直到不满足条件体里面的语句时不再执行循环体里的语句
while循环的一般形式
while语句的一般形式是:
while(表达式){
语句
}
避免while循环语句中的死循环
在使用while语句中,要先分析程序设计循环体,要考虑循环体第一次能否执行还要让循环体不断地执行,直到程序停止,否则成勋会进入死循环
例:
#include "stdio.h"
int main(){
while(1){
printf("Hello!");
}
}
while循环语句的空循环体
while循环语句是用while循环条件和循环体组成的,如果循环体只是单行语句,那就是从while开始到第一个分号结束,如果while后面的循环体只有一个分号,那就说明单行语句为空,这样的循环体就叫空循环体
while循环语句的常见错误
- 循环体内有多个语句没有用大括号括起来,导致只执行一个语句
- while表达式后面多写了一个分号,不出现编译错误但是会不停执行空操作,无法执行循环体的语句
for循环语句
for循环语句不到那可以用于循环次数不确定的情况还可以用于循环次数确定的情况,只给出循环的结束条件,for循环语句是C语言中最常见的循环语句
一般形式
for语句的一般形式为:for(表达式1;表达式2;表达式3)
for语句的简答应用形式为:for(循环变量赋初值;循环条件;循环变量){循环体}
for语句中三个表达式和循环体的作用:
- 表达式1:主要用于循环变量的初始化,一般是一个赋值表达式,用来控制循环的变量称为循环变量
- 表达式2:主要作用是进行判断,条件为真的时候执行循环体,为假则跳出循环体,每执行一次循环体后都会进行一次判断,来决定是否再次执行循环体
- 表达式3:主要用于来改变循环变量的值,让循环变量接近结束条配件,每执行一次循环体都会执行这个表达式,对循环变量进行修改
- 循环体:循环体可以是一条语句也可以是多条语句,一条语句可以不用大括号但多条语句一定要用大括号
使用例子:
for(int i=1;i<10;i++){
printf("Hello!");
}
用while语句来表示for语句
表达式1;
while(表达式2){
语句
表达式3;
}
for语句的省略
- 省略表达式1
for(;i<10;i++)
表达式1可以省略但是表达式1后的分号不能省略,当表达式1省略时,一般都需要在for循环语句前进行变量的赋值
- 省略表达式2
如果表达式2省略,循环就会无终止的执行下去,也就是默认表达式2的值为真
如果省略了表达式2,想要终止循环可以用***break***语句 - 省略表达式3
表达式3省略了就会无终止的执行下去,需要用其他的方法来结束循环,比如用***break***语句 - 表达式1中可以是与循环变量无关的表达式
for循环语句中的逗号
可以在for循环语句中的表达式1中用逗号表达式,为多个变量赋初值
do-while循环语句
do-while的一般形式为:
do{
循环体
}while(表达式);
do-while语句的功能是:先执行一遍循环体的内容再进行判断表达式的值。如果表达式的值为真则就继续执行,为假则跳出循环
do-while语句是一个“直到型”循环,但是do-while语句是直到表达式的值为假时跳出循环
while和do-while的区别:while语句在执行前判断条件,do-while语句先执行一次循环体再判断条件
三种循环的比较
- while和do-while循环,只在while后面指定循环条件,在循环体中包含趋于结束的语句(如i++,i+=1等)
- for循环可以在表达式3中包含趋于结束的操作,也可以把循环体中的操作全部放在循环体3中
- while循环、do-while循环和for循环都可以在循环体中用break语句跳出循环,用continue语句结束本次循环
- while语句和do-while语句的循环变量的初始化应该在语句之前完成,for语句在表达式1中完成
循环嵌套
在一个循环的循环体内包含一个完整的循环结构就是循环嵌套
各种结构的嵌套
while循环、do-while循环和for循环之间可以互相嵌套,例如:
- while循环中嵌套while循环
while(表达式){
语句
while(表达式){
语句
}
}
- do-while循环中嵌套while循环
do{
语句
while(表达式){
语句
}
} while(表达式);
- do-while循环中嵌套do-while循环
do{
语句
do{
语句
}while(表达式);
} while(表达式);
- do-while循环中嵌套for循环
do{
语句
for(表达式1;表达式2;表达式3){
语句
}
}while(表达式);
- for循环中嵌套while语句
for(表达式1;表达式2;表达式3){
语句
while(表达式){
语句
}
}
- for循环中嵌套for循环
for(表达式1;表达式2;表达式3){
语句
for(表达式1;表达式2;表达式3){
语句
}
}
但是各循环必须完整,不允许互相交叉
循环嵌套实例
例一、输出金字塔
#include "stdio.h"
int main(){
for(int i=1;i<=5;i++){
for(int j=1;j<=5-i;j++){
prntf(" ");
}
for(int j=1;j<=2*i-1;j++){
printf("*");
}
printf("\n");
}
}
转移语句
goto语句
goto语句为无条件转向语句,让程序可以立即跳转到函数内部的任意一条可执行语句
goto关键字后面带一个分号,该语句标识是同一个函数内后跳语句的标识
标识可以出现在任何可执行语句前面,并以一个冒号":"做后缀
除了while语句、do-while语句和for语句外,goto语句也可以实现循环
- 例子:求出数值内所有正整数的和
#include "stdio.h"
int main(){
int n,i,sum;
sum=0;
i=1;
printf("请输入一个整数");
scanf("%d",&n);
xh: if(i<=n){
sum+=i;
i++;
goto xh;
}
printf("计算%d内所有正整数的和为%d",n,sum);
return 0;
}
break语句
break语句的作用是终止并跳出循环,继续执行后面的代码
break语句的一般形式是:break;
break语句不能用于循环语句和switch语句之外的任何语句中
在循环语句中使用break语句例子:
while(1){
printf("Break");
break;
}
continue语句
continue语句的作用是在不跳出循环的前提下回到循环开头继续执行
continue语句的一般形式是:continue;
continue语句和break语句的区别是:continue语句只结束本次循环,break语句是结束整个循环
在循环语句中使用continue语句例子:
#include "stdio.h"
int main(){
int iC;
for(iC=0;iC<10;iC++){
if(iC==5){
printf("iC here\n");
continue;
}
printf("the C is:%d\n",iC);
}
return 0;
}
三种跳转语句的区别
- break语句只能在switch语句和循环语句中,作用是跳出switch语句或跳出本层循环,执行后面的程序
- continue语句只能用在循环体中,作用是结束本次循环,不再执行continue语句后面的语句,转到下一次循环条件的判断和执行
- goto语句也称为无条件转移语句
数组
数组概念
数组是类型相同、数目固定的若干个标量的有限集合
数组元素是组成数组的基本单元,必须是具有相同的数据类型,并且“数组名”和“下标”是唯一的
- 数组名是用户定义的数组标识符
- 下标是数组各个元素之间的相对位置
数组的分类
- 按照元素类型分类
数组元素是组成数组的基本单元,必须具有相同的数据类型
如果数组中有一个是整数那整个数组中的变量都是整数
按照数组元素类型分类可以把数组分为:整形数组、实型数组、字符型数组、指针数组等 - 按照下标个数分类
按照下标个数分类可以分为:一维数组、二维数组和多维数组
数组的维数
一维、二维、多维的维数都是按照数组元素的下标个数进行区分的
使用数组的时候,如果只有一个下标,则称为一维数组,一维数组标识一种线性数据的组合
二维数组有两个下标,可以看作是平面数据的组合
C语言对数组下标的个数没有限制
一维数组
一维数组指的是数组元素只有一个下标的组数
一维数组的定义
C语言变量中有一条规定:“先定义,后使用”,数组也要先定义后使用
一维数组的一般形式如下:
类型说明符 数组标识符[常量表达式];
- 类型说明符
标识数组中所有的元素类型,可以是任意一种简单类型、构造类型或指针 - 数组标识符
这个数组型变量的名称也叫数组名,命名规则与变量一致 - 中括号
数组的标志,不能缺也不能换成其他符号 - 常量表达式
中括号内的常量表达式必须为常量或符号常量,一般情况下不能为变量
常量表达式定义了数组中存放的元素个数,即数组长度
如果数组长度为5,那下标从0开始到4结束
一维数组的引用
引用数组的时候应该注意下标的范围,其范围是0 ~ (常量表达式-1),在C语言中数组元素不能被整体引用只能逐个引用
标识数组元素的一般形式如下:
数组标识符[下标]
一维数组的初始化
在定义数组后,就可以对数组进行初始化,初始化方法如下:
- 在定义数组的时候直接对数组元素进行赋值
例如:
int i,iS[6]={1,2,3,4,5,6};
- 只给一部分元素赋值,其未赋值的部分元素值为0
例如:
int iS[6]={1,2,3};
- 对全部数组元素赋初值,不指定数组长度
例如:
int iS[]={1,2,3,4};
一维数组的应用
例:使用数组保存学生资料
#include "stdio.h"
int main(){
char Stu[5];
int Age[5]={11,12,11,12,13};
int i;
Stu[0]="Wang";
Stu[1]="Zhang";
Stu[2]="Li";
Stu[3]="Su";
Stu[4]="Liu";
for(i=0;i<5;i++){
printf("%s\n",Stu[i]);
printf("%d\n",Age[i]);
}
return 0;
}
二维数组
二维数组的定义和一维数组大致相同,只是比一维数组多了一个常量表达式
二维数组的一般形式:
数据类型 数组名[常量表达式1][常量表达式2];
- 常量表达式1被称为行下标
- 常量表达式2被称为列下标
如有一个二维数组S[m][n],则二维数组下标取值如下: - 行下标取值范围0 ~ m-1
- 列下标取值范围0 ~ n-1
- 二维数组最大下标元素是S[m-1][n-1]
二维数组的引用
二维数组元素的一般形式是:
数组名[下标][下标];
下标可以是整型变量或者整形表达式
二维数组的初始化
在给二维数组赋值的时候有以下几种情况:
- 将所有数据卸载一个大括号内,按照数组元素排列顺序对元素赋值
例如:
int S[2][2]={1,2,3,4};
- 在为所有元素赋初值时,可以省略行下标但不能省略列下标
例如:
int S[][3]={1,2,3,4,5,6};
- 可以分行给数组赋值
例如:
int a[2][3]={{1,2,3},{4,5,6}};
- 二维数组也可以直接对数组元素赋值
例如:
int a[2][3];
a[0][0]=1;
q[0][1]=2;
二维数组的应用
例子:任意输入一个3行3列的二维数组,求对角线上的元素之和
#include "stdio.h"
int main(){
int a[3][3];
int i,j,sum=0;
printf("请输入\n");
for(i=0;i<3;i++){
for(j=0;j<3;j++){
scanf("%d",&a[i][j];
}
}
for(i=0;i<3;i++){
for(j=0;j<3;j++){
if(i==j){
sum+=a[i][j];
}
}
}
printf("和为:%d",sum);
return0;
}
多维数组
多维数组的定义和二维数组相同,只是下标更多,一般形式如下:
数据类型 数组名[常量表达式1][常量表达式2]…[常量表达式n];
字符数组
当数组中的元素类型为字符型时,称为字符数组
字符数组中的每一个元素都可以存放一个字符
字符数组的定义
字符数组的定义与其他数据类型的数组定义类似,一般形式如下:
char 数组名[常量表达式];
因为是定义字符数组,所以数组名前面的类型是char
字符数组的引用
字符数组的引用和其他数据类型一样,也是使用下标的形式
字符数组的初始化
- 逐个字符赋给数组中的各元素
char ch[3]={'a','b','c'};
- 在定义数组的时候初始化,可以省略数组长度
char ch[]={'a','b','c'};
- 利用字符串给数组赋值
char ch[]={"abc"};
char ch[]="abc";
字符数组的结束标志
字符数组的结束符是**’\n’**
字符数组的输出
-
使用格式符"%c"进行输出
for(i=0;i<5;i++){ printf("%c",cS[i]); }
-
使用格式符"%s"进行输出
char cS[]="Hello!"; printf("%s",cS);
使用格式符"%s"将字符串输出,需注意几种情况:
- 输出字符不包括结束符’\0’
- 用"%s"格式输出字符串时,printf函数中输出项是字符数组名,不是数组中的元素名
- 如果数组长度大于字符串实际长度,也输出到’\0’为止
- 如果一个字符数组内有多的结束字符,则在第一个结束字符时轻质输出
字符数组的输入
-
使用格式符"%c"进行输入
使用格式符"%c",将字符逐个输入
for(i=0;i<10;i++){ scanf("%c",&c[i]); }
-
使用格式符"%s"进行输入
使用格式符"%s",将字符串依次输入
char c[9]; scanf("%s",&c);
字符串处理函数
在使用字符串处理函数时,要用编译预处理命令 #include***将头文件"string.h"***包含进来
字符串的复制
strcpy是字符串的复制函数,作用是复制特定长度的字符串到另一个字符串中
strcpy函数一般形式为:
strcpy(目的字符数组名,源字符数组名)
将源字符数组的字符串复制到目的字符数组中,字符串结束符’\0’也会一起复制
例子:
#include "stdio.h"
#include "string.h"
int main(){
char s1[20],s2[20];
printf("请输入目的字符串:\n");
gets(s1);
printf("请输入源字符串:\n");
gets(s2);
printf("输出目的字符串:%s\n",s1);
printf("输出源字符串:%s\n",s2);
strcpy(s1,s2);
printf("复制后的目的字符串:%s\n",s1);
return 0;
}
字符串的复制注意事项:
- 不能用赋值语句将一个字符串常量或者字符数组直接赋给一个字符数组
- 目的字符数组应该有足够的长度,否则不能完全装入复制到字符串,目的字符数组的长度一定不能比源字符数组短
- 目的字符数组必须写成数组名形式,而源字符数组可以是字符数组名也可以是一个字符串常量
字符串的连接
strcat是字符串的连接函数,作用是将一个字符串连接到另一个字符串的末尾
strcat函数的一般形式为:
strcat(目标字符数组名,源字符数组名)
strcat函数使用时,会把目标字符数组的结束字符删去,目标字符数组也要有足够的长度来避免连接源字符数组后新字符串产生的错误
字符串的比较
strcmp是字符串的比较函数,作用是比较两个字符串是否相同并返回结果
strcmp函数的一般形式为:
strcmp(字符数组名1,字符数组名2)
strcmp函数使用时,会对两个字符串的字符逐个比较,直到字符串的ASCII顺序不相同或遇到结束标识符时结束比较,并返回比较结果
返回值为:
- 字符串1=字符串2,返回值为0
- 字符串1>字符串2,返回值为正数
- 字符串1<字符串2,返回值为负数
字符串大小写转换
strupr是字符串小写字母转换成大写字母的函数
strupr函数的一般形式为:
strupr(字符串)
strlwr是字符串大写字母转换成小写字母的函数
strlwr函数的一般形式为:
strlwr(字符串)
两个函数在转换字母的时候其他字符都不变
获取字符串长度
strlen是获取字符串长度的函数,作用是计算字符串的实际长度并返回(不包含字符串结束字符)
strlen函数的一般形式为:
strlen(字符数组名)
函数
函数概念
函数是C语言的基本模块,是构成C程序的基本单元,函数中包含程序的可执行代码
C程序中所有的函数都是平行的,在定义函数时时相互独立的。在一个函数中,不能嵌套定义另一个函数,函数之间可以互相调用,但是main函数不能被调用
定义函数
函数包括用户自己定义和系统提供的标准函数两种,如果时系统提供的标准函数那么不需要定义就可以使用,如果是用户自定义的函数与则要先定义后使用
如果main函数前返回值类型为***int***,则main函数定义为:
int main(){
......
return 0;
}
如果main函数前返回值类型为***void***,则main函数定义为:
void main(){
......
}
函数定义的形式
一个函数的定义分为两个部分,函数头和函数体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWGYda59-1692877453648)(./img/img_11.png)]
定义代码如下:
int add(int num1,int num2){
int s;
s=num1+num2;
return s;
}
-
函数头
函数头分为一下三部分:
-
返回值类型
返回值可以是某个C语言的数据类型
-
函数名
也就是函数标识符,函数名在程序中是必须唯一的,因为是标识符所以也要遵守标识符命名规则
-
参数表
形式参数列表可以简称为形参,可以没有变量也可以有多个变量,在调用的时候把实际参数的值复制到这些变量中。
函数定义中没有形式参数叫无参函数,无参函数一般不需要写范围值类型
-
-
函数体
函数体在函数头的下方用一个大括号括起来,大括号决定了函数体的范围
函数体包括了局部变量的申明和函数的可执行代码,函数要实现特定的功能都是在函数体这部分通过代码语句完成的
最后通过return语句返回实现的结果
-
无参函数
无参函数语法形式如下:
返回值类型 函数名(){ 函数体 }
-
空函数
空函数语法形式如下:
类型说明符 函数名(){ }
定义与声明
在程序中编写函数时,一般会对函数进行声明,然后再对函数进行定义
这个不是绝对是的,有时可以不先进行声明直接定义
函数的声明是让编译器直到有这个函数的名称、参数、返回值类型等信息,函数的定义是让编译器直到函数的功能
函数声明的格式由函数返回值类型、函数名、参数列表和分号四部分组成
返回值类型 函数名(参数列表);
声明代码如下:
int add(int num);
实际代码例子如下:
#include "stdio.h"
void sn(int num);
int main(){
int Sn;
printf("你想显示什么数字\n");
scanf("%d",&Sn);
sn(Sn);
return 0;
}
void sn(int num){
printf("你想显示的数字是:%d\n",num);
}
如果将函数的定义放在了调用函数之前,那么就不需要惊醒函数的声明,此时函数的定义就包含了函数的声明
实际代码如下:
#include "stdio.h"
void sn(int num){
printf("你想显示的数字为:%d\n",num);
}
int main(){
int a;
printf("你想显示什么数字:\n");
scanf("%d",&a);
sn(a);
return 0;
}
返回语句
返回语句的主要用途为:
- 能立即从所在的函数中推书,返回到调用的程序中去
- 能返回值
在一个函数中,可以由多个返回语句,每个返回语句都有一个返回值,并且也只能由一条返回语句能够被执行
从函数返回
从函数返回时返回语句的第一个特主要用途,在程序中由两种方法可以终止函数的执行并返回到调用函数的位置
第一种方法就是在函数体中,从第一句一直执行到最后一句,当所有的语句都执行完,程序遇到结束符号"}"后返回
第二种方法就是采用函数返回语句return
return语句一般形式为:
return 表达式;
返回值
通常调用者希望通过调用其他函数得到一个确定的值,这就是函数的返回值
-
函数的返回值都是通过函数中return语句获得的,return语句将被调用函数中的一个确定值返回到调用函数中
-
函数的返回值类型应该在声明函数或定义函数的时候明确地指出函数得返回值类型
-
如果函数值得类型和return语句中表达式得值不一样,则以函数的含绘制为准
数值型数据,可以自动进行类型转换,即函数定义的返回值决定最终返回值的类型
传递函数参数
在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系,这就是函数参数
函数参数的作用就是传递数据给函数使用,函数利用接受的数据进行具体的操作处理
形式参数和实际参数
-
通过名称理解
-
形式参数
形式上存在的参数
-
实际参数
实际上存在的参数
-
-
通过作用理解
-
形式参数
在定义函数时,函数名后面括号中的变量名为形式参数
在调用函数之前,传递给函数的值将被复制到这些形式参数中
-
实际参数
在调用一个函数(真正使用一个函数)时,函数名后面括号中的参数为实际参数
函数的调用者提供给函数的参数叫做实际参数
-
通常形式参数简称为形参,实际参数简称为实参
数组作为函数参数
当数组作为函数的实参时,值传递数组的地址,而不是将整个数组赋值到函数中
当用数组名作为实参调用函数时,指向该数组的第一个元素的指针就被传递到函数中
在C语言中,没有任何下标的数组名,就是一个只想该数组第一个元素的指针
-
使用数组元素作为函数参数
由于实参可以时表达式形式,数组元素是表达式的组成部分,因此数组元素可以作为函数的实参,它与用变量作为函数的实参一样,是单向传递
使用数组元素作为函数参数注意事项:
- 用数组名做函数参数,应该在主调用函数和被调用函数中分别定义数组
- 实参数组与形参数组类型应该一样
- 形参数组也可以不指定大小,定义数组时,在数组名后面跟一个空的方括号
- 用数组名做函数实参时,不是把数组元素的值传递给形参,而是把实参数组的起始地址传递给形参数组
-
使用数组名作为函数参数
可以用数组名作为函数参数,此时实参和形参都使用数组名
使用数组名作为函数参数注意事项
- 对程序中将要使用的函数进行声明
-
使用指针作为函数参数
声明一个函数参数为指针时,传递数组方法如下:
void fun(int *p); int a[10]; fun(a);
调用函数
函数的调用有三种情况,分别为:
- 函数语句调用
- 函数表达式调用
- 函数参数调用
-
函数语句调用
把函数的调用作为一个语句就叫做函数语句调用,函数语句调用是最常使用的调用函数的方式
fun();
这个函数的功能就是在函数的内部显示一条消息
这时不要求函数带返回值,只要求完成一定的操作
-
函数表达式调用
函数出现在一个表达式中,这时要求函数带回一个确定的值,这个是参加表达式的运算
i=num*fun(3,5);
这个函数的功能就是,给fun函数两个值并返回一个确定的值进行运算
-
函数参数调用
函数调用作为一个参数的形参,将函数返回值作为实参传递到函数中进行使用
i=fun(10,fun(3,5));
函数出现在一个表达式中,要求函数带回一个确定的值,让这个值可以参加表达式的运算
嵌套调用
在C语言中,函数的定义都是互相平行的、独立的,也就是说,在定义函数时,一个函数体内不能包含定义的另一个函数
如果在程序中出现了嵌套定义,在编译的时候就会出现编译错误的提示
在C语言中可以进行嵌套调用,可以在一个函数体内调用另一个函数
递归调用
递归函数又称为子调用函数,C语言的函数都支持递归。也就是说每个函数都可以直接或间接调用自己
-
直接调用
函数直接调用自身函数
-
间接调用
在递归函数调用的下层函数中调用自己
灵活应用函数
函数的应用
在使用数学函数时,要为程序添加头文件:#include “math.h”
-
abs函数
abs函数的功能是求整数的绝对值,函数定义如下:
int abs(int i);
求一个负数的绝对值方法如下:
int a; int b=-12; a=abs(b);
-
labs函数
labs函数的功能是求长整数的绝对值,函数定义如下:
long labs(long n);
求一个长整型的绝对值的方法如下:
long a; long b=-1234567890; a=labs(b);
-
fabs函数
fabs函数的功能是求浮点数的绝对值,函数定义如下:
double fabs(double x);
求一个浮点数的绝对值方法如下:
double a; double b=-12.234; a=fabs(b);
-
sin函数
sin函数的功能是返回一个弧度值的正弦值,函数定义如下:
double sin(double x);
求一个弧度值得正弦值方法如下:
double a; double b=0.5; //b是一个角的弧度值 a=sin(b);
-
cos函数
cos函数的功能是返回一个弧度值的余弦值,函数定义如下:
double cos(couble x);
求一个弧度值的余弦值方法如下:
double a; double b=0.5; a=cos(b);
-
tan函数
tan函数的功能是返回一个弧度值的正切值,函数定义如下:
double tan(double x);
求一个弧度值的正切值方法如下:
double a; double b=0.5; a=tan(b);
指针
指针概念
地址与指针
在C语言中,存取变量值的方法有两种。
按变量地址存取变量值的方法称为“直接访问”
将变量地址存放到另一个变量中,先找到存放变量地址的另一个变量,通过另一个变量找到变量的地址,这种方法就称为“间接访问”
变量和指针
变量的地址是指针和变量的纽带,如果一个变量包含了另一个变量的地址,那么第一个变量可以说是指向第二个变量
“指向”是通过地址来体现的,在C语言程序中用符号“*”表示“指向”
因为指针变量是指向一个变量的地址,所以将一个变量的地址赋给指针变量后,可以说这个指针指向了这个变量
使用指针变量
在C语言中没专门用来存放内存单元地址的变量类型就是指针变量
-
定义指针变量
一般形式为:
类型说明 *变量名;
其中*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明表示本指针变量所指向的变量的数据类型
-
指针变量的赋值
指针变量跟普通变量一样,使用前不仅要定义而且必须赋予具体的值,没有进过赋值的指针不能使用
给指针变量的赋值与给其他变量所赋的值不同,给指针变量的赋值只能赋予地址,而不能赋予任何其他数据,否则会引起数据
C语言中提供了地址运算符“&”来表示变量的地址
一般形式为:
& 变量名
-
定义指针变量的同时赋值
int a; int *p=&a;
-
先定义指针变量再赋值
int a; int *p; p=&a;
-
指针的引用
引用指针变量是对变量进行间接访问的一种形式,引用指针变量的含义为引用指针变量所指向的值
对指针变量的引用形式为:
*指针变量
-
“&”和“*”运算符
运算符“&”是一个返回操作数地址的单目运算符,叫做取地址运算符
p=&i;
就是将变量i的内存地址赋给p,这个地址是改变了再计算机内部的存储位置
运算符“*”是单目运算符,叫做指针运算符,作用是返回指定地址内的变量的值
q=*p;
就是将变量i的值赋给q,假如变量i的值为5,则q的值也是5
-
“&*”和“*&”的区别
有以下语句:
int a; p=&a;
&和*的运算符优先级相同,按自右而左的方向结合
&*先进行*运算,*p相当于变量a,再进行&运算,&*p就相当于取变量a的地址
*&先计算&运算符,&a就是取变量a的地址,然后进行*运算,*&a就相当于取变量a所在的地址,就是变量a的值
指针自加、自减运算
指针的自加、自减运算不同于普通变量的自加、自减运算
如有指针p指向整形变量i
因为整形变量i在内存中占四个字节,指针p指向变量i的地址,如果指针p进行自增运算不是在地址上加1,而是指向下一个存放整形数的地址
如果指针p进行自减运算,那就是指向上一个存放整形的地址
空指针
在指针声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL 值,赋为NULL值的指针被称为空指针
#include <stdio.h>
int main (){
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr);
return 0;
}
数组和指针
一维数组和指针
当定义了一个一维数组时,系统会在内存中为该数组分配一个存储空间,其数组的名字就是数组在内存中的首地址。若定义一个指针变量,并将数组的首地址传给之间,则该指针就指向了这个一维数组
int *p,a[10];
p=a;
int *p,a[10];
p=&a[0];
因为a[0]的地址就是数组的首地址,所以以上两种形式的赋值效果完全相同
字符串和指针
使用一个字符串有两种方法,一个是用字符数组一个是用字符指针
#include "stdio.h"
int main(){
char *s="Hello!";
printf("%S\n",s);
return 0;
}
上面的这个例子定义了字符指针变量s,用字符串常量“Hello”赋值,其中这不是把“Hello”这些字符存放到s中,只是把这个字符串的第一个字符的地址赋给指针变量
指向指针的指针
一个指针变量可以指向整型变量、实型变量、字符型变量,当然也可以指向指针变量。当这种指针变量指向指针变量时,被称为指向指针的指针
指向指针的指针定义
整形变量i的地址是&i,其值传递给指针变量p1,则p1指向i;同时,将p1的地址&p1传递给p2,则p2指向p1。这里的p2就是指向指针的指针
指向指针的指针的定义为:类型标识符 **指针变量名;
int **p;
其含义为定义一个指针变量p,指向另一个指针变量,该指针变量又指向一个基本整型变量
由于指针运算符"*"是自右往左,所以该定义相当于:
int *(*p);
作为函数参数的指针变量
指针变量也可以作为函数参数
#include "stdio.h"
int swap(int *a,int *b){
int t;
t=*a;
*a=*b;
*b=t;
return 0;
}
int main(){
int x,y;
printf("请输入两个数:");
scanf("%d %d",&x,&y);
swap(x,y);
printf("两个数的交换结果为:x=%d y=%d",x,y);
return 0;
}
swap函数为用户自定义函数,在main函数中调用该函数交换变量a和b的值,swap函数的两个形参被传入了两个地址值,也就是两个指针变量。在swap函数中,使用整形变量t作为中间变量,将两个指针变量指向的值进行交换
在程序调用过程中,主调用函数与被调用函数之间有一个数值传递过程
函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,在函数调用过程中,形参的值发生改变,实参的值不会发生变化
返回指针值的函数
指针变量也可以指向一个函数,一个函数在编译时被分配给一个入口地址,这个函数的入口地址就成为函数的指针,可以用一个指针变量指向函数,然后通过该指针变量调用此函数
返回指针值的函数定义
一个函数可以返回一个整型值、字符值、实型值等,也可以待会指针型的数据(地址)。概念与前面的函数蕾西,只是返回值的类型是指针类型。返回值为指针类型的函数简称为指针函数
定义指针函数的一般形式为:
类型名 *函数名(参数列表);
例如:
int *fun(int a,int b)
fun是函数名,调用它后能得到一个指向整形数据的指针。x和y是fun函数的形式参数,这两个参数也均为基本整形。这个函数的函数名前面有一个*,表示此函数是指针型函数,类型说明是int,表示返回的指针指向整形变量
结构体和共用体
结构体
在一些情况下,基本的类型不能满足使用要求。此时,可以将一些有关的变量组织起来定义成一个结构(structure),来表示一个有机的整体,一种新的类型,这样程序就可以像处理内部的基本数据一样来对结构进行各种操作
结构体类型概念
C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。结构体中的数据成员可以是基本数据类型(如 int、float、char 等),也可以是其他结构体类型、指针类型等。
构造体声明的关键字是***struct***,声明构造体的一般形式为:
struct 构造体名{
成员列表
};
在声明构造体时,大括号后面有一个分好不能忘记
例子:
struct Pro{
char name[10];
char shapr[10];
char color[10];
char func[20];
int price;
char area[20];
};
结构体的定义
结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义。
struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:
struct tag {
member-list
member-list
member-list
...
} variable-list ;
tag 是结构体标签。
member-list 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。
-
没有标明标签
struct { int a; char b; double c; } s1;
-
没有声明变量
struct SIMPLE { int a; char b; double c; };
结构体的引用
定义了结构体变量后,可以引用这个变量。但是不能直接将一个结构体变量作为一个整体进行输入和输出
对结构体变量进行赋值、存取或运算,实质上是对结构体成员的操作,结构体变量成员的一般形式为:
结构体变量名.成员名
有一定义:
struct date{
int year;
int month;
int day;
};
struct student{
int num;
char name[30];
char sex[10];
int age;
struct date birthday;
}s1,s2;
在引用结构的成员时,可以在结构体变量名后面加上成员运算符”.“和成员的名字
例如:
s1.num=10;
s2.name="Zhang";
上面的赋值语句就是对s1结构体中num变量和s2结构体中name变量赋值
如果一个成员的本身又属于一个结构体类型,这个时候要使用若干个成员运算符,一级一级找到最低级额度成员,对最低级的成员进行赋值、存取或运算
例如对s1变量中的出生日期赋值:
s1.birthday.year=2000;
s1.birthday.month=03;
s1.birthday.day=24;
这里不能使用s1.birthday来访问s1变量中成员birthday,因为birthday是一个结构体变量
使用结构体变量的成员也可以像普通变量一样进行各种运算,例如:
s1.age=s2.age+3;
s1.num++;
因为成员运算符的优先级最高,所以s1.num++是对s1的num变量进行自加运算不是对num进行自加运算
结构体的初始化
和其它类型变量一样,对结构体变量可以在定义时指定初始值
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
在初始化时,在定义的变量后面使用等号,然后将其初始化的值放在大括号内,并且每个数据要与结构体的成员列表的顺序一样
-
结构体的初始化实例
在声明结构体变量时初始化和在定义结构体变量后初始化
#include "stdio.h"
struct book{
char title[50];
char author[50];
char subject[100];
int book_id;
} book1={"C 语言","RUNOOB","编程语言",000001};
//上述初始化是在声明结构体变量时初始化
int main(){
struct book2={"C# 语言","RUNOOB","编程语言",000002};
//上述初始化是在定义结构体变量后初始化
printf("book1:\ntitle=%s\nauthor=%s\nsubject=%s\nbook_id=%d\n",book1.title,book1.author,book1.subject,book1.book_id);
printf("book2:\ntitle=%s\nauthor=%s\nsubject=%s\nbook_id=%d\n",book2.title,book2.author,book2.subject,book2.book_id);
}
结构体数组
结构体变量中可以存放一组数据,如果需要定义10个学生的数据,也可以使用数组的形式,这个时候称数组为结构体数组
结构体数组与数组的区别在于,数组中的元素是根据要求定义的结构体类型,而不是基本类型
定义结构体数组
定义结构体数组的方法与定义结构体变量的方法相同,只是将结构体变量替换称数组。定义结构体数组一般形式如下:
struct 结构体名{
成员列表;
}数组名;
例如定义学生信息数组,包含5个学生的信息:
struct Stu{
char name[20];
int num;
char sex;
int grade;
}Stu1[5];
定义这种结构体数组的方式是,声明结构体类型的同时定义结构体数组,结构体数组和结构体变量的位置是相同的
初始化结构体数组
初始化结构体数组和初始化基本类型的数组相同,初始化结构体数组的一般形式为:
struct 结构体名{
成员列表;
}数组名={初始化列表};
例如,为学生信息结构体数组初始化:
struct student{
int mum;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3] = { {10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"}};
在数组进行初始化时,最外层的大括号表示所列出的时数组中的元素,因为每一个元素都是结构体类型,所以每一个元素也要使用大括号,每一个结构体元素都是成员数据
在定义数组stu时,也可以不指定数组中的元素个数,编译器会自动根据初始化列表中的元素个数来确定数组中的元素个数
stu[]={......};
定义结构体数组时,可以先声明结构体类型然后再定义结构体数组
结构体指针
如果一个指针指向结构体变量,那么该指针指向的是结构体变量的起始地址,同样指针变量也可以指向结构体数组中的元素
指向结构体变量的指针
既然指针可以指向结构体变量的地址,就可以使用指针来访问结构体中的成员,定义一个结构体指针的一般形式为:
结构体指针 *指针名;
例如,定义一个指向struct Stu结构类型,名为pStu的指针变量:
struct Stu *pStu;
使用指向结构体变量的指针访问成员有两种方法
-
使用点运算符引用结构体成员
(*pStu).成员名
*pStu表示指向结构体的变量,所以使用点运算符可以应用结构体中的成员变量
例如,pStu指针指向了结构体Stu结构体变量,引用其中的成员:
(*pStu).num=12306;
*pStu一定要使用括号,因为点运算符的优先级是最高的,如果不适用括号就会先执行点运算在执行*运算
-
使用指向运算符引用结构体成员
pStu -> 成员名;
例如使用指向运算符引用一个变量的成员:
pStu -> num=12306;
假如Stu为结构体变量,pStu为指向结构体变量的指针,以下三种形式的效果是等价的:
- Stu.成员名
- (*pStu).成员名
- pStu -> 成员名
在使用->引用成员时,要注意分析几种情况:
- pStu -> num 表示指向的结构体变量中的成员num值
- pStu -> num++ 表示指向的结构体变量中的成员num的值,然后该值加1
- ++pStu -> num 表示指向的结构体变量中的成员num的值加1,计算后再使用
指向结构体数组的指针
结构体指针变量不仅可以指向结构体变量,还可以指向结构体数组,指针变量的值就是结构体数组的首地址
例如,定义一个结构体数组stu[5],使用结构体指针指向该数组:
struct Stu *p;
p=stu;
如果想利用指针指向第三个元素,则在数组名后加上下标,然后在数组名前使用取地址符&,例如:
p=&stu[2];
结构体作为函数参数
结构体变量的值可以作为一个函数的参数,使用结构体所谓函数的参数有三种形式:
-
使用结构体变量作为函数的参数
使用结构体变量作为函数的参数时,采用的是“值传递”,将结构体变量所占的内存单元内容全部顺序传递给形参,形参也必须是同类型的结构体变量,例如:
void dis(struct stu stu1);
如果在函数内部修改了变量中的成员的值,改变的值不会返回到主调函数中
-
使用结构体变量的成员作为函数的的参数
在传递结构体的指针时,只是将结构体变量的首地址进行传递,并没有将变量的副本进行传递。例如,声明一个传递结构的变量指针如下:
void dis(struct Stu *stu1);
这样使用形参stu1指针就可以引用结构体变量中的成员了,如果在函数内修改了成员的数据,那么返回主调函数的值会改变
-
使用指向结构体变量的指针作为函数的参数
这种方式为函数残敌参数与普通变量作为实参是一样的,是传值方式,例如:
dis(Stu.score[0]);
传值时实参要与形参的类型一致
包含结构的结构
有一定义:
struct date{
int year;
int month;
int day;
};
struct student{
int num;
char name[30];
char sex[10];
int age;
struct date birthday;
}s1,s2;
在引用结构的成员时,可以在结构体变量名后面加上成员运算符”.“和成员的名字
例如:
s1.num=10;
s2.name="Zhang";
上面的赋值语句就是对s1结构体中num变量和s2结构体中name变量赋值
如果一个成员的本身又属于一个结构体类型,这个时候要使用若干个成员运算符,一级一级找到最低级额度成员,对最低级的成员进行赋值、存取或运算
例如对s1变量中的出生日期赋值:
s1.birthday.year=2000;
s1.birthday.month=03;
s1.birthday.day=24;
这里不能使用s1.birthday来访问s1变量中成员birthday,因为birthday是一个结构体变量
使用结构体变量的成员也可以像普通变量一样进行各种运算,例如:
s1.age=s2.age+3;
s1.num++;
因为成员运算符的优先级最高,所以s1.num++是对s1的num变量进行自加运算不是对num进行自加运算
共用体
共用体和结构体相似,但是关键字由struct变成了union,共用体和结构体的区别在于:结构体是定义一个由多个数据成员组成的特殊类型,而共用体是定义一块为所有数据成员共享的内存
共用体概念
共用体也称为联合,共用体把集中不同的类型变量存放到同意段内存单元中,所以共用体在同一时刻只能有一个值,它属于某一个数据成员。
由于所有的成员都放在同一块内存,共用体的大小等于最大成员的大小
定义共用体的类型变量的一般形式为:
union 共用体名{
成员列表
}标量列表;
例如,定义一个共用体,其中包括数据成员由整形、字符型和浮点型
union date{
int i;
char c;
float f;
}var;
其中var为定义的共用体变量,而union date为共用体类型,共用也可以像结构体一样将类型的声明和变量定义分开
union date var;
共用体定义变量的方式和结构体定义变量的方式很相似,但是结构体的变量带下为所有的数据成员大小的中和,每个成员都有自己的内存单元;而共用体的大小为数据成员中最大的内存长度
共用体的引用
共用体变量定义完后就可以引用其中的成员数据进行使用。引用的一般形式为:
共用体变量.成员名;
例如,引用定义的var变量中的成员数据:
var.i;
var.c;
var.f;
不能直接引用共用体变量,如printf("%d",var);
在程序中改变共用体的一个成员,其他的成员也会随之改变。当给某个特定的成员赋值时,其他成员也会具有一致的含义,这是因为他们的每一个二进制位的值都被新值所覆盖
共用体变量的初始化
在定义共用体变量时,可以同时对变量进行初始化操作。初始化的值放在一堆大括号中
- 对共用体变量初始化时,只需要一个初始化值就可以了,其类型必须和共用体的第一个成员的类型相一致
- 如果共用体的第一个成员是结构体类型,则初始化值中可以包括多个用于初始化该结构的表达式
共用体类型的数据特点
- 同一内存段可以用来存放几种不同类型的成员,但每次只能存放一种类型,而不是同时存放所有的类型(在共同体中只有一个成员起作用,其他成员不起作用)
- 共同体变量中起作用的成员时最后一次存放该成员,在存入一个新的成员后,原有的成员就失去了作用
- 共同体变量的地址与其他各成员的地址都是同一个变量
- 不能对共同体变量名赋值,也不能引用变量名来得到一个值
枚举类型
利用关键字enum可以声明枚举类型,枚举类型也是一种数据类型。使用该类型可以定义枚举类型变量,一个枚举类型变量包括一对相关的标识符。每个标识符都对应一个整数,称为枚举类型变量
例如定义一个枚举变量,其中每个标识符都对应一个整数值
enum Color(Red,Green,Blue);
Color就是定义的枚举类型变量,在括号中的第一个标识符对应着数值0,第二个标识符对应着数值1,以此类推
在C语言中,每个标识符都是唯一的,而且不能采用关键字或者当前数组用于类的其他相同的标识符
在定义枚举类型变量时,可以对某个特定的标识符指定其对应的整数值,后面的标识符相对应依次加一,例如:
enum Color(Red=2,Green,Blue);
Red为数值2,Green为数值3,Blue为数值4
位运算
位运算操作符
运算符 | 含义 |
---|---|
& | 按位与 |
| | 按位或 |
~ | 取反 |
^ | 按位异或 |
<< | 左移 |
>> | 右移 |
“与”运算符
按位“与”运算符“&”是双目运算符,功能是对应的俩个二进制数都为1时结果为1
“或”运算符
按位“或”运算符“|”是双目运算符,功能是对应的俩个二进制有一个为1时结果为1
“取反”运算符
”取反“运算符”~“为单目运算符,具有左右结合性,功能是按二进制取反,0变1,1变0
“异或”运算符
“异或”运算符“^”是双目运算符,功能是对应的俩个二进制数不相同时位1,相同时为0
“左移”运算符
左移运算符“<<”是双目运算符,功能是把左边的运算数的二进制位全部左移若干位,由右边的数指定移动的位数,高位丢弃,低位补0
“右移”运算符
右移运算符“<<”是双目运算符,功能是把左边的运算数的二进制位全部右移若干位,由右边的数指定移动的位数
编译预处理
宏定义
#define命令就是定义一个可替换的宏,宏定义是预处理命令的一种,它提供了一种可以替代源代码中字符串的机制,根据宏定义中是否有参数,可以分为不带参数的宏定义和带参数的宏定义两种
不带参数的宏定义
宏定义指令#define用来定义一个标识符和一个字符串,以这个标识符来代表这个字符串,在程序中每次遇到该标识符时,就用所定义的字符串替换它,它的作用相当于给指定的字符串起一个别名
不带参数的宏定义一般形式如下:
#define 宏名 字符串
- “#”表示这是一条预处理命令
- 宏名是一个标识符,必须符合C语言标识符的规定
- 字符串可以是常熟、表达式、格式字符串等
使用#define进行宏定义的好处是:需要改变一个常量的时候只需要改变#define命令行,整个程序的常量都会该表,大大提高程序的灵活性。宏名药简单且意义明确,一般习惯用大写字母表示与变量名区分
宏定义不是C语句,不需要在行末加分号
带参数的宏定义
带参数的宏定义不是简单的字符串替换,它还要进行参数替换。一般形式如下:
#define 宏名 (参数表)字符串
读写文件
文件指针
文件指针是一个指向文件有关信息的指针,它们保存在一个结构体变量中
在使用文件时,需要在内存中为其分配空间,用来存放文件的基本信息,该结构体类型由系统定义,C语言规定类型为FILE型,其声明如下:
typedef struct {
short level; //缓冲区的使用量
unsigned flags; //标志文件状态
char fd; //文件号
unsigned char hole; //无缓冲区取消字符输入
short bsize; //缓冲区大小
unsigned char *buffer; //缓冲区指针
unsigned char *curp; //无活动指针
unsigned istemp; //草稿文件标识
short token; //做正确性检测
}FILE;
在定义变量时写成以下形式:
FILE *文件指针名;
在使用FILE定义文件指针时,FILE要大写,例如:
FILE *fp;
打开和关闭文件
文件的打开
在C语言中用fopen函数来实现文件的打开功能,打开文件的操作就是创建一个流,fopen函数的原型在stdio.h中,其调用的一般形式为:
FILE *fp;
fp=fopen("文件名","使用文件方式");
其中,”文件名“是将要被打开文件的文件名,”使用文件方式“是对打开的文件要进行读还是写。
参数 | 作用 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
r+ | 以读/写方式打开文件,该文件必须存在。 |
rb+ | 以读/写方式打开一个二进制文件,只允许读/写数据。 |
rt+ | 以读/写方式打开一个文本文件,允许读和写。 |
w | 打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
w+ | 打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会创建该文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF 符保留)。 |
a+ | 以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据。 |
wb+ | 以读/写方式打开或新建一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或新建一个文本文件,允许读和写。 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
如果使用fopen函数打开成功,则将返回一个有确定指向的FILE类型指针,如果打开失败则返回NULL,打开失败有以下几个原因:
- 指定盘符或路径不存在
- 文件名中含有无效字符
- 以r模式打开一个不存在文件
文件的关闭
文件在使用完后应该使用fclose函数关闭,fclose函数和fopen函数一样,原型在stdio.h中,调用一般形式为:
fclose(文件指针名);
fclose函数也带回一个值,当正常完成关闭时返回0,否则返回EOF(值为-1)
在程序结束前关闭所有文件,防止因为没有关闭文件而造成数据流失
读写文件
打开文件后,即可对文件进行读出或写入操作,在使用读写函数时,必须包含头文件“stdio.h"
写字符函数——fputc函数
fputc函数的一般形式为:
fputc(字符,文件指针名);
fputc函数的功能时候把一个字符写入指针所指向的文件中,例如:
fputc(ch,fp);
ch是要输出的字符,可以是常量也可以是变量,fp是文件指针变量,当函数输出成功就返回输出的字符,否则返回EOF
读字符函数——fgetc函数
fgetc函数的一般形式为:
字符变量=fgetc(文件指针名);
fgetc函数和功能是:从文件指向磁盘文件中读取一个字符,并且将读取的字符存储到字符变量中
fgetc函数的调用形式如下:
ch=fgetc(fp);
写字符串函数——fputs函数
fputs是每次向文件中写入一个字符串,fputs函数的一般形式为:
fputs(字符串,文件指针);
fputs函数功能是向指定文件写入一个字符串,字符串可以是常量也可以是字符数组名,指针或变量
读字符串函数——fgets函数
fgets函数时每次从文件中读出一个字符串,fgets函数的一般形式为:
fgets(字符数组名,n,文件指针);
fgets函数的功能是从指定文件中读一个字符串到字符数组中,n表示所得到的字符串个数(包含’\0’)
格式化写入函数——fprintf函数
fprintf函数的一般形式为:
fprintf(文件类型指针,格式字符串,输出列表);
fprintf函数功能是:将“输出列表”的数据按照指定格式写入到“文件指针名”指向的文件中。例如:
fprintf(fp,"%s","abcd");
这句代码的意思是将字符串"abcd"以%s的格式写入fp所指向的文件中。
格式化读取函数——fscanf函数
fscanf函数的一般形式为:
fscanf(文件类型指针,格式字符串,输入列表);
fscanf函数的功能为:将“格式字符串”中指定的格式,从“文件指针名”指向的文件中将数据存储到输入列表中。例如:
char str[100];
fscanf(fp,"%s",str);
fp所指向的文件中进行读数据,与空格或换行结束,将结果保存到str数组中
数据块读写函数——fwrite函数和fread函数
不会
定位文件
随机读写操作——fseek函数
fseek的一般形式为:
fseek(文件类型指针,位移量,起始点)
返回位置指针——rewind函数
rewind函数一般形式为:
rewind(文件类型指针);
功能时候让位置指针重新返回文件开头文章来源:https://www.toymoban.com/news/detail-672062.html
完
/无活动指针
unsigned istemp; //草稿文件标识
short token; //做正确性检测
}FILE;
在定义变量时写成以下形式:
```C
FILE *文件指针名;
在使用FILE定义文件指针时,FILE要大写,例如:
FILE *fp;
打开和关闭文件
文件的打开
在C语言中用fopen函数来实现文件的打开功能,打开文件的操作就是创建一个流,fopen函数的原型在stdio.h中,其调用的一般形式为:
FILE *fp;
fp=fopen("文件名","使用文件方式");
其中,”文件名“是将要被打开文件的文件名,”使用文件方式“是对打开的文件要进行读还是写。
参数 | 作用 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
r+ | 以读/写方式打开文件,该文件必须存在。 |
rb+ | 以读/写方式打开一个二进制文件,只允许读/写数据。 |
rt+ | 以读/写方式打开一个文本文件,允许读和写。 |
w | 打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
w+ | 打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会创建该文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF 符保留)。 |
a+ | 以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据。 |
wb+ | 以读/写方式打开或新建一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或新建一个文本文件,允许读和写。 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
如果使用fopen函数打开成功,则将返回一个有确定指向的FILE类型指针,如果打开失败则返回NULL,打开失败有以下几个原因:
- 指定盘符或路径不存在
- 文件名中含有无效字符
- 以r模式打开一个不存在文件
文件的关闭
文件在使用完后应该使用fclose函数关闭,fclose函数和fopen函数一样,原型在stdio.h中,调用一般形式为:
fclose(文件指针名);
fclose函数也带回一个值,当正常完成关闭时返回0,否则返回EOF(值为-1)
在程序结束前关闭所有文件,防止因为没有关闭文件而造成数据流失
读写文件
打开文件后,即可对文件进行读出或写入操作,在使用读写函数时,必须包含头文件“stdio.h"
写字符函数——fputc函数
fputc函数的一般形式为:
fputc(字符,文件指针名);
fputc函数的功能时候把一个字符写入指针所指向的文件中,例如:
fputc(ch,fp);
ch是要输出的字符,可以是常量也可以是变量,fp是文件指针变量,当函数输出成功就返回输出的字符,否则返回EOF
读字符函数——fgetc函数
fgetc函数的一般形式为:
字符变量=fgetc(文件指针名);
fgetc函数和功能是:从文件指向磁盘文件中读取一个字符,并且将读取的字符存储到字符变量中
fgetc函数的调用形式如下:
ch=fgetc(fp);
写字符串函数——fputs函数
fputs是每次向文件中写入一个字符串,fputs函数的一般形式为:
fputs(字符串,文件指针);
fputs函数功能是向指定文件写入一个字符串,字符串可以是常量也可以是字符数组名,指针或变量
读字符串函数——fgets函数
fgets函数时每次从文件中读出一个字符串,fgets函数的一般形式为:
fgets(字符数组名,n,文件指针);
fgets函数的功能是从指定文件中读一个字符串到字符数组中,n表示所得到的字符串个数(包含’\0’)
格式化写入函数——fprintf函数
fprintf函数的一般形式为:
fprintf(文件类型指针,格式字符串,输出列表);
fprintf函数功能是:将“输出列表”的数据按照指定格式写入到“文件指针名”指向的文件中。例如:
fprintf(fp,"%s","abcd");
这句代码的意思是将字符串"abcd"以%s的格式写入fp所指向的文件中。
格式化读取函数——fscanf函数
fscanf函数的一般形式为:
fscanf(文件类型指针,格式字符串,输入列表);
fscanf函数的功能为:将“格式字符串”中指定的格式,从“文件指针名”指向的文件中将数据存储到输入列表中。例如:
char str[100];
fscanf(fp,"%s",str);
fp所指向的文件中进行读数据,与空格或换行结束,将结果保存到str数组中
数据块读写函数——fwrite函数和fread函数
不会
定位文件
随机读写操作——fseek函数
fseek的一般形式为:
fseek(文件类型指针,位移量,起始点)
返回位置指针——rewind函数
rewind函数一般形式为:
rewind(文件类型指针);
功能时候让位置指针重新返回文件开头
pdf链接
链接:https://pan.quark.cn/s/a97e08fb4ac0
提取码:gPf1文章来源地址https://www.toymoban.com/news/detail-672062.html
到了这里,关于C语言学习笔记(完整版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!