C语言基础

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)看视频,提取出要点,理解代码,默写形成肌肉记忆,多调试代码,多总结(输出至CSDN博客,及思维导图);

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;
}
  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值