1 前言
1.1 揭秘什么是程序
(1)那些程序用来上位机开发?嵌入式开发开发分类,及相应的应用领域?
上位机开发常用:C++,Java,C#,Python,C;
嵌入式开发分类:C51搞门禁;
STM32搞无人机;
ARM高级(带Linux内核)搞手机的操作系统,app,微信小程序,搞智能终端的人脸识别等;
1.2 数据结构与算法之间的关系
(1)程序=数据结构+算法,数据结构中数据有哪几种类型,数据结构中的结构又有那些;算法又分为哪几种,适用范围如何及之后从事的方向常用那种。
数据结构中的数据:整型,浮点型,字符型,复合型(结构体,数组);
数据结构中的结构:链表,数,图;
算法:逻辑型(例如控制小车左右转逻辑判断等),数据数学型(例:累加算法,音视频处理算法等)
1.3 生产一个程序的过程
(1)什么是进程?
进程:跑起来的程序。
(2)程序要跑起来需要那些步骤?
步骤:编码,编译,执行
1.4 计算机语言
(1)计算机语言的发展历程?有哪几种,它们之间的差异?
历程:机器语言(读穿孔纸)->符号语言(汇编语言)->高级语言(面向过程的C,面向对象的C++,JAVA)
1.5 线上课程学习方法
(1)看视频,提取出要点,理解代码,默写形成肌肉记忆,多调试代码,多总结(输出至优快云博客,及思维导图);
2 初识
2.1 C语言常用的开发环境
(1)C语言有那些开发工具?嵌入式在什么环境下用C语言开发?
开发工具:Vscode,devC++,啊哈C,Vc++,turboC
2.2 开发环境的安装
(1)window下模拟开发环境。包括notpepad++安装(颜色设置,中文编码设置),gcc安装(配置环境变量),使用;
gcc的使用:命令终端CMD->cd进入代码所在文件夹->编译(gcc + XX.c,可指定输出程序名字gcc xx.c -o 新程序名)->运行(a.exe,指定名字的程序,新程序名.exe)
2.3 C语言的基础框架
(1)C语言程序由那几部分组成?
组成部分:编译预处理指令,程序入口主函数main,程序开始标志,代码,程序退出前返回给调用者的值,程序结束标志;
代码:
#include<stdio.h>// 编译预处理指令
int main()//程序入口主函数main
{//程序开始标志
printf("Are you OK!");//代码部分
return 0;//程序退出前返回给调用者的值
}//程序结束标志
2.4 什么是变量
(1)变量由哪几部分组成?
组成部分:变量名,变量值,变量类型,存储单元
2.5 变量名标识符
(1)变量名及标识符命名有那些规则?
规则:必须由数字,字母,下划线组成,并且数字不能开头,一般采用驼峰命名法(int studentsPerClass)
2.6 计算机数据类型
(1)数据类型那些种类?主要需要掌握的是哪几种及所含字节数?
数据类型种类:
常用数据类型:整型(4字节),字符型(1字节),浮点型(4字节)这3类
2.7 printf打印的用法
(1)printf函数如何使用?
使用:print(“格式控制”,输出表列);
例:print(“%d”,a);。
格式控制包含格式声明,普通字符;
格式声明由%加格式字符组成;
普通字符可以为逗号,空格,行换符及其他字符;
(2)格式声明中的格式字符有那些种类?
格式字符种类:d 十进制,c 单个字符,f 小数,x 十六进制,p打印内存地址,其中取变量地址运算符用&
2.8 print案例
(1)print函数加减乘除练习,注相除时变量定义及相除强转float;
代码:
//计算2个数的加减乘除
#include<stdio.h>
int main(){
int a=12;
int b=5;
int date;
float date1;
date=a+b;
printf("输出两数之和%d\n",date);
date=a-b;
printf("输出两数之差%d\n",date);
date=a*b;
printf("输出两数之积%d\n",date);
date1=(float)a/b;
printf("输出两数之商%f\n",date1);
printf("输出两数之商%f\n",(float)a/b);
return 0;
}
2.9 scanf输入的用法
(1)scanf函数的作用。
作用:用来扫描键盘输入
(2)scanf函数的使用。
使用:scanf(“格式控制”,地址列表);
例子:scanf(“%d”,&a);
格式控制包含格式声明,普通字符;注:格式控制后面接的是变量地址;
格式控制中输入了那些字符,输入的时候也都要输入;)
代码:
/*
scanf 函数进行输入;
一次输入一个数;
一次输入3个数,控制声明中不同方式书写
*/
#include<stdio.h>
int main(){
int date1;
int date2;
int date3;
printf("请输入1个整数:\n");
scanf("%d",&date1);
printf("输入完毕!\n");
printf("你输入的整数为:%d\n",date1);
printf("请输入3个整数\n");
scanf("%d%d%d",&date1,&date2,&date3);
printf("你输入的3个整数分别为:date1=%d,date2=%d,date3=%d\n",date1,date2,date3);
getchar();
printf("请输入3个整数:\n");
scanf("date1=%d,date2=%d,date3=%d",&date1,&date2,&date3);
printf("你输入的3个整数分别为:date1=%d,date2=%d,date3=%d",date1,date2,date3);
return 0;
}
2.10 scanf混合输入注意问题
(1)scanf函数中混合输入数字,字符,小数;
例子:scanf(“%d%c%f”,&a,&b,&c);
注:输入数值数据时候,若输入了空格,回车,tab键或非法字符,判定该数据结束;
代码:
/*
(1)输入2个字符;
(2)输入整数,字符,小数;
*/
#include<stdio.h>
int main(){
char data1;
char data2;
int data3;
char dataChar;
float dataFloat;
printf("请输入2个字符:\n");
scanf("%c%c",&data1,&data2);
printf("你输入的2个字符分别为data1=%c,data2=%c\n",data1,data2);
printf("请分别输入整数,字符,小数:\n");
scanf("%d%c%f",&data3,&dataChar,&dataFloat);
printf("你输入的整数,字符,小数分别为data3=%d,dataChar=%c,dataFloat=%f\n",data3,dataChar,dataFloat);
return 0;
}
2.11 其他输入输出的方式
(1)输入一个字符getchar()
(2)输出一个字符putchar()
(3)输出字符串puts()
(4)puts跟printf的区别?
区别:puts自动加入换行符,printf支持多种类型输出,puts就是字符串
代码:
#include<stdio.h>
int main(){
char c;
puts("请输入一个字符:");
c=getchar();
puts("你输入的字符是:");
putchar(c);
return 0;
}
2.12 输入输出案例
(1)输入大写字母,输出小写字母练习。用到了输入输出函数及ASCII码中大小写字母差值为32;注:连续多次输入scanf获取字符时候,要吸收回车符;
/*
1.验证字母间的大小关系
2.将输入的大写字母转化为小写字母
3.使用scanf输入,及getchar输入;
*/
#include<stdio.h>
int main(){
char a='a';
char b='b';
char A='A';
char B='B';
printf("%c,%c,%c,%c\n",a,b,A,B);
printf("a=%d,b=%d,A=%d,B=%d\n",a,b,A,B);
printf("请输入一个大写字母:\n");
scanf("%c",&a);
b=a+32;
printf("输入的大写字母对应的小写字母为:%c\n",b);
getchar();
printf("请再次输入一个大写字母\n");
b=getchar()+32;
printf("对应的小写字母为%c\n",b);
getchar();
putchar(getchar()+32);
return 0;
}
2.13 密码学讲解和计算器作业
(1)任意输入2个数可获得这2个数的加减乘除的值;
代码:
/*
任意输入2个数可获得这2个数的加减乘除的值;
*/
#include<stdio.h>
int main(){
int data1;
int data2;
puts("请输入2个整数:");
scanf("%d%d",&data1,&data2);
printf("你输入的2个整数分别为%d,%d\n",data1,data2);
printf("两个数之和为%d\n",data1+data2);
printf("两个数之差为%d\n",data1-data2);
printf("两个数之积为%d\n",data1*data2);
printf("两个数相除为%f\n",(float)data1/data2);
return 0;
}
(2)将China转换成每个字母对应的后4个字母,Glmre;
代码:
/*
将China转换成每个字母对应的后4个字母,Glmre;
*/
#include<stdio.h>
int main(){
char data1;
char data2;
char data3;
char data4;
char data5;
puts("请输入China这五个字符");
scanf("%c%c%c%c%c",&data1,&data2,&data3,&data4,&data5);
printf("你输出的China转化后的单词为%c%c%c%c%c\n",data1+4,data2+4,data3+4,data4+4,data5+4);
return 0;
}
3 流程控制
3.1 流程控制之if语句
(1)if语句的使用
例子:if(a==1){print(“a=1”);}
(2)6种关系运算符号
关系运算符:<,<=,>,>=,,!=),优先级(<,<=,>,>=)高于(,!=
3.2 if控制经典案例代数法交换值
(1)输入2个数,按照值从小到大输出;(重点在于引入第3个变量);
代码1:
/*
输入2个整数,按照从小到大的顺序排列
采用代数法
*/
#include<stdio.h>
int main(){
int data1;
int data2;
int dataTmp;
printf("请输入2个整数\n");
scanf("%d%d",&data1,&data2);
if(data1>data2){
dataTmp=data1;
data1=data2;
data2=dataTmp;
}
printf("2个整数排序为%d<=%d\n",data1,data2);
return 0;
}
代码2:
/*
用if函数比较2数大小
*/
#include<stdio.h>
int main(){
int data1;
int data2;
printf("请输入2个整数\n");
scanf("%d%d",&data1,&data2);
if(data1>data2){
printf("2个整数中最大的是:%d\n",data1);
printf("2个整数中最小的是:%d\n",data2);
}
if(data1<data2){
printf("2个整数中最大的是:%d\n",data2);
printf("2个整数中最小的是:%d\n",data1);
}
if(data1==data2){
printf("2个整数相等\n");
}
return 0;
}
(2)输入3个数,按照值从小到到输出;
3.3 逻辑判断与或非
(1)逻辑运算符:逻辑与&&,逻辑或||,逻辑非!
代码:
/*
逻辑与或非的判定
*/
#include<stdio.h>
int main(){
int i;
int j;
puts("请输入2个整数:");
scanf("%d%d",&i,&j);
if(i==1&&j==0){
printf("与逻辑验证无误\n");
}
if(i==1||j==0){
printf("或逻辑验证无误\n");
}
if(!i){
printf("非逻辑验证无误\n");
}
return 0;
}
(2)逻辑运算符在if函数中作为判定条件的使用
3.4 ifelse编程练习
(1)ifelse的使用,案例输入一个字母,如果是大写字母转化为小写,如果不是直接输出;(主要知道ifelse结构,字母ASCII的数值范围)
代码:
/*
输入一个字母,判定其是否为小写,是小写字母则输出小写字母,不是的话,转化为小写字母后输出;
大写字母从A-Z:65-90
小写字母从a-z:97-122
*/
#include<stdio.h>
int main(){
char data;
puts("请输入一个英文字母");
scanf("%c",&data);
if(data>=97&&data<=122){
printf("%c\n",data);
}else if(data>=65&&data<=90){
printf("%c\n",data+32);
}else{
printf("请输入正确的英文字母");
}
printf("Game Over!");
return 0;
}
3.5 if嵌套案例
(1)ifelse嵌套案例的编写,理解逻辑及遵守编程规范;
代码:
/*
ifelse的嵌套判定
*/
#include<stdio.h>
int main(){
int healthyYesOrNot;
int richYesOrNOt;
int handsomeYesOrNot;
int daFangYesOrNot;
puts("请输入你是否健康,健康请扣1,不健康请扣0");
scanf("%d",&healthyYesOrNot);
if(healthyYesOrNot==1){
puts("请输入你是否有钱,是否帅");
scanf("%d%d",&richYesOrNOt,&handsomeYesOrNot);
if(richYesOrNOt==1&&handsomeYesOrNot==1){
puts("请输入你是否大方");
scanf("%d",&daFangYesOrNot);
if(daFangYesOrNot==1){
printf("我愿意跟你做朋友");
}else{
printf("我不愿意跟你做朋友");
}
}
}else{
printf("我不愿意跟你交朋友");
}
return 0;
}
3.6 列表选择switchcase
(1)switchcase跟if相比,switchcase适用于多分支处理场景,不会造成if函数导致的程序冗余,可读性差的缺点;
(2)switchcase案例编辑练习(清楚switchcase结构,注多case可用同一个输出及break)
代码:
/*
swithCase的使用,多分支条件判定
*/
#include<stdio.h>
int main(){
int dataNum;
char dataChar;
puts("请输入一个数字:");
scanf("%d",&dataNum);
printf("你输入的数字是%d\n",dataNum);
switch(dataNum){
case 0:
puts("满足数字0的情况");
break;
case 1:
case 2:
case 3:
puts("满足数字1,2,3的情况");
break;
default :
puts("满足其他数字的情况");
break;
}
getchar();
puts("请输入一个字母:");
scanf("%c",&dataChar);
printf("你输入的字母是%c\n",dataChar);
switch(dataChar){
case 'a':
puts("满足字母a的情况");
break;
case 'b':
puts("满足字母b的情况");
break;
default:
puts("满足其他字母的情况");
break;
}
return 0;
}
3.7 switch练习学习成绩等级划分
(1)switchcase学习成绩等级练习(清楚switchcase结构,用求商判定大小进行划分等级)
3.8 案例根据路程算折扣
(1)根据路程算折扣案例,分别用ifelse,switchcase进行算;
(2)ifelse直接通过范围判断,switchcase需要求商分情况,但结构清晰,可读性好;
/*
根据里程算折扣
*/
#include<stdio.h>
int main(){
int distant;
int discount;
printf("请输入里程数:\n");
scanf("%d",&distant);
if(distant<250){
discount=0;
}else if(distant>=250 && distant<500){
discount=2;
}else if(distant>=500 && distant<1000){
discount=5;
}else if(distant>=1000 && distant<2000){
discount=8;
}else if(distant>=2000 && distant<3000){
discount=10;
}else{
discount=15;
}
printf("你输入的里程数为%d,你将获得的折扣为%.2f",distant,(float)discount/100);
return 0;
}
代码2:
/*
用里程算折扣
s<1 求商为0
1<=s<2 求商为1
2<=s<4 求商为2,3
4<=s<8 求商为4,5,6,7
8<=s<12 求商为8,9,10,11
12<=s 求商为大于等于12
*/
#include<stdio.h>
int main(){
int lucheng;
int discount;
puts("请输入里程数:");
scanf("%d",&lucheng);
switch(lucheng/250){
case 0:
discount=0;
break;
case 1:
discount=2;
break;
case 2:
case 3:
discount=5;
break;
case 4:
case 5:
case 6:
case 7:
discount=8;
break;
case 8:
case 9:
case 10:
case 11:
discount=10;
break;
default:
discount=15;
break;
}
printf("你输入的里程数为%d,可享有%.2f的折扣优惠\n",lucheng,(float)discount/100);
return 0;
}
3.9 案例
(1)函数题用判断语句实现;
(2)分数等级用判断语句实现;
3.10 while循环爱你一万遍
(1)while循环语句的结构;
(2)变量自加(times++),变量自减(times–)的使用;
/*
输出我爱你,循环10遍
*/
//代码1:
#include<stdio.h>
int main(){
int times=10;
while(times){
puts("我爱你!");
times=times-1;
}
return 0;
}
//代码2:
#include<stdio.h>
int main(){
int times=10;
do{
//puts("我爱你!");
printf("我爱你 %d\n",times);
times--;
printf("%d\n",times);
}while(times==1);
printf("%d",times);
return 0;
}
//代码3:
#include<stdio.h>
int main(){
int times;
for(times=10;times>=1;times--){
printf("我爱你\n");
}
return 0;
}
3.11 while循环计算1到100累加
(1)利用while循环计算1到100的累加案例(清楚while语句用法,注:定义2个变量)
代码:
/*
求1累加到100的和
*/
#include<stdio.h>
int main(){
int data=1;
int sum=0;
while(data<=100){
sum=sum+data;
printf("从1累加到%d的和为%d\n",data,sum);
data++;
}
printf("1累加到100的和为%d\n",sum);
return 0;
}
3.12 doWhile的微妙
(1)doWhile循环语句结构(while先判断后执行,而doWhiles是先执行后判断)
代码:
/*
while跟doWhile的应用
*/
#include<stdio.h>
int main(){
int i=1;
/*
while(i){
printf("While is No Problem!");
i--;
}
*/
do{
printf("DoWhile is NO Problem!\n");
i--;
}while(i!=0);
return 0;
}
3.13 while的表达式及for循环等价引入
(1)for循环语句结构
结构:for(表达式1;表达式2;表达式3)
结构说明:for(循环变量赋初值;循环条件;循环变量增值))
(2)while的表达式跟for循环是等价的
3.14 关于for循环表达式省略
(1)for循环中表达式省略用法
注意:表达式1省略,可在for语句前定义;表达式2省略,则无限循环;表达式3省略,可在for语句中加
3.15 循环干涉之break和continue
(1)break跟continue都应用在循环语句中;
(2)break是提前结束循环;continue是提前结束本次循环,直接进入下次循环;
代码:
/*
找出100到200中能被3整除的数。
找出100到200中不能被3整除的数。
注:用到for循环及continue跳出本次循环
*/
/*
#include<stdio.h>
int main(){
int i;
for(i=100;i<=200;i++){
if(i%3!=0){
continue;
}
printf("%d ",i);
}
return 0;
}
*/
#include<stdio.h>
int main(){
int i;
for(i=100;i<=200;i++){
if(i%3==0){
continue;
}
printf("%d ",i);
}
return 0;
}
i
3.16 循环嵌套输出
(1)for循环的循环嵌套(输出i*j个数)
代码:
/*
for循环嵌套
*/
#include<stdio.h>
int main(){
int i;
int j;
int data=0;
for(i=0;i<5;i++){
for(j=0;j<3;j++){
printf("%d ",data++);
printf("%d,%d\n",i,j);
}
}
return 0;
}
(2)4*5矩阵用循环嵌套计算
代码:
/*
输入4行5列的矩阵
*/
#include<stdio.h>
int main(){
int i;
int j;
for(i=1;i<=4;i++){
for(j=1;j<=5;j++){
printf("%d ",i*j);
}
printf("\n");
}
return 0;
}
3.17 案例
(1)求两个正整数m和n的最大公约数和最小公倍数;
(2)水仙花数计算;
4 数组
4.1 数组的引用及基本用法
(1)数组的定义:int a[10]; 类型符 数组名[常量表达式];数组类型,数组名,数组元素个数
(2)利用for循环给数组赋值并输出值;
代码:
/*
数组输出100-110值及地址
*/
#include<stdio.h>
int main(){
int arry[11];
int data;
for(data=0;data<=10;data++){
arry[data]=data+100;
}
for(data=0;data<=10;data++){
printf("%d,%p\n",arry[data],&arry[data]);
}
return 0;
}
4.2 数组计算大小和各种初始化
(1)数组初始化:定义数组用{}直接全部赋值;定义数组用{}赋部分值;定义数组不指定数组长度赋值;
(2)sizeof()是关键字,可用来就算数据内存空间大小;数组长度可用sizeof(数组名)/sizeof(a[0])可得;
代码:
/*
初始化不定长度数组并输出
*/
#include<stdio.h>
int main(){
int arr[]={1,2,3,4,5,6,7,8,9,10};
int i;
int len;
len=sizeof(arr)/sizeof(arr[0]);
printf("数组长度为%d\n",len);
for(i=0;i<len;i++){
printf("%d\n",arr[i]);
}
return 0;
}
4.3 数组应用编程练习
(1)10个数组元素按照逆序输出;(数组,i++,i–)
代码:
/*
顺序输出0-9,逆序输出0-9
*/
#include<stdio.h>
int main(){
int arry[10];
int i;
for(i=0;i<10;i++){
arry[i]=i;
}
printf("顺序输出:\n");
for(i=0;i<10;i++){
printf("%d\n",arry[i]);
}
printf("逆序输出:\n");
for(i=9;i>=0;i--){
printf("%d\n",arry[i]);
}
return 0;
}
(2)斐波那契数列;(数组,for循环,F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*))
代码:
/*
斐波那契数列;(数组,for循环,F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*))
*/
#include<stdio.h>
int main(){
int arry[30];
int len;
int i;
len=sizeof(arry)/sizeof(arry[0]);
arry[0]=0;
arry[1]=1;
for(i=2;i<len;i++){
arry[i]=arry[i-1]+arry[i-2];
}
puts("输出数列:");
for(i=0;i<len;i++){
printf("%d ",arry[i]);
}
return 0;
}
4.4 数组编程练习之冒泡排序法
(1)数组,lenth=元素个数,i<length-1,j<length-i-1,从小到大排取大,绘图可理解;
代码:
/*
冒泡排序法
*/
#include<stdio.h>
int main(){
int array[]={2,35,6,7,43,99,23,12,34};
int i;
int j;
int tmp;
int len;
len=sizeof(array)/sizeof(array[0]);
for(i=0;i<len;i++){
for(j=0;j<len-i-1;j++){
if(array[j]>array[j+1]){
tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
}
}
}
for(i=1;i<len;i++){
printf("%d ",array[i]);
}
return 0;
}
4.5 数组编程练习之简单选择排序
(1)简单选择排序法,lenth=元素个数,i<lenth-1,j=i+1,j<lenth,绘图找规律;
代码:
/*
简单排序法
*/
#include<stdio.h>
int main(){
int array[]={23,6,67,23,4,8,9,2,33};
int i;
int j;
int len;
len=sizeof(array)/sizeof(array[0]);
int tmp;
for(i=0;i<len-1;i++){
for(j=i+1;j<len;j++){
if(array[i]>array[j]){
tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
}
puts("从小到大排序:");
for(i=1;i<len;i++){
printf("%d ",array[i]);
}
return 0;
}
4.6 二维数组
(1)二维数组的定义:类型说明符号 数组名[常量表达式][常量表达式];
例:float a[3][4],表示3行4列;
4.7 二维数组初始化
(1)全部初始化:int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
(2)部分初始化:可以不写行,但要写列 int [][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int [3][4]={{1},{5},{9}},int [3][4]={{1}}等;
4.8 二维数组找最大值及下标
(1)案例找二维数组最大值;for循环,if判定,赋值给max后输出即可
代码:
/*
二维数组定义及取最大值
*/
#include<stdio.h>
int main(){
int array[3][4]={{1,2,4,6},{34,7,8,9},{2,55,89,23}};
int i;
int j;
int max=array[0][0];
int hang;
int lie;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%d ",array[i][j]);
}
puts("");
}
for(i=0;i<3;i++){
for(j=0;j<4;j++){
if(max<array[i][j]){
max=array[i][j];
hang=i+1;
lie=j+1;
}
}
}
printf("第%d行,第%d列为最大值%d\n",hang,lie,max);
return 0;
}
5 函数
5.1 函数的使用
(1)避免冗长,模块化设计,函数按照功能划分;
5.2 函数的三要素
(1)先定义后使用
(2)函数名,参数列表,返回值
(3)函数体:执行功能涉及的处理代码;
5.3 函数的封装和调用1
(1) 定义无参数函数
代码:
/*
定义一个无参数函数
*/
#include<stdio.h>
void printStrings(){
printf("This is a nice day!\n");
printf("I am very happy!\n");
}
int main(){
printStrings();
return 0;
}
(2)定义有参数有返回值函数(一个参数,一个返回值)
代码:
/*
定义含参函数(一个参数,一个返回值)
*/
#include<stdio.h>
int getDataFromX(int x){
int y;
y=x+1;
return y;
}
int main(){
int y;
int x;
puts("请输入一个数:");
scanf("%d",&x);
y=getDataFromX(x);
printf("y的结果为:%d\n",y);
return 0;
}
5.4 函数的封装和调用2
(1)定义有参数有返回值函数(两个参数,一个返回值)
代码:
/*
定义一个函数(2个参数,一个返回值)
*/
#include<stdio.h>
int add(int data1,int data2){
int sum;
sum=data1+data2;
return sum;
}
int main(){
int x;
int y;
int z;
puts("请输入2个数字:");
scanf("%d%d",&x,&y);
z=add(x,y);
printf("%d+%d=%d\n",x,y,z);
return 0;
}
(2)定义有参数有返回值函数(三个参数,一个返回值)
(3)空函数用于模块化设计占坑
5.5 形参实参区别
(1)主调函数中的参数为实际参数,被调函数中的参数为形式参数;
(2)实际参数可以是常量,变量或表达式;
(3)变量要素:变量名,变量类型,变量值,变量地址;
代码:
/*
形参跟实参的区别
*/
#include<stdio.h>
int dataFromX(int x){
printf("dataFromX函数中x的地址为%d,数值为%d\n",&x,x);
return 0;
}
int main(){
int x;
int y;
puts("请输入一个数:");
scanf("%d",&x);
y=dataFromX(x);
printf("main函数中x的地址为%d,数值为%d\n",&x,x);
return 0;
}
5.6 案例:函数封装获两个数的大数
(1)函数包含一个返回值,两个参数,函数名,函数体(正常实现,三目运算符)
代码:
/*
用函数实现取2个整数的最大值
*/
#include<stdio.h>
int getMaxFromTwo(int x,int y){
int z;
/*
if(x>y){
z=x;
}else{
z=y;
}
*/
z=x>y?x:y;
return z;
}
int main(){
int x;
int y;
int z;
puts("请输入两个数:");
scanf("%d%d",&x,&y);
z=getMaxFromTwo(x,y);
printf("两个数的最大值为%d",z);
return 0;
}
5.7 函数总结
函数调用过程:
(1)函数形参只是在被调用时占内存空间;(内存空间)
(2)实参只是把值传递给形参;(值传递)
(3)return语句将函数值返回给主调函数;(值返回)
(4)调用结束,形参单元被释放;(内存释放)
函数调用条件:
(1)函数要被定义;
(2)调用库函数(#include<stdio.h>的stdio.h头文件中包含输入输出库函数的声明);
(3)被调函数如果在主调函数后的话,需先声明被调函数;(例:int getMax(int x,int y);)
5.8 函数嵌套及编程实战
(1)函数嵌套调用(main函数->a函数->b函数)
(2)函数找四个数中最大值
代码:
/*
函数嵌套调用,计算四个数中最大值
*/
#include<stdio.h>
int getBigOneFromTwo(int a,int b){
int max;
max=a>b?a:b;
return max;
}
int getBigOneFromFour(int a,int b,int c,int d){
int max;
max=getBigOneFromTwo(a,b);
max=getBigOneFromTwo(max,c);
max=getBigOneFromTwo(max,d);
return max;
}
int main(){
int data1;
int data2;
int data3;
int data4;
int biggestOne;
puts("please input four numbers!\n");
scanf("%d%d%d%d",&data1,&data2,&data3,&data4);
biggestOne=getBigOneFromFour(data1,data2,data3,data4);
printf("the biggest one is %d\n",biggestOne);
return 0;
}
5.9 递归函数编程
(1)age(n)=10(n=1),age(n)=age(n-1)+2(n>1)
代码:
/*
递归函数求:
数学表达式age(n)=10(n=1),age(n)=age(n-1)+2(n>1)
*/
#include<stdio.h>
int getAge(int num){
int age;
if(num==1){
age=10;
}else{
age=getAge(num-1)+2;
}
return age;
}
int main(){
int num;
int age;
puts("请输入你需要了解的第几位学生的年龄:");
scanf("%d",&num);
age=getAge(num);
printf("第%d个学生的年龄为%d岁\n",num,age);
return 0;
}
5.10 阶乘案例
(1)f(n)=1(n=1),f(n)=f(n-1)*n(n>1),注意int越界问题;
代码:
/*
递归函数求n的阶乘:
n!
*/
#include<stdio.h>
int getF(int n){
int f;
if(n==1){
f=1;
}else{
f=getF(n-1)*n;
}
return f;
}
int main(){
int n;
int f;
puts("请输入你要计算那个阶乘的的值:");
scanf("%d",&n);
while(n>=13){
puts("你所输入的n大于等于13,越界了,请重新输入你要计算那个阶乘的的值:");
scanf("%d",&n);
}
f=getF(n);
printf("计算得出%d!的阶乘等于%d\n",n,f);
//printf("int的内存大小为%d",sizeof(int));
return 0;
}
5.11 数组和函数开发
(1)数组当成函数的实际参数;
(2)函数的实参用数组名(数组名代整个数组的首地址);
代码1:
/*
将数组当成实参
*/
#include<stdio.h>
void prinfArry(int arry[3]){
int i;
for(i=0;i<3;i++){
printf("%d ",arry[i]);
}
}
int main(){
int arry[3]={1,2,3};
int i;
prinfArry(arry);
return 0;
}
代码2:
/*
将数组当成实参2
*/
#include<stdio.h>
void prinfArry(int arry[],int len){
int i;
//printf("arry的大小为%d\n",sizeof(arry));
for(i=0;i<len;i++){
printf("%d ",arry[i]);
}
}
int main(){
int len;
int arry[6]={1,2,3,4,5,6};
len=sizeof(arry)/sizeof(arry[0]);
prinfArry(arry,len);
//prinfArry(&arry[0],len);
return 0;
}
5.12 数组传参(形式参数)
(1)函数的实参为数组时,这个实参可用数组名(例:arry), 数组第一个元素的地址(例:&arry[0])
(2)函数的形参中不存在数组的概念,传递的是一个地址,是数组的首地址。 即便形参中写了数组的大小,也是无效的。(例:int arry[4],int arry[]都是一样的)
(3)操作系统中用8个字节表示一个地址;
5.13 数组实参
(1)函数实参为变量传递的是值,操作不同的内存空间。
代码:
/*
值传递
*/
#include<stdio.h>
int changeData(int data){//传递的是值,操作不同的内存空间
data=data+100;
printf("changeData中data的值为:%d\n",data);
}
//changeData中data的值为:200
int main(){
int data;
data=100;
changeData(data);
printf("main中data的值为:%d\n",data);
return 0;
}
//main中data的值为:100
(2)函数实参为数组,传递的是地址,操作相同的内存空间。
代码:
/*
地址传递
*/
#include<stdio.h>
int changeData(int arry[]){//传递的是地址,操作相同的内存空间
arry[0]=arry[0]+2;
printf("changeData中arry[0]的值为:%d\n",arry[0]);
return 0;
}
//changeData中arry[0]的值为:4
int main(){
int arry[2]={2,4};
changeData(arry);//数据名为实参
printf("main中arry[0]的值为:%d\n",arry[0]);
return 0;
}
//main中arry[0]的值为:4
5.14 案例:数组计算不同班级学生的平均分
(1)利用数组输入各个班级同学的分数,由于2个班级求平均分结构是一样的,引入函数避免冗余;
(数组初始化赋值函数,数组输出函数,数组累计求和后求平均值函数)
代码:
/*
输入各班级人员分数,并求出平均分;
利用数组输入各个班级同学的分数,由于2个班级求平均分结构是一样的,引入函数避免冗余
(数组初始化赋值函数,数组输出函数,数组累计求和后求平均值函数)
*/
#include<stdio.h>
void initArry(int arr[],int len){
int i;
for(i=0;i<len;i++){
printf("请输入第%d个同学的分数:\n",i+1);
scanf("%d",&arr[i]);
}
}
void printfArry(int arr[],int len){
int i;
for(i=0;i<len;i++){
printf("请输出第%d个同学的分数为%d\n",i+1,arr[i]);
}
}
float averOfClass(int arr[],int len){
int i;
float sum=0;
float aver;
for(i=0;i<len;i++){
sum=sum+arr[i];
}
aver=sum/len;
return(aver);
}
int main(){
int classOne[5];
int classTwo[2];
int lenOfClassOne;
int lenOfclassTwo;
float averOfClassOne;
float averOfClassTwo;
lenOfClassOne=sizeof(classOne)/sizeof(classOne[0]);
lenOfclassTwo=sizeof(classTwo)/sizeof(classTwo[0]);
puts("请输入1班学生的分数。");
initArry(classOne,lenOfClassOne);
puts("请输入2班学生的分数。");
initArry(classTwo,lenOfclassTwo);
puts("---------------------------------------------------------");
puts("输出1班学生的分数。");
printfArry(classOne,lenOfClassOne);
puts("---------------------------------------------------------");
puts("输出2班学生的分数。");
printfArry(classTwo,lenOfclassTwo);
puts("---------------------------------------------------------");
averOfClassOne=averOfClass(classOne,lenOfClassOne);
averOfClassTwo=averOfClass(classTwo,lenOfclassTwo);
printf("一班的平均分为%f\n",averOfClassOne);
printf("二班的平均分为%f\n",averOfClassTwo);
return 0;
}
5.15 二维数组和函数的概念和实战
(1)二维数组中的形参需考虑"数组数据类型","二维数组中一维数组个数"这2点;(例:arr[][3])
代码:
/*
有3x4矩阵,初始化它并输出,然后求最大值并输出
*/
#include<stdio.h>
void printfArrOfDouble(int arr[][4],int hang,int lie){
int i;
int j;
for(i=0;i<hang;i++){
for(j=0;j<lie;j++){
printf("%d ",arr[i][j]);
}
putchar('\n');
}
}
int getMaxFromArrOfDouble(int arr[][4],int hang,int lie){
int max=arr[0][0];
int i,j;
for(i=0;i<hang;i++){
for(j=0;j<lie;j++){
if(max<arr[i][j]){
max=arr[i][j];
}
}
}
return max;
}
int main(){
int arr[3][4]={{1,2,3},{4,5,6,7},{1,8,8,9}};
int max;
printfArrOfDouble(arr,3,4);
puts("--------------------------------------------------------");
max=getMaxFromArrOfDouble(arr,3,4);
printf("矩阵中最大值为%d",max);
return 0;
}
5.16 外部变量和全局变量
(1)函数内的变量为局部变量;
(2)函数外的变量为外部变量;
(3)写在所有函数之前的外部变量为全局变量;
5.17 全局变量实战开发
(1)调用一个函数,无法返回多个值时,可以定义全局变量;
代码:
/*
班上10 个学生,封装一个函数,调用该函数后获得班上的平均分,最高分,最低分
场景:调用一个函数,无法返回多个值时,可以定义全局变量;
*/
#include<stdio.h>
int max;
int min;
float getResult(int arr[],int len){
int i;
max=min=arr[0];
int sum=0;
float aver;
for(i=0;i<len;i++){
if(max<arr[i]){
max=arr[i];
}
if(min>arr[i]){
min=arr[i];
}
sum +=arr[i];
}
aver=(float)sum/len;
return aver;
}
int main(){
float aver;
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int len=sizeof(arr)/sizeof(arr[0]);
aver=getResult(arr,len);
printf("班上最高分为%d,最低分为%d,平均分为%.2f",max,min,aver);
return 0;
}
5.18 函数案例
(1)要求输入10个数,找出最大数以及最大数的下标
(2)封装冒泡排序的函数
(3)封装选择排序的函数
6 指针
6.1 指针的引入
(1)指针与地址等价;
(2)回顾点:变量的4要素(变量名,变量类型,变量值,内存地址)
6.2 指针变量的引入
(1)指针变量就是存放地址变量;
(2)int a中的为标识符,用于指针变量的声明和定义;(&a)中的为取值运算符,读取内存地址中的值
6.3 指针变量为什么要求类型
(1)指针变量类型为char时候,占1个字节(0x1234只会显示0x34),变量+1,其实就是跨越1个字节长度(0x1235);
(2)指针变量类型为int时候,占4个字节(0x1234显示0x1234),变量+1,其实就是跨越了4个字节长度(0x1238);
(3)总结:指针变量类型决定了所占空间大小,及增量变化;
代码:
/*
指针取值
*/
#include<stdio.h>
int main(){
int a=10;
int *p;
p=&a;
printf("a的值为%d\n",a);
printf("a的地址为0x%p\n",&a);
printf("通过指针取a的值为%d\n",*(&a));
printf("通过指针变量取a的值为%d\n",*p);
printf("p的地址为%p\n",p);
printf("++p的地址为%p\n",++p);//整形数占4个字节,地址+1就是跨越了4个字节
return 0;
}
/*结果:
a的值为10
a的地址为0x000000000061FE14
通过指针取a的值为10
通过指针变量取a的值为10
p的地址为000000000061FE14
++p的地址为000000000061FE18
*/
6.4 为什么要用指针场景一
(1)封装函数实现2个数的交换;(函数实参为变量a,传递的是值;函数实参为&a,传递的是地址)
代码:
/*
封装函数实现2个数的交换;
(函数实参为变量a,传递的是值;函数实参为&a,传递的是地址)
*/
#include<stdio.h>
//常规交换2数值方法
/*
int main(){
int data1=1;
int data2=2;
int tmp;
tmp=data1;
data1=data2;
data2=tmp;
printf("data1=%d,data2=%d\n",data1,data2);
return 0;
}
*/
void changData(int *pdata1,int *pdata2){
int tmp;
tmp=*pdata1;
*pdata1=*pdata2;
*pdata2=tmp;
}
//封装函数交换2数的值
int main(){
int data1=1;
int data2=2;
int tmp;
changData(&data1,&data2);
printf("data1=%d,data2=%d\n",data1,data2);
return 0;
}
6.5 为什么要用指针场景二
(1)指向固定的内存地址
代码:
/*
指针使用场景:指向固定的内存地址
*/
#include<stdio.h>
int main(){
int *p=(int *)0x1234;//0x1234是整形,要强转为指针;
printf("输出指针p的地址%p\n",p);
printf("输出指针p的值%d\n",p);
return 0;
}
6.6 指针回顾和作业布置
(1)输入三个数a,b,c; 要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现
6.7 定义一个指针变量指向数组
(1)可将数组名赋给指针变量(例:int *p=arr),也可将数组首个元素的地址赋给指针变量(例:int *p=&arr[0]),两者等价;
代码:
/*
指针变量指向数组;
可将数组名赋给指针变量(例:int *p=arr),也可将数字的首地址赋给指针变量(例:int *p=&arr[0]),两者等价;
*/
#include<stdio.h>
int main(){
int arr[5]={12,12,5,6,49};
//int *p=arr;
int *p=&arr[0];
printf("数组的第一个元素为%d\n",*p);
return 0;
}
6.8 指针偏移遍历数组
(1)指针增量其实是地址的偏移,地址偏移要根据指针类型决定(int类型的指针变量+1,增4个字节)
代码:
/*
指针偏移遍历数组
*/
#include<stdio.h>
int main(){
int arr[3]={1,2,3};
int *p=arr;
int i;
printf("下标法-数组第1个元素的值为%d\n",arr[0]);
printf("指针法-数组第1个元素的值为%d\n",*p);
puts("下标法-------------------------------------------");
for(i=0;i<3;i++){
printf("下标法-数组第%d个元素的地址为%p,值为%d\n",i+1,&arr[i],arr[i]);
}
puts("指针法-------------------------------------------");
for(i=0;i<3;i++){
printf("指针法-数组第%d个元素的地址为%p,值为%d\n",i+1,(p+i),*(p+i));
}
return 0;
}
6.9 指针偏移补充
(1)p++中先p优先后p=p+1(依次获取地址中值)
代码:
/*
*p++(依次获取地址中值)
*/
#include<stdio.h>
int main(){
int arr[3]={1,2,3};
int *p;
int i;
p=arr;
for(i=0;i<3;i++){
printf("%d ",*p++);
}
p=arr;
for(i=0;i<3;i++){
printf("%d ",*p);
p++;
}
return 0;
}
6.10 指针和数组名
(1)指针用来当数组名(例:p[i]);
(2)数组名来加(*(arr+i));
(3)数组名不可用来++(*p属于指针变量,*arr属于常量指针)
(4)数组名sizeof为数组长度,指针变量长度为地址长度;
(例:sizeof(arr)中数组有3个元素,则其长度为12;
sizeof§为指针变量,其长度为8;)
代码:
/*
指针与数组混合使用
*/
#include<stdio.h>
int main(){
int arr[3]={1,2,3};
int *p;
int i;
p=arr;
//指针用来当数组名(例:p[i]);
for(i=0;i<3;i++){
printf("%d ",p[i]);
}
putchar('\n');
//数组名来加(*(arr+i));
for(i=0;i<3;i++){
printf("%d ",*(arr+i));
}
putchar('\n');
//数组名不可用来++(*p属于指针变量,*arr属于常量指针)
for(i=0;i<3;i++){
printf("%d ",*p++);
}
putchar('\n');
//数组名sizeof为数组长度,指针变量长度为地址长度;
printf("数组长度为%d\n",sizeof(arr));
printf("指针长度为%d\n",sizeof(p));
return 0;
}
6.11 函数指针数组结合练习
(1)函数封装数组初始化,遍历(将地址换成指针变量)
代码:
/*
函数封装数组初始化,遍历
*/
#include<stdio.h>
void initArry(int *parr,int len){
int i;
for(i=0;i<len;i++){
printf("请输入数组的第%d个元素\n",i+1);
scanf("%d",parr);
parr++;
}
}
void printArry(int *parr,int len){
int i;
for(i=0;i<len;i++){
printf("%d ",*parr++);
}
}
int main(){
int arr[3]={1,2,3};
int len=sizeof(arr)/sizeof(arr[0]);
initArry(arr,len);
printArry(arr,len);
return 0;
}
6.12 数组翻转练习
(1)将数组中的n个元素按逆序存放(数组中元素交换,交换次数i<len/2,i跟j进行交换,j=len-1-i;)
代码:
/*
将数组中的n个元素按逆序存放
*/
#include<stdio.h>
void initArry(int *parr,int len){
int i;
for(i=0;i<len;i++){
printf("请输入数组的第%d个元素\n",i+1);
scanf("%d",parr);
parr++;
}
}
void reverseArry(int *parr,int len){
int i,j;
int tmp;
for(i=0;i<len/2;i++){
j=len-1-i;
tmp=*(parr+i);
*(parr+i)=*(parr+j);
*(parr+j)=tmp;
}
}
void printArry(int *parr,int len){
int i;
for(i=0;i<len;i++){
printf("%d ",*parr++);
}
putchar('\n');
}
int main(){
int arr[3]={1,2,3};
int len=sizeof(arr)/sizeof(arr[0]);
initArry(arr,len);
printArry(arr,len);
reverseArry(arr,len);
printArry(arr,len);
return 0;
}
6.13 二维数组的地址一
(1)二维数组还是数组,只是数组中的元素还是数组(子数组);
例:二维数组arr[3][4]中的父数组包含3个子数组,每个子数组中含有4个元素;
6.14 二维数组的地址二
(1)数组名代表数组首个元素的地址;
例:a.二维数组arr[3][4]中的arr是父数组名字,也是父数组第一个元素地址;
b.二维数组arr[3][4]中的arr[0]是第一个子数组名字,也是第一个子数组第一个元素的地址;
6.15 编程验证
(1)二维数组中取的值又是数组,计算机中操作数组是得到数组的首地址;(二维数组arr[3][4]中arr[0]跟*(arr+0)等价)
代码:
/*
父数组地址,子数组地址
*/
#include<stdio.h>
int main(){
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
printf("arr的地址为%p,偏移1后的地址为%p\n",arr,arr+1);
printf("arr[0]的地址为%p,偏移1后的地址为%p\n",arr[0],arr[0]+1);
printf("arr[0]的地址为%p,偏移1后的地址为%p\n",*(arr+0),*(arr+0)+1);
return 0;
}
6.16 二维数组地址写法应用
(1)二维数组取值方式arr[i][j],(arr[i]+j),(*(arr+i)+j);
代码:
/*
二维数组的多种取值方式
*/
#include<stdio.h>
int main(){
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int i,j;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("二维数组的地址0x%p,值%d\n",&arr[i][j],arr[i][j]);
printf("二维数组的地址0x%p,值%d\n",arr[i]+j,*(arr[i]+j));
printf("二维数组的地址0x%p,值%d\n",*(arr+i)+j,*(*(arr+i)+j));
}
}
return 0;
}
6.17 数组指针
(1)数组指针定义:int (*p)[4],其中p等价为二维数组名;
(2)数组指针输出二维数组所有值;
代码:
/*
数组指针
*/
#include<stdio.h>
int main(){
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int i,j;
int (*p)[4];
p=arr;
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("二维数组的地址0x%p,值%d\n",*(arr+i)+j,*(*(arr+i)+j));
printf("二维数组的地址0x%p,值%d\n",*(p+i)+j,*(*(p+i)+j));
}
}
return 0;
}
6.18 数组指针和二维数组
(1)数组指针输出二维数组任意行列值;
代码:
/*
输出二维数组中的任意行列
*/
#include<stdio.h>
void inputHangLie(int *hang,int *lie){
puts("请输入行,列值:");
scanf("%d%d",hang,lie);
}
int outputData(int (*p)[4],int hang,int lie){
int data;
data=*(*(p+hang)+lie);
return data;
}
int main(){
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int hang,lie;
int data;
inputHangLie(&hang,&lie);
data=outputData(arr,hang,lie);
printf("%d行%d列的值为%d",hang,lie,data);
return 0;
}
6.19 函数指针
(1)函数名就是地址;
(2)函数指针变量的定义int (*p)(int a,int b);
补充函数声明:int getData(int a,int b);
代码:
/*
函数指针
*/
#include<stdio.h>
void printfWelocme(){
puts("welcome to china!");
}
int printfIncData(int data){
return ++data;
}
int main(){
void (*p)();//无参函数指针
int (*p2)(int data);//有1个参数函数指针
p=printfWelocme;
p2=printfIncData;
(*p)();
printf("增1函数的值为",(*p2)(12));
return 0;
}
6.20 函数指针编程-回调函数
(1)两个整数a和b,用户输入1,2或3。输入1求a,b最大值,输入2求a,b的最小值,输入3求a,b之和;
(编译检查错误详情:gcc XXX.c -g ->gdb a.exe ->r->quit)
代码:
/*
两个整数a和b,用户输入1,2或3。输入1求a,b最大值,输入2求a,b的最小值,输入3求a,b之和;
*/
#include<stdio.h>
#include<stdlib.h>
int getMax(int data1,int data2){
return data1>data2? data1:data2;
}
int getMin(int data1,int data2){
return data1<data2? data1:data2;
}
int getSum(int data1,int data2){
return data1+data2;
}
//封装函数获取结果
/*
int getData(int data1,int data2,int (*pfunc)(int data1,int data2)){
int res;
res=(*pfunc)(data1,data2);
return res;
}
*/
int main(){
int a=10,b=12;
int num;
int res;
int (*pfunc)(int data1,int data2);
puts("请输入1或2或3");
scanf("%d",&num);
switch(num){
case 1:
pfunc=getMax;
break;
case 2:
pfunc=getMin;
break;
case 3:
pfunc=getSum;
break;
default:
printf("你输入有误!");
exit(-1); //(编译检查错误详情:gcc XXX.c -g ->gdb a.exe ->r->quit)
break;
}
res=(*pfunc)(a,b);
//res=getData(a,b,pfunc);//封装函数获取结果
printf("结果为:%d",res);
return 0;
}
6.21 指针数组概念
(1)指针数组定义:类型名* 数组名[数组长度](例:int* p[4],指向整形数据的指针类型)
(2)指针数组(数组的每一项都是指针变量),数组指针(就是一个指向数组的指针变量)
(例:指针数组int* p[4],数组指针int (*p)[4])
代码:
/*
函数指针数组;
两个整数a和b,同时输出a,b最大值,a,b的最小值,a,b之和;
*/
#include<stdio.h>
#include<stdlib.h>
int getMax(int data1,int data2){
return data1>data2? data1:data2;
}
int getMin(int data1,int data2){
return data1<data2? data1:data2;
}
int getSum(int data1,int data2){
return data1+data2;
}
int main(){
int a=10,b=12;
int num;
int res;
int (*pfunc[3])(int data1,int data2)={getMax,getMin,getSum};
for(int i=0;i<3;i++){
res=(*pfunc[i])(a,b);
printf("res=%d\n",res);
}
return 0;
}
6.22 指针函数概念
(1)指针函数定义:返回指针值的函数(例:int* a(int x,int y);)
(2)案例:a个学生,每门学生有b门课程成绩,利用指针函数实现输入学生序号后,输出学生全部成绩;
代码:
/*
指针函数
*/
#include<stdio.h>
int* getPosition(int num,int (*pscore)[4]){
int *pos;
pos=(int *)(pscore+num);
return pos;
};
int main(){
int score[3][4]={{62,52,69,87},{74,85,76,89},{85,94,76,99}};
int num;
int *ppos;
puts("请输入学生号0或1或2");
scanf("%d",&num);
ppos=getPosition(num,score);
for(int i=0;i<4;i++){
//printf("%d ",*(ppos+i));
printf("%d ",*ppos++);
}
return 0;
}
6.23 二级指针
(1)一级指针保存普通变量的地址,二级指针保存指针变量的地址(例:int **p);
代码:
/*
二级指针
*/
#include<stdio.h>
int main(){
int data=100;
int *p=&data;
int **p2=&p;
printf("data的值为:%d\n",data);
printf("data的地址为:0x%p\n",&data);
printf("p保存了data地址为:0x%p\n",p);
printf("p的地址为:0x%p\n",&p);
printf("p2保存了p地址为:0x%p\n",p2);
printf("*p2访问p的地址:0x%p\n",*p2);
printf("**p2访问data:%d\n",**p2);
return 0;
}
//结果如下:
/*
data的值为:100
data的地址为:0x000000000061FE14
p保存了地址为:0x000000000061FE14
p的地址为:0x000000000061FE08
p2保存了p地址为:0x000000000061FE08
*p2访问p的地址:0x000000000061FE14
**p2访问data:100
*/
6.24 二级指针实战
(1)函数调用修改调用函数指针指向,相当于调用函数修改某变量的值;(同理)
代码:
/*
二级指针实战
*/
#include<stdio.h>
void getPosition(int num,int (*pscore)[4],int **ppos){
*ppos=(int *)(pscore+num);
};
int main(){
int score[3][4]={{62,52,69,87},{74,85,76,89},{85,94,76,99}};
int num;
int *ppos;
puts("请输入学生号0或1或2");
scanf("%d",&num);
getPosition(num,score,&ppos);
for(int i=0;i<4;i++){
//printf("%d ",*(ppos+i));
printf("%d ",*ppos++);
}
return 0;
}
6.25 二级指针和二维数组
(1)二级指针的使用;(先定义一个一级指针后定义二级指针)
代码:
/*
二级指针的使用
*/
#include<stdio.h>
int main(){
int score[3][4]={{62,52,69,87},{74,85,76,89},{85,94,76,99}};
int (*p)[4]=score;
int **p2=(int **)&p;
printf("%d",**p2);
return 0;
}
6.26 指针总结
(1)各种指针定义;
例:
int (**a)[10];->一个指向指针的指针,被指向的指针指向一个有10个 整型数的数组;
int *(*a)[10];->一个指向数组的指针,该数组有10个整形指针;
int (a[10])(int);->一个有10个指针的数组,每个指针指向一个函数,该函数有一个整形参数并返回一个整数类型;
int ((*a)(int,int))(int);->一个函数指针,指向函数的类型是有2个整形参数并且返回一个函数指针的函数,返回的函数指针指向有一个整形参数且返回整形数的函数;
7 字符串
7.1 认识字符串
(1)定义:char str[]=“hello”;(字符串变量,允许修改值)
char *p=“hello”;(字符串常量,指向字符串常量的地址空间,不允许修改值)
代码:
/*
字符串定义
*/
#include<stdio.h>
int main(){
char str[]="hello";
char *p="hello";
printf("%s\n",str);
printf("%s\n",p);
puts(str);
puts(p);
return 0;
}
7.2 字符串存放方式及结束标志
(1)字符串与字符数组区别(字符串中默认添加了’\0’作为结束标志)
代码:
/*
字符串与字符数组区别
*/
#include<stdio.h>
int main(){
char data1[]={'h','e','l','l','o'};
char data2[]="hello";
int len1=sizeof(data1)/sizeof(data1[0]);
int len2=sizeof(data2)/sizeof(data2[0]);//sizeof(data2)其实相当于存了{'h','e','l','l','o','\0'},所以长度为6
printf("%s\n",data1);//数组以%s输出 ,由于编译器优化程度不一致,可能会乱码
printf("%s\n",data2);
printf("%d\n",len1);
printf("%d\n",len2);
return 0;
}
/*
hello
hello
5
6
*/
7.3 sizeof和strlen区别
(1)sizeof用来计算字符数组或字符串长度,strlen用来计算字符数组或字符串有效长度;
代码:
/*
sizeof和strlen区别
*/
#include<stdio.h>
#include <string.h>
int main(){
char data1[100]="hello";
printf("%d\n",sizeof(data1));//输出:100
printf("%d\n",strlen(data1));//输出:5
char *pdata1="hello";
printf("%d\n",sizeof(pdata1));//输出:8 (pdata1为char *,sizeof计算表示用多少字节表示一个地址)
printf("%d\n",strlen(pdata1));//输出:5
//指针类型存储一个地址都是用8个字节
printf("%d\n",sizeof(char *));//输出:8
printf("%d\n",sizeof(int *));//输出:8
return 0;
}
7.4 malloc动态开辟内存空间
(1)malloc函数原型:void malloc(size_t size),用来开辟内存空间;
(例:(char)malloc(10),开辟内存空间为10个字节,强转为char类型指针返回)
(2)realloc函数原型:void *realloc(void *ptr, size_t size),用来扩容;
(例:realloc(p,10),给p增加10个字节内存空间)
(3)free函数原型:void free(void *ptr) ,用来释放内存;
(例:free§;p=NUlLL;内存释放,指针指向NULL;
由于calloc、malloc 或 realloc 申请的内存空间都是堆存放,使用后需及时释放,为防止悬挂指针,释放指针指向NULL)
(4)memset函数原型: void *memset(void *str, int c, size_t n),内存初始化;
(例:memset(p,‘\0’,12),对12个字节内存空间的每一个字节初始化为’\0’)
(5)strcpy函数原型:char strcpy(char dest, const char *src),拷贝数据;
(strcpy(p,“hello”)拷贝hello字符串到p中)
代码:
/*
malloc动态开辟内存空间
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
char *p;
p=(char *)malloc(1);
*p='c';
printf("%c\n",*p);//输出:c
printf("%x\n",p);//输出:bc13d0
printf("%p\n",p);//输出:0000000000BC13D0
free(p);
p=NULL;
p=(char *)malloc(10);
if(p==NULL){
printf("malloc erro\n");//打印开辟内存空间失败
exit(-1);
}
memset(p,'\0',10);//10个字节的内存空间初始化为
printf("%x\n",p);//输出:bc6b40
int newlen=strlen("hellochina123456789a")-12+1;//+1是加'\0'这个字节
printf("%d\n",newlen);//输出:9
realloc(p,newlen);//扩容
printf("%x\n",p);//输出:bc6b40
strcpy(p,"hellochina123456789a");//拷贝"hellochina123456789a"到p中
printf("%s\n",p);//输出:hellochina123456789a
return 0;
}
/*
c
bc13d0
0000000000BC13D0
bc6b40
9
bc6b40
hellochina123456789a
*/
7.5 字符串常用操作函数
(1)输出字符串:puts();printf(“%s”,p);
(2)获取字符串:scanf(“%s”,p);gets§;
代码:
/*
字符串常用操作函数
*/
#include<stdio.h>
int main(){
char *p="hello";
puts(p);
printf("%s\n",p);
char str[100]={'\0'};
puts("gets输入:");
gets(str);
puts(str);
puts("scanf输入:");
scanf("%s",str);
printf("%s\n",str);
return 0;
}
//输出如下:
/*
hello
hello
gets输入:
ddd
ddd
scanf输入:
kk
kk
*/
7.6 字符串拷贝函数
(1)strcpy函数:char strcpy(char dest, const char *src);(例:strcpy(p,“hello”),将hello复制到p中);
代码:
/*
strcpy实现
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//方法一:
char* mystrcpy1(char *des,char *src){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while(*src!='\0'){
*des=*src;
des++;
src++;
}
*des='\0';
return bak;
}
//方法二:
char* mystrcpy2(char *des,char *src){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while(*src!='\0'){
*des++=*src++;//先*src后++,先*des后++
}
*des='\0';
return bak;
}
//方法三:
char* mystrcpy3(char *des,char *src){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while((*des++=*src++)!='\0');
*des='\0';
return bak;
}
int main(){
char str[100]={'\0'};
char *pstr="hello";
mystrcpy1(str,pstr);
//mystrcpy2(str,pstr);
//mystrcpy3(str,pstr);
puts(str);
return 0;
}
(2)strncpy函数:char *strncpy(char *dest, const char *src, int n)(例:strncpy(p,“hello”,2),将hello复制到p中前2个字节he复制到p中));
代码:
/*
strncpy实现
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//方法一:
char* mystrcpy1(char *des,char *src,int num){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while(*src!='\0' && num>0){
*des=*src;
des++;
src++;
num--;
}
//hello只有5个字节,截取了20个字节,超出的直接赋值'\0'
if(num>0){
while(num>0){
*des='\0';
num--;
des++;
}
}
*des='\0';
return bak;
}
//方法二:
char* mystrcpy2(char *des,char *src,int num){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while(*src!='\0'&& num>0){
*des++=*src++;//先*src后++,先*des后++
num--;
}
*des='\0';
return bak;
}
//方法三:
char* mystrcpy3(char *des,char *src,int num){
if(des==NULL || src==NULL){
return NULL;
}
char *bak=des;
while((*des++=*src++)!='\0');
*des='\0';
return bak;
}
int main(){
char str[100]={'\0'};
char *pstr="hello";
int num=2;
//mystrcpy1(str,pstr,num);
//mystrcpy2(str,pstr,num);
mystrcpy3(str,pstr,num);
puts(str);
return 0;
}
7.7 函数assert
(1)assert(条件),断言,如果条件为假,终止程序运行,打印错误信息;(例:assert(a>0);)
7.8 字符串拼接strcat
(1) strcat函数原型:char *strcat(char *dest, const char src);
将src所指字符串(含’\0’)复制到dest所指字符串(删掉dest末尾中的’\0’)中,*src中原有的字符不变。返回指向dest的指针。
代码:
/*
strcat函数实现
*/
#include<stdio.h>
#include<assert.h>
char* mystrcat(char *des,char *src){
assert(des!=NULL && src!=NULL);
char *bak=des;
while(*des !='\0'){
des++;
}
while(*src!='\0'){
*des=*src;
des++;
src++;
}
*des='\0';
return bak;
}
int main(){
char str[100]="hello ";
char *pstr="china";
mystrcat(str,pstr);
printf("%s",str);
return 0;
}
//输出:hello china
7.9 字符串比较strcmp
(1)strcmp函数原型:int strcmp(const char *s1,const char *s2);若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数
(2)strncmp函数原型:int strncmp ( const char * str1, const char * str2, size_t n ); str1 和 str2 前 n 个字节进行比较;
7.10 字符串其他函数
(1)strchr函数原型:char *strchr(const char *str, int c);用来查找子字符,返回c字符首次出现的位置;
(2)strstr函数原型:char *strstr(char *str1, const char *str2);用来查找子串,返回str2指向的字符首次出现的位置;
(3)strtok函数原型:char *strtok(char *str, const char *delim);用来字符串分割,分割处理后原字符串str会变,原字符串的改动是切分符原位置均更改为 ‘\0’;
代码:
/*
字符串相关函数使用
*/
#include<stdio.h>
#include<string.h>
int main(){
char *pstr1="hello";
char *pstr2="hello";
char str3[100]="i-am-chinese";
int value;
char *pos,*pos2;
char *tok;
value=strcmp(pstr1,pstr2);//字符串比较
pos=strchr(pstr1,'e'); //字符串中某字符第一次出现时的位置
pos2=strstr(pstr1,"llo");//字符串中某子字符串第一次出现时的位置
tok=strtok(str3,"-");
printf("%d\n",value);//输出:0
printf("%x\n",pos);//输出:404001
printf("%x\n",pos2);//输出:404002
while(tok!=NULL){
printf("%s\n",tok);
tok=strtok(NULL,"-");
}
return 0;
}
//输出如下:
/*
0
404001
404002
i
am
chinese
*/
8 结构体
8.1 结构体引入
(1)引入原因:多种类型数据作为一个整体的数据集合
(2)定义:struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};每个成员都是结构体中的一个域,也称为成员变量;
8.2 定义结构体和使用变量
(1)案例:定义一个学生信息结构体。
代码:
/*
结构体定义及引用;
定义一个学生信息结构体;
*/
#include<stdio.h>
#include<string.h>
struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};
int main(){
struct Student stu1;
stu1.num=1;
strcpy(stu1.name,"张三");
stu1.sex='M';
stu1.age=20;
stu1.score=90.5;
strcpy(stu1.addr,"北京");
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s",stu1.num,stu1.name,stu1.sex,stu1.age,stu1.score,stu1.addr);
return 0;
}
8.3 应用
(1)成员变量赋值分2种,一种是成员变量一个个赋值(例:stu1.score=90.5;),另一种定义的时候一次性给所有成员变量赋值(例:struct Student stu2={2,“小玉”,‘F’,19,99,“深圳”}😉;
(2)案例:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息。
代码:
/*
案例:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息。
*/
#include<stdio.h>
#include<string.h>
struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};
int main(){
struct Student stu1;
struct Student stu2={2,"小玉",'F',19,99,"深圳"};
struct Student stuMaxScore;
stuMaxScore=stu1;
stu1.num=1;
strcpy(stu1.name,"张三");
stu1.sex='M';
stu1.age=20;
stu1.score=90.5;
strcpy(stu1.addr,"北京");
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",stu1.num,stu1.name,stu1.sex,stu1.age,stu1.score,stu1.addr);
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",stu2.num,stu2.name,stu2.sex,stu2.age,stu2.score,stu2.addr);
if(stu1.score<stu2.score){
stuMaxScore=stu2;
}
printf("成绩最好的学生是->学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",stuMaxScore.num,stuMaxScore.name,stuMaxScore.sex,stuMaxScore.age,stuMaxScore.score,stuMaxScore.addr);
return 0;
}
8.4 结构体和数组结合
(1)结构体数组的定义及使用;(
例:struct Student arr[3]={
{1,“张三”,‘M’,20,90.5,“北京”},
{2,“小玉”,‘F’,19,99,“深圳”},
{3,“李四”,‘M’,19,89,“上海”}
};
)
代码:
/*
结构体数组
*/
#include<stdio.h>
#include<string.h>
struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};
int main(){
struct Student arr[3]={
{1,"张三",'M',20,90.5,"北京"},
{2,"小玉",'F',19,99,"深圳"},
{3,"李四",'M',19,89,"上海"}
};
int len=sizeof(arr)/sizeof(arr[0]);
int i;
for(i=0;i<len;i++){
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",arr[i].num,arr[i].name,arr[i].sex,arr[i].age,arr[i].score,arr[i].addr);
}
return 0;
}
8.5 结构体数组应用之选票系统
(1)结构体数组-选票系统
代码:
/*
结构体数组-选票系统
*/
#include<stdio.h>
#include<string.h>
struct XuanPiao{
char name[32];
int tikets;
};
int main(){
struct XuanPiao xm[3];
struct XuanPiao max;
int len=sizeof(xm)/sizeof(xm[0]);
int i;
int j;
char tmpName[32];
int invalTickets;
int mark=0;
for(i=0;i<len;i++){
xm[i].tikets=0;
printf("请输入第%d被选人员名字:\n",i+1);
scanf("%s",xm[i].name);
}
for(i=0;i<5;i++){
int mark=0;
puts("请输入要投给谁:");
memset(tmpName,'\0',sizeof(tmpName));//每次清空一下
scanf("%s",tmpName);
for(j=0;j<len;j++){
if(strcmp(tmpName,xm[j].name)==0){//strcmp进行字符串比较
xm[j].tikets++;
mark=1;
}
}
if(mark==0){
puts("无此人,废票");
invalTickets++;
}
}
for(i=0;i<len;i++){
printf("%s的选票为%d\n",xm[i].name,xm[i].tikets);
}
max=xm[0];
for(i=1;i<len;i++){
if(max.tikets<xm[i].tikets){
max=xm[i];
}
}
printf("%s以%d张票当选,其中弃票%d张\n",max.name,max.tikets,invalTickets);
return 0;
}
8.6 结构体指针变量
(1)指针变量就是存放地址的变量,变量可以通过"变量名"或"地址"访问;
(2)结构体指针变量就用来保存结构体变量的地址(struct Student *stu1;);
代码:
/*
结构体指针变量定义及使用
*/
#include<stdio.h>
struct Student{
char name[32];
float scores;
};
int main(){
struct Student stu1={"张三",90};
struct Student *pstu1=&stu1;
printf("stu1是:%s,分数:%f\n",stu1.name,stu1.scores);
printf("stu1是:%s,分数:%f\n",pstu1->name,pstu1->scores);
return 0;
}
8.7 结构体指针访问结构体内容
(1)普通变量访问结构体内容(例:stu.name);
(2)指针变量访问结构体内容(例:pstu->name);
8.8 结构体指针应用
(1)结构体数组输出,结构体数组指针输出;
代码:
/*
结构体数组指针
*/
#include<stdio.h>
#include<string.h>
struct Student{
int num;
char name[32];
char sex;
int age;
double score;
char addr[32];
};
int main(){
struct Student arr[3]={
{1,"张三",'M',20,90.5,"北京"},
{2,"小玉",'F',19,99,"深圳"},
{3,"李四",'M',19,89,"上海"}
};
int len=sizeof(arr)/sizeof(arr[0]);
int i;
struct Student *p;
p=arr;
//结构体数组输出
for(i=0;i<len;i++){
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",arr[i].num,arr[i].name,arr[i].sex,arr[i].age,arr[i].score,arr[i].addr);
}
//结构体数组指针输出
for(i=0;i<len;i++){
printf("学号:%d,姓名:%s,性别:%c,年龄:%d,分数:%.2lf,地址:%s\n",p->num,p->name,p->sex,p->age,p->score,p->addr);
p++;
}
return 0;
}
8.9 结构体指针应用之改写票选系统
(1)结构体指针选票系统案例(注:对指针操作完后需再次操作,需重新初始化指向最初地址)
代码:
/*
结构体数组指针-选票系统
*/
#include<stdio.h>
#include<string.h>
struct XuanPiao{
char name[32];
int tickets;
};
int main(){
struct XuanPiao xm[3];
struct XuanPiao max;
int len=sizeof(xm)/sizeof(xm[0]);
int i;
int j;
char tmpName[32];
int invalTickets;
int mark=0;
struct XuanPiao *p;
p=xm;
for(i=0;i<len;i++){
p->tickets=0;
printf("请输入第%d被选人员名字:\n",i+1);
scanf("%s",p->name);
p++;
}
for(i=0;i<5;i++){
int mark=0;
puts("请输入要投给谁:");
memset(tmpName,'\0',sizeof(tmpName));//每次清空一下
scanf("%s",tmpName);
p=xm;
for(j=0;j<len;j++){
if(strcmp(tmpName,p->name)==0){//strcmp进行字符串比较
p->tickets++;
mark=1;
}
p++;
}
if(mark==0){
puts("无此人,废票");
invalTickets++;
}
}
p=xm;
for(i=0;i<len;i++){
printf("%s的选票为%d\n",p->name,p->tickets);
p++;
}
max=xm[0];
p=xm;
for(i=1;i<len;i++){
if(max.tickets<p->tickets){
max=xm[i];
}
p++;
}
printf("%s以%d张票当选,其中弃票%d张\n",max.name,max.tickets,invalTickets);
return 0;
}
8.10 结构体指针数组函数综合应用改写票选系统
(1)结构体指针数组函数综合之票选系统案例(仅含一级指针,封装有返回值的函数)(printf(“%s以%d票当选,废票为%d\n”,final->name,final->tickets,feipiao);//%d票中占位符若写成了s%会出现段错误)
代码:
/*
结构体指针数组函数综合之票选系统案例(仅含一级指针,封装无返回值的函数)
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct XuanPiao{
char name[32];
int tickets;
};
struct XuanPiao* initXms(struct XuanPiao *p,int *pn){
int i;
if(p==NULL){
printf("请输入有多少个参选人\n");
scanf("%d",pn);
p=(struct XuanPiao*)malloc(*pn*sizeof(struct XuanPiao));
}
for(i=0;i<*pn;i++){
p->tickets=0;
printf("请输入第%d号参选人名字\n",i+1);
scanf("%s",p->name);
p++;
}
return p-*pn;
}
void printfXms(struct XuanPiao *p,int len){
int i;
for(i=0;i<len;i++){
printf("名字:%s,票数:%d\n",p->name,p->tickets);
p++;
}
}
int doVot(struct XuanPiao *p,int len){
int i;
int j;
int feipiao=0;
int mark;
char tmpName[32];
struct XuanPiao *pbak=p;
for(i=0;i<5;i++){
mark=0;
printf("请输入要投给谁:\n");
memset(tmpName,'\0',sizeof(tmpName));
scanf("%s",tmpName);
p=pbak;
for(j=0;j<len;j++){
if(strcmp(tmpName,p->name)==0){
p->tickets++;
mark=1;
}
p++;
}
if(mark==0){
printf("无此人,为废票\n");
feipiao++;
}
}
return feipiao;
}
struct XuanPiao* getMax(struct XuanPiao *p,int len){
struct XuanPiao *max;
max=p;
int i;
for(i=0;i<len;i++){
if(max->tickets < p->tickets){
max=p;
}
p++;
}
return max;
}
int main(){
struct XuanPiao *xm=NULL;
struct XuanPiao *final;
int total=0;
xm=initXms(xm,&total);
printfXms(xm,total);
int feipiao=doVot(xm,total);
printf("废票数为%d\n",feipiao);
printfXms(xm,total);
final=getMax(xm,total);
printf("%s以%d票当选,废票为%d\n",final->name,final->tickets,feipiao);//%d票中占位符若写成了s%会出现段错误
return 0;
}
8.11 结构体二级指针
(1)结构体指针数组函数综合之票选系统案例(含二级指针struct XuanPiao **p,封装无返回值的函数)
a.二级指针保存的是指针变量的地址
b. 由于p是二级指针,*p存储的是一级指针变量的地址,所以(*p)++;//注意用括号保证优先级,(*p)=(*p)-(*pn);//注意用括号保证优先级
代码:
/*
结构体指针数组函数综合之票选系统案例(含二级指针,封装有返回值的函数)
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct XuanPiao{
char name[32];
int tickets;
};
void initXms(struct XuanPiao **p,int *pn){
int i;
if(*p==NULL){
printf("请输入有多少个参选人\n");
scanf("%d",pn);
*p=(struct XuanPiao*)malloc(*pn*sizeof(struct XuanPiao));
}
for(i=0;i<*pn;i++){
(*p)->tickets=0;
printf("请输入第%d号参选人名字\n",i+1);
scanf("%s",(*p)->name);
(*p)++;//注意用括号保证优先级
}
(*p)=(*p)-(*pn);//注意用括号保证优先级
}
void printfXms(struct XuanPiao *p,int len){
int i;
for(i=0;i<len;i++){
printf("名字:%s,票数:%d\n",p->name,p->tickets);
p++;
}
}
int doVot(struct XuanPiao *p,int len){
int i;
int j;
int feipiao=0;
int mark;
char tmpName[32];
struct XuanPiao *pbak=p;
for(i=0;i<5;i++){
mark=0;
printf("请输入要投给谁:\n");
memset(tmpName,'\0',sizeof(tmpName));
scanf("%s",tmpName);
p=pbak;
for(j=0;j<len;j++){
if(strcmp(tmpName,p->name)==0){
p->tickets++;
mark=1;
}
p++;
}
if(mark==0){
printf("无此人,为废票\n");
feipiao++;
}
}
return feipiao;
}
struct XuanPiao* getMax(struct XuanPiao *p,int len){
struct XuanPiao *max;
max=p;
int i;
for(i=0;i<len;i++){
if(max->tickets < p->tickets){
max=p;
}
p++;
}
return max;
}
int main(){
struct XuanPiao *xm=NULL;
struct XuanPiao *final;
int total=0;
initXms(&xm,&total);
printfXms(xm,total);
int feipiao=doVot(xm,total);
printf("废票数为%d\n",feipiao);
printfXms(xm,total);
final=getMax(xm,total);
printf("%s以%d票当选,废票为%d\n",final->name,final->tickets,feipiao);//%d票中占位符若写成了s%会出现段错误
return 0;
}
8.12 联合体共用体概念
(1)联合体概念:不同类型的数据共用一个内存空间;(
union TestUnion{
int intData;
char charData;
}😉
(2)联合体和结构体区别:结构体元素有各自的空间(空间大小由各元素类型所占空间之和),联合体元素共用一个内存空间(空间大小由元素最大类型所占空间决定);
(3)案例:定义一个联合体,输出联合体跟结构体地址所占空间差异;
代码:
/*
定义一个联合体,输出联合体跟结构体地址所占空间差异;
*/
#include<stdio.h>
struct TestStruct{
int intData;
char charData;
};
union TestUnion{
int intData;
char charData;
};
int main(){
struct TestStruct data1;
union TestUnion data2;
printf("结构体TestStruct大小为:%d\n",sizeof(data1));//输出:8
printf("结构体TestStruct大小为:%d\n",sizeof(struct TestStruct));//输出:8
printf("结构体TestStruct中intData的地址为:%p\n",data1.intData);//输出:0000000000000010
printf("结构体TestStruct中intData的地址为:%p\n",data1.charData);//输出:0000000000000000
printf("联合体TestUnion大小为:%d\n",sizeof(data2));//输出:4
printf("联合体TestUnion大小为:%d\n",sizeof(union TestUnion));//输出:4
printf("联合体TestUnion中intData的地址为:%p\n",data2.intData);//输出:0000000000000000
printf("联合体TestUnion中intData的地址为:%p\n",data2.charData);//输出:0000000000000000
}
8.13 共用体的数据覆盖问题
(1)联合体是不同类型数据共用一块内存空间,就存在数据覆盖问题;
(2)案例:联合体数据覆盖(简单多次赋值覆盖)
代码:
/*
联合体数据覆盖(简单多次赋值覆盖)
*/
#include<stdio.h>
struct TestStruct{
int intData;
char charData;
};
union TestUnion{
int intData;
char charData;
};
int main(){
struct TestStruct data1;
union TestUnion data2;
data1.intData=8;
data1.charData='A';
data2.intData=8;
data2.charData='A';
printf("结构体TestStruct大小为:%d\n",sizeof(data1));//输出:8
printf("结构体TestStruct大小为:%d\n",sizeof(struct TestStruct));//输出:8
printf("结构体TestStruct中intData的地址为:%p,值为:%d\n",&data1.intData,data1.intData);//输出:000000000061FE18,值为:8
printf("结构体TestStruct中intData的地址为:%p,值为:%c\n",&data1.charData,data1.charData);//输出:000000000061FE1C,值为:A
printf("联合体TestUnion大小为:%d\n",sizeof(data2));//输出:4
printf("联合体TestUnion大小为:%d\n",sizeof(union TestUnion));//输出:4
printf("联合体TestUnion中intData的地址为:%p,值为:%d\n",&data2.intData,data2.intData);//输出:000000000061FE14,值为:65
printf("联合体TestUnion中intData的地址为:%p,值为:%c\n",&data2.charData,data2.charData);//输出:000000000061FE14,值为:A
}
8.14 共用体开发
(1)共同体案例:老师学生共用一张表,其中老师的职务,学生的班级共用一个内存空间;
代码:
/*
共同体案例:老师学生共用一张表,其中老师的职务,学生的班级共用一个内存空间;
学生数据包含:姓名,号码,性别,职业,班级
老师数据包含:姓名,号码,性别,职业,职务
*/
#include<stdio.h>
struct Person{
char name[32];
int numb;
int age;
char sex;
char job;
union {int class;char pos[32];} mes;
};
int main(){
struct Person p[2];
int i;
for(i=0;i<2;i++){
puts("请输入职业:");
scanf("%c",&p[i].job);
if(p[i].job=='s'){
puts("请输入名字:");
scanf("%s",p[i].name);
puts("请输入班级:");
scanf("%d",&p[i].mes.class);
}else{
puts("请输入名字:");
scanf("%s",p[i].name);
puts("请输入学科:");
scanf("%s",p[i].mes.pos);
}
getchar();
};
for(i=0;i<2;i++){
if(p[i].job=='s'){
printf("姓名:%s,班级:%d\n",p[i].name,p[i].mes.class);
}else{
printf("姓名:%s,学科:%s\n",p[i].name,p[i].mes.pos);
}
}
return 0;
}
8.15 枚举类型
(1)枚举概念:一个变量只有几种可能的值;
(2)枚举的定义:enum WeekDay{sun,mon,tue,wed,thu,fri,sat}; enum WeekDay day; day=mon;
(3)可忽略枚举名,直接定义枚举变量;
enum{sun,mon,tue,wed,thu,fri,sat} day;
(4)枚举值默认从0开始,枚举元素不能被赋值;
(5)枚举定义案例
代码:
/*
枚举定义案例
*/
#include<stdio.h>
enum WeekDay{sun,mon,tue,wed,thu,fri,sat};
int main(){
enum WeekDay day;
day=sat;
printf("day=%d\n",day);
return 0;
}
//输出:day=6
8.16 typedef关键字
(1)typedef概念:给已有的变量类型重新起名;
(2)typedef定义:typedef int zhengshu;zhengshu a=2;
(3)typedef定义案例
代码:
/*
typedef定义案例
*/
#include<stdio.h>
typedef int zs;
typedef char zf;
typedef float fds;
struct Test{
int test1;
char test2;
};
typedef struct {
int test1;
char test2;
} T2;
typedef struct Test T;
int main(){
zs data1=2;
zf data2='A';
fds data3=3.45;
T t1;
t1.test1=8;
t1.test2='b';
T2 t2;
t2.test1=9;
t2.test2='c';
printf("data1=%d,data2=%c,data3=%.2f\n",data1,data2,data3);
printf("%d,%c\n",t1.test1,t1.test2);
printf("%d,%c\n",t2.test1,t2.test2);
return 0;
}
//输出:
/*
data1=2,data2=A,data3=3.45
8,b
9,c
*/
8.17 typedef和结构体案例
(1)typedef和结构体的结合运用案例;(
typedef struct{
int num;
char name[32];
char sex;
}Person;)
代码:
/*
typedef和结构体的结合运用
*/
#include<stdio.h>
typedef struct{
int num;
char name[32];
char sex;
}Person,*PPerson;
int main(){
Person p1={1,"张三",'M'};
PPerson pp1=&p1;
printf("序号:%d,姓名:%s,性别:%c\n",p1.num,p1.name,p1.sex);
printf("序号:%d,姓名:%s,性别:%c\n",pp1->num,pp1->name,pp1->sex);
return 0;
}
//输出:
/*
序号:1,姓名:张三,性别:M
序号:1,姓名:张三,性别:M
*/
9 链表
9.1 认识链表
(1)数组中元素地址联系,不便于增删,链表中元素地址灵活,上个元素指向下一个元素地址;
9.2 链表和数组的区别与实现
(1)数组是一串数据,链表中数据是分散的,通过链表中的指针连接在一块;
(2)案例:分别用数组,链表输出数据;
#include<stdio.h>
struct Test{
int data;
struct Test *next;
};
int main(){
int i;
int array[]={1,2,3};
//数组输出数据
for(i=0;i<sizeof(array)/sizeof(array[0]);i++){
printf("%d ",array[i]);
}
putchar('\n');
struct Test t1={1,NULL};
struct Test t2={2,NULL};
struct Test t3={3,NULL};
t1.next=&t2;
t2.next=&t3;
printf("use t1 to print three nums\n");
// 链表输出数据
printf("%d %d %d\n",t1.data,t1.next->data,t1.next->next->data);
return 0;
}
9.3 链表静态添加和动态遍历
(1)链表就是原先结构体含有的数据类型中多了一个指针,该指针指向自己同类型的结构体;
(2)案例:链表输出数据(函数输出);
#include<stdio.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
int main(){
int i;
int array[]={1,2,3,4};
for(i=0;i<sizeof(array)/sizeof(array[0]);i++){
printf("%d ",array[i]);
}
putchar('\n');
struct Test t1={1,NULL};
struct Test t2={2,NULL};
struct Test t3={3,NULL};
struct Test t4={4,NULL};
t1.next=&t2;
t2.next=&t3;
t3.next=&t4;
printf("use t1 to print three nums\n");
//printf("%d %d %d %d\n",t1.data,t1.next->data,t1.next->next->data,t1.next->next->next->data);
printLink(&t1);
putchar('\n');
return 0;
}
9.4 链表遍历中的point=point->next
(1)结构体指针struct Test *point中point为地址,point->next为链表下个元素的地址;
9.5 统计链表节点个数及链表查找
(1)链表节点个数及链表查找案例(分别定义函数统计个数及查找即可)
#include<stdio.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装统计链表节点个数函数
int getLinkTotalNodeNum(struct Test *head){
int cnt=0;
while(head !=NULL){
cnt++;
head=head->next;
}
return cnt;
}
//封装链表查找函数
int searchLink(struct Test *head,int data){
while(head !=NULL){
if(head->data==data){
return 1;
}
head=head->next;
}
return 0;
}
int main(){
int i;
int array[]={1,2,3,4};
for(i=0;i<sizeof(array)/sizeof(array[0]);i++){
printf("%d ",array[i]);
}
putchar('\n');
struct Test t1={1,NULL};
struct Test t2={2,NULL};
struct Test t3={3,NULL};
struct Test t4={4,NULL};
t1.next=&t2;
t2.next=&t3;
t3.next=&t4;
printf("use t1 to print four nums\n");
//printf("%d %d %d %d\n",t1.data,t1.next->data,t1.next->next->data,t1.next->next->next->data);
printLink(&t1);
putchar('\n');
int ret=getLinkTotalNodeNum(&t1);
printf("total node number:%d\n",ret);
ret=searchLink(&t1,4);
if(ret==1){
printf("have number!\n");
}else{
printf("no number!\n");
}
return 0;
}
9.6 链表从指定节点后方插入新节点
(1)找到指定节点(p->data==data)-》新节点的下一个指向指定节点原先指向的地址(new->next=p->next)-》指定节点指向新节点地址(p->next=new)
/*
找到指定节点(p->data==data)-》新节点的下一个指向指定节点原先指向的地址(new->next=p->next)-》指定节点指向新节点地址(p->next=new)
*/
#include<stdio.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装链表从指定节点后方插入新节点
int insertFromBehind(struct Test *head,int data,struct Test *new){
struct Test *p=head;
while(p->next!=NULL){
if(p->data==data){
new->next=p->next;
p->next=new;
return 1;
}
p=p->next;
}
return 0;
}
int main(){
struct Test t1={1,NULL};
struct Test t2={2,NULL};
struct Test t3={3,NULL};
struct Test t4={4,NULL};
t1.next=&t2;
t2.next=&t3;
t3.next=&t4;
printf("insert from behind:\n");
struct Test new={100,NULL};
insertFromBehind(&t1,3,&new);
printLink(&t1);
putchar('\n');
return 0;
}
9.7 链表从指定节点前方插入新节点
(1)情况1:第一个节点为指定节点,在第一个节点前方插入新节点;
(2)情况2:其他节点为指定节点,在其他节点前方插入新节点;
#include<stdio.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装链表从指定节点前方插入新节点函数
struct Test* insertFromFront(struct Test *head,int data, struct Test *new){
struct Test *p=head;
//情况1:第一个节点为指定节点,在第一个节点前方插入新节点
if(p->data==data){
new->next=head;
return new;
}
//情况2:其他节点为指定节点,在其他节点前方插入新节点
while(p->next!=NULL){
if(p->next->data==data){
new->next=p->next;
p->next=new;
return head;
}
p=p->next;
}
return head;
}
int main(){
struct Test t1={1,NULL};
struct Test t2={2,NULL};
struct Test t3={3,NULL};
struct Test t4={4,NULL};
t1.next=&t2;
t2.next=&t3;
t3.next=&t4;
struct Test new={100,NULL};
struct Test *head=&t1;
head=insertFromFront(head,3,&new);
puts("insert from front:\n");
printLink(head);
putchar('\n');
return 0;
}
9.8 链表删除指定节点
(1)分为情况1:删除第一个节点;情况2:删除其他节点;
(2)man 3 free(手册3中查free的需调#include<stdlib.h>),gcc xxx.c -g -》gdb a.out->r(查问题);
(3)用struct Test p=(struct Test)malloc(sizeof(struct Test))开辟内存空间并转结构体指针类型;删除动态变量节点就需要释放内存空间free§;
#include<stdio.h>
#include<stdlib.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装链表删除指定节点函数
struct Test* deleteNode(struct Test *head,int data){
struct Test *p=head;
if(p->data==data){
head=p->next;
//用struct Test *p=(struct Test*)malloc(sizeof(struct Test))开辟内存空间并转结构体指针类型;删除动态变量节点就需要释放内存空间free(p)
free(p);
return head;
}
while(p->next!=NULL){
if(p->next->data==data){
p->next=p->next->next;
return head;
}
p=p->next;
}
return head;
}
int main(){
struct Test *head=NULL;
struct Test *p=(struct Test*)malloc(sizeof(struct Test));
struct Test t2={2,NULL};
struct Test t3={3,NULL};
struct Test t4={4,NULL};
p->data=1;
p->next=&t2;
t2.next=&t3;
t3.next=&t4;
head=p;
head=deleteNode(head,1);
printLink(head);
putchar('\n');
return 0;
}
9.9 链表动态创建之头插法
(1)从链表头部一个个插入节点,动态创建新节点-》输入新节点值-》新节点值判断-》老节点为NULL或老节点不为NULL情况头插新节点
/*
从链表头部一个个插入节点,动态创建新节点-》输入新节点值-》新节点值判断-》老节点为NULL或老节点不为NULL情况头插新节点
*/
#include<stdio.h>
#include<stdlib.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装链表头插法函数
struct Test* insertHead(struct Test* head){
struct Test *new;
while(1){
new=(struct Test*)malloc(sizeof(struct Test));
printf("input your new node data:\n");
scanf("%d",&(new->data));
if(new->data==0){
printf("input data is 0 then quit\n");
return head;
}
if(head==NULL){
head=new;
}else{
new->next=head;
head=new;
}
}
return head;
}
int main(){
struct Test *head=NULL;
head=insertHead(head);
printLink(head);
putchar('\n');
return 0;
}
9.10 头插法优化补充
(1)创建链表跟头部插入节点分别封装函数;
#include<stdio.h>
#include<stdlib.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装头部插入节点函数
struct Test* insertHead(struct Test* head,struct Test* new){
if(head==NULL){
head=new;
}else{
new->next=head;
head=new;
}
return head;
}
//封装创建链表函数(头插创建)
struct Test* createLink(struct Test* head){
struct Test *new;
while(1){
new=(struct Test*)malloc(sizeof(struct Test));
printf("input your new node data:\n");
scanf("%d",&(new->data));
if(new->data==0){
printf("input data is 0 then quit\n");
free(new);
return head;
}
head=insertHead(head,new);
}
}
int main(){
struct Test *head=NULL;
head=createLink(head);
struct Test t1={1000,NULL};
head=insertHead(head,&t1);
printLink(head);
putchar('\n');
return 0;
}
9.11 尾插法创建链表
(1)创建链表(可用头插创建也可用尾插创建)跟尾部插入节点分别封装函数;
#include<stdio.h>
#include<stdlib.h>
struct Test{
int data;
struct Test *next;
};
//封装链表输出函数
void printLink(struct Test *head){
struct Test *point;
point=head;
while(point!=NULL){
printf("%d ",point->data);
point=point->next;
}
}
//封装尾部插入节点函数
struct Test* insertBehind(struct Test* head,struct Test* new){
struct Test* p=head;
if(head==NULL){
head=new;
return head;
}
while(p->next!=NULL){
p=p->next;
}
p->next=new;
return head;
}
//封装创建链表函数(尾插创建)
struct Test* createLink(struct Test* head){
struct Test *new;
while(1){
new=(struct Test*)malloc(sizeof(struct Test));
printf("input your new node data:\n");
scanf("%d",&(new->data));
if(new->data==0){
printf("input data is 0 then quit\n");
free(new);
return head;
}
head=insertBehind(head,new);
}
}
int main(){
struct Test *head=NULL;
head=createLink(head);
struct Test t2={2000,NULL};
head=insertBehind(head,&t2);
printLink(head);
putchar('\n');
return 0;
}