C语言学习笔记

常用的输入输出函数

printf():

printf:字面理解由“ print ”和“ f ”组成(“ f ”代表“ format ”格式化),其作用是将参数文本输出到屏幕上,并定制化输出文本的格式,其中文本格式就由占位符来决定。

printf()常用的%占位符

  • 十位
  • 单个字符
  • 多个字符
  • 小数

m.nf

%-m.nf

  •  x 以16进制的格式输出
  • p 般打印内存地址,也是16进制格式输出,输出地址,取变量地址的运算符号&

示例:

#include<stdio.h>

int main()
{
	int	data1 = 12;
	int	data2 =	8;
	int	data3;
	float data4;
	
	printf("有两个原始数据%d和%d\n",data1,data2);
	
	data3=data1+data2;
	printf("那么这两个数的和是%d\n",data3);
	
	data3=data1-data2;
	printf("那么这两个数的差是%d\n",data3);
	
	data3=data1*data2;
	printf("那么这两个数的积是%d\n",data3);
	
	data4=(float)data1/data2;
	printf("那么这两个数的除是%f\n",data4);
	
	
	return 0;
}

:两个整形的数相除有小数时,要强制转换成浮点型

强制转换的目的:C语言是强类型语言,如果一个运算符两遍的运算数据类型不同,先要将其转换为相同的类型,强制类型转换可以消除程序中的警告,即确保写代码的程序员自己清楚类型转换,允许丢失一定精度,或做类型匹配。

scanf():

scanf()的功能用一句话来概括就是“通过键盘给程序中的变量赋值”

#include<stdio.h>

int main()
{
	int data1 ,data2,data3;
	
	printf("请输入三个数\n");
	scanf("%d %d %d",&data1,&data2,&data3);
	printf("data1=%d\ndata2=%d\ndata3=%d\n",\
	data1,data2,data3);
	
	return 0;
}

scanf()使用注意事项:

原样输入:如果这样写scanf("data=%d",&data);那么在结果输出的时候也要输入data=加想输入的值输入(逗号也算但是空格可以

混合输入:如果有三个字符scanf("%c%c%c",&data);那么结果输入A B C时会错误,因为空格也算字符(回车也不行)

特别注意:当你连续多次scanf()获得字符时,要注意回车 

#include<stdio.h>

int main()
{
	char A='A';
	char B='B';
	char a='a';
	char b='b';
	char c;
	
	printf("%c,%c,%c,%c\n",a,b,A,B);
	printf("%d,%d,%d,%d\n",a,b,A,B);
	
    puts("请输入一个大写字母");
	scanf("%c",&c);           //scanf在一次输入结束后,不会舍弃最后的回车符(即回车符会残留在数据缓冲区中)
	printf("%c\n",c+32);      //%c会读回车 把他视为一个字符,%d不会读,所以%c会出现吃回车现象
	puts("请输入一个小写字母");
 	getchar();                //getchar() 可以吸收回车符,来源于scanf()
	putchar(getchar()-32);    
//  scanf("%c",&c);
//	printf("%c\n",c-32);      //方法2
    return 0;
}

别的输入输出函数

puts():

作用:将一个字符串(以‘\0’结束的字符序列)输出到终端

和printf的区别:

  1. 自动加入换行符
  2. printf支持多种花样输出,而puts就是输出字符串

getchar():

getchar()函数原型:int getchar(void)

用于从标准输入输出控制台读取字符

putchar():

putchar()原型:int putchar(int ch)

用于将给定的字符输出控制台

【参数】ch为要输出的字符。

【返回值】输出成功,返回该字符的ASCII码值,否则返回EOF。

#include<stdio.h>

int main()
{
	char c;
	
	puts("请输入一个字符");
	c = getchar();
	puts("你输入的字符是");
	putchar(c);
	
	return 0;
}

常用的流程控制函数

if控制经典案例代数法交换值

#include<stdio.h>

int main()
{
	int data1,data2,datatemp;
	
	puts("请输入两个数");
	scanf("%d %d",&data1,&data2);
	if(data1>data2){         //不管怎么输入数据,data1当中存放的就是小数
	datatemp = data2;
	data2 = data1;
	data1 = datatemp;
	}
	printf("data1=%d,data2=%d\n",data1,data2);
	
	return 0;
}

switch应用

#include<stdio.h>

int main()
{
	int score;
	puts("请输入您的成绩");
	scanf("%d",&score);
	score=score/10;
	switch(score){
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		printf("E");
		break;
		case 6:
		printf("D");
		break;
		case 7:
		printf("C");
		break;
		case 8:
		printf("B");
		break;
		case 9:
		case 10:
		printf("A");
		break;
		default:
		break;
	}
	return 0;
}

do{}while{}

#include<stdio.h>

int main()
{
	int sum=0;	//变量初始值最好为0,编程习惯
	int data=1;	
	do{	
		sum=sum+data;
		data++;			//条件发生变化
	}while(data!=101);//条件发临界值
	printf("1加到100的值为%d",sum);
	return 0;
}

这个循环与 while 循环的不同在于:它先执行循环中的语句,然后再判断表达式是否为 真, 如果为真则继续循环;如果为假, 则终止循环。因此, do-while 循环至少要执行一次 循环语句

break应用

#include<stdio.h>

int main()
{
	int sumOfMoney=0;
	int money;
	int numOfDonors=1;
	for(numOfDonors;numOfDonors<=1000;numOfDonors++){
		puts("请输入捐款数额:");	
		scanf("%d",&money);
		sumOfMoney=sumOfMoney+money;
		if(sumOfMoney>=100000){
			puts("很棒已经达到10W元");
			break;									//达到10W跳出for()循环
		}
	}
	printf("此时捐款人数为%d人\n",numOfDonors);
	printf("平均每人捐款%f元\n",(float)sumOfMoney/numOfDonors);
	return 0;
}

 求两个数的最小公约数和最大公倍数(暴力求解法)

#include<stdio.h>

int main()
{
	int a;
	int b;
	puts("请输入两个数");
	scanf("%d%d",&a,&b);
	int temp=a<b?a:b;	
	while(1){
		if(a%temp==0&&b%temp==0){
			break;
		}
		temp--;
	}
	printf("两个数的最小公倍数为%d\n",temp);
    temp=a>b?a:b;	
	while(1){
		if(temp%a==0&&temp%b==0){
			break;
		}
		temp++;
	}
	printf("两个数的最大公约数为%d",temp);

 continue应用(输出100~200之间能被3整除的数)

#include<stdio.h>

int main()
{
	int data;
	for(data=100;data<=200;data++){
		if(data%3 != 0){
			continue;				//不能被3整除进入下一个循环
		}
		printf("%d  ",data);
	}
	return 0;
}

水仙花数

#include<stdio.h>

int j,k,l;
	for(int i=100;i<1000;i++){
		j=i/100;
		k=i/10%10;
		l=i%10;
		if(i!=j*j*j+k*k*k+l*l*l){
			continue;
		}
		printf("%d\n",i);
	}

数组

sizeof(): 关键字能计算括号中数据内存空间的大小

例子:

int size;	
int array[]={1,2,3,4,5,6,7};
//把整个数组的大小除以数组中一个元素的大小,获得总个数
size=sizeof(array)/sizeof(array[0]);

冒泡排序法:

从小到大排列:18,13,50,14

i\j012max
013,18,50,1413,18,50,1413,18,14,5050
113,18,1413,14,1818
213,14
#include<stdio.h>

int main()
{
	int array[] ={18 ,13 ,50, 14};
	int size ;
	int temp;
	
	size = sizeof(array)/sizeof(array[0]);
	for(int i=0;i<size-1;i++){
		for(int j=0;j<size-1-i;j++){
			if(array[j] > array[j+1]){
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] =temp;
			}
		}
	}
	for(int i=0;i<size;i++){
		printf("%d ",array[i]);
	}
	return 0;
}

 选择排序法:

从大到小排列:18,13,50,14

012max
018,13,50,1450,13,18,1450,13,18,14a[0]=50
118,13,1418,13,14a[1]=18
214,13a[2]=14
#include<stdio.h>

int main()
{
	int array[] ={18 ,13 ,50, 14};
	int size ;
	int temp;
	
	size = sizeof(array)/sizeof(array[0]);
	for(int i=0;i<size-1;i++){
		for(int j=i+1;j<size;j++){
			if(array[i] < array[j]){
				temp = array[j];
				array[j] = array[i];
				array[i] =temp;
			}
		}
	}
	for(int i=0;i<size;i++){
		printf("%d ",array[i]);
	}
	return 0;
}

二维数组应用

#include<stdio.h>

int main()
{
	int array[4][5]={{25,17,29,13,12},
					{18,20,14,11,16},
					{15,27,19,23,30},
					{28,10,24,21,26}};//有4行5列
	int max;
	max = array[0][0];
	int row = 0;
	int column = 0;
	for(int i=0;i<4;i++){
		for(int j=0;j<5;j++){
			printf("%d \t",array[i][j]);
		}
		printf("\n");
	}
	for(int i=0;i<4;i++){
		for(int j=0;j<5;j++){
			if(max<array[i][j]){
				max=array[i][j];
				row = i;
				column = j;
			}
		}
	}
	printf("最高的同学在第%d行,第%d列,最大%d\n",row+1,column+1,max);
	return 0;
}

函数 

输入两个整数,要救输出其较大的值

#include<stdio.h>

float getMaxFromTwoData(float x, float y)
{
	float z;
	z=x>y?x:y;
	return z;
}
float main()
{
	float data1,data2,bigOne;
	printf("请输入两个数\n");
	scanf("%f%f",&data1,&data2);
	bigOne=getMaxFromTwoData(data1,data2);
	printf("两个数是%.2f和%.2f,大数为%.2f",data1,data2,bigOne);
	return 0;
}

递归 

【例题1】
有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁。问第4个学生多少岁,他说比第3个学生大2岁。问第3个学生多少岁,他说比第2个学生大2岁。问第2个学生多少岁,他说比第1个学生大2岁。最后问第1个学生,他说他是10岁。求第5个学生多少岁。
【思路】
要求第5个学生的年龄,就必须知道第4个学生的年龄,而第4个学生的年龄也不知道,要求第4个学生的年龄就必须先知道第3个学生的年龄,而第3个学生的年龄取决于第2个学生的年龄,而第二个学生的年龄取决于第1个学生的年龄,第1个学生的年龄是10。而且每一个学生的年龄都比其前一个学生的年龄大2。即
Age(5)=Age(4)+2
Age(4)=Age(3)+2
Age(3)=Age(2)+2
Age(2)=Age(1)+2
Age(1)=10
用数学公式表示如下:
Age(n)= 10,(n=1)
Age(n)=Age(n-1)+2,(n>1)

#include<stdio.h>

int getAge(int currPersonNum)
{
	int age;
	if(currPersonNum==1){
	age=10;	
	}else{	
	age=getAge(currPersonNum-1)+2;
	}
	return age;
}
int main()
{
	int age;
	int num;
	puts("是第几个学生?");
	scanf("%d",&num);
	age=getAge(num);
	printf("第%d个学生的年龄是%d",num,age);
	return 0;
}

 【例题2】阶乘

#include<stdio.h>
#include<stdlib.h>

unsigned long getFactorial(int num)
{
	,
	if(num>12)
	{
		puts("越界");
		exit(-1);
	}
	if(num==1){
		result=1;
	}else{
		result=getFactorial(num-1)*num;
	}
	return result;
}
int main()
{
	int num;
	unsigned long ret;
	puts("请输入要求的阶乘数:");
	scanf("%d",&num);
	ret=getFactorial(num);
	printf("%d的阶乘是:%ld\n",num,ret);
	printf("%d\n",sizeof(long int));
	printf("%d\n",sizeof(int));
	return 0;
}

数组和函数配合应用

#include<stdio.h>

void printArr(int arry[],int len)//形参中不存在数组的概念,即便括号约定了数组的大小,也无效
								 //传递是一个地址,是数组的首地址
{
	//printf("main:arry的大小是:%d\n",sizeof(arry));//在os用8个字节来表示首地址
	for(int i=0;i<len;i++){
		printf("%d  ",arry[i]);
	}
	printf("\n");
}
int main()
{
	int arry[3]={1,2,3};
	int len;
	len=sizeof(arry)/sizeof(arry[0]);
	printf("main:arry的大小是:%d\n",sizeof(arry));
	printArr(arry,len);//数组名代表整个数组的首地址
	printArr(&arry[0],len);//第一个元素的地址也是数组的首地址
	return 0;
}

有3X4矩阵,初始化它并输出,然后求最大值输出 

#include<stdio.h>

void initArryDoublt(int arry[][4],int row,int coumn)
{
	for(int i=0;i<row;i++){
		for(int j=0;j<coumn;j++){
			printf("请输入第%d行,第%d列的数据\n",i+1,j+1);
			scanf("%d",&arry[i][j]);
		}
	}
}

void printfArryDoublt(int arry[][4],int row,int coumn)
{
	for(int i=0;i<row;i++){
		for(int j=0;j<coumn;j++){
			printf("%d\t",arry[i][j]);
		}
		printf("\n");
	}
}
int getMzxFromArryDouble(int arry[][4],int row,int coumn)
{
	int max=arry[0][0];
	for(int i=0;i<row;i++){
		for(int j=0;j<coumn;j++){
			if(max<arry[i][j])
				max=arry[i][j];
		}
	}
	return max;
}
int main()
{
	int max;
	int arr[3][4];
	
	initArryDoublt(arr,3,4);
	printfArryDoublt(arr,3,4);
	
	max=getMzxFromArryDouble(arr,3,4);
	printf("二维数组中最大的值是%d\n",max);
	
	return 0;
}

利用函数找出最大数以及最小数和它们的下标,封装冒泡排序的函数由小到大,封装选择函数的排序由大到小

#include<stdio.h>

int numOfMax=0;
int numOfMin=0;
void initArry(int arr[],int len)
{
	for(int i=0;i<len;i++){
		printf("请输入第%d个数:",i+1);
		scanf("%d",&arr[i]);
	}
}
void printfArry(int arr[],int len)
{
	for(int i=0;i<len;i++){
		printf("%d  ",arr[i]);
	}
	printf("\n");
}
int getMaxFromArry(int arr[],int len)
{
	int max=arr[0];
	for(int i=0;i<len;i++){
		if(max<arr[i]){
			max=arr[i];
			numOfMax=i;
		}
	}
	return max;
}
int getMinFromArry(int arr[],int len)
{
	int min=arr[0];
	for(int i=0;i<len;i++){
		if(min>arr[i]){
			min=arr[i];
			numOfMin=i;
		}
	}
	return min;
}
void bubbleSort(int arr[],int len)//冒泡
{
	int temp;
	for(int i=0;i<len-1;i++){
		for(int j=0;j<len-1-i;j++){
			if(arr[j]>arr[j+1]){
				temp=arr[j+1];
				arr[j+1]=arr[j];
				arr[j]=temp;
			}
		}
	}
}
void selectionSort(int arr[],int len)//选择
{
	int temp;
	for(int i=0;i<len-1;i++){
		for(int j=i+1;j<len;j++){
			if(arr[i]<arr[j]){
				temp=arr[j];
				arr[j]=arr[i];
				arr[i]=temp;
			}
		}
	}
}
int main()
{
	int max;
	int min;
    int arry[10];
	int len=sizeof(arry)/sizeof(arry[0]);
	initArry(arry,len);
	printfArry(arry,len);
	max=getMaxFromArry(arry,len);
	min=getMinFromArry(arry,len);
	printf("第%d个数为最大值:%d\n",numOfMax+1,max);
	printf("第%d个数为最小值:%d\n",numOfMin+1,min);
	bubbleSort(arry,len);
	printfArry(arry,len);
	selectionSort(arry,len);
	printfArry(arry,len);
	return 0;
}

 指针

指针==地址

#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));//取值运算符1*(&a),他把后面跟的内存地址中的数据“取出来”
	printf("指针变量的方式a=%d\n",*p);
	return 0;
}

(int *p; p=&a;) == (int *p=&a; )

指针要区分类型

不同类型存储大小不同

指针指向固定区域

#include<stdio.h>

int main()
{
	int a=10;
	printf("adress of a is 0X%p\n",&a);
	unsigned int *p=(unsigned int *)0X000000000061FE3C;
	printf("P:%p",p);
	return 0;
}

 小练习

输入三个数a,b,c;要求不管怎么输入,在输出的时候,a,b,c就由小到大输出

#include<stdio.h>

void changeData(int *pa,int *pb,int *pc)
{
	int temp;
	if(*pa>*pb){
		temp =*pa;
		*pa=*pb;
		*pb=temp;
	}
	if(*pa>*pc){
		temp =*pa;
		*pa=*pc;
		*pc=temp;
	}
	if(*pb>*pc){
		temp =*pc;
		*pc=*pb;
		*pb=temp;
	}
}

int main()
{
	int a,b,c;
	puts("请输入三个数");
	scanf("%d",&a);
	scanf("%d",&b);
	scanf("%d",&c);
	printf("改变前这三个数为:\n%d,%d,%d\n",a,b,c);
	changeData(&a,&b,&c);
	printf("改变后这三个数为:\n%d,%d,%d\n",a,b,c);
	return 0;
}

定义一个指针变量指向数组 

int arry[4]={1,2,3,4};
	int *p;
	p=arry;//数组名就是数组的首地址
    p=&arry[0]; //把arry[0]元素地址赋给指针变量p

指针偏移遍历数组

 p+1并不代表地址数加了1,而是地址偏移了一个类型的字节数

printf("0:%d\n",*p);
printf("1:%d\n",*(p+1));
printf("2:%d\n",*(p+2));
printf("3:%d\n",*(p+3));

for(int i=0;i<4;i++){
	printf("%d  ",*(p+i));
}
	for(int i=0;i<4;i++){
		printf("%d  ",*p);
        p++;
	}
	for(int i=0;i<4;i++){
		printf("%d  ",*p++);//*P先结合,地址p再加加(优先级)
	}

注意*p++可以但是*arry++不可以, p是一个指针变量,它是一个保存地址的变量,它保存的地址是可改的,arry数组它是一个常变量,占内存空间地址定了就是定了

注意:通过偏移的方式访问地址,如果还需要再次使用,重新让指针回到首元素 

见怪不怪

#include<stdio.h>

int main()
{
    int arr[5] = {10,20,30,50,70};
	int *p;
	p = arr;
	
	printf("%d\n",arr);//arr地址
	printf("%d\n",*arr);
	printf("%d\n",*(arr+1));	
	printf("%d\n",*p);
	printf("%d\n",*(p+1));
	printf("%d\n",p[1]);//见怪不怪
	printf("%d\n",sizeof(arr));//4*5=20四个字节五个元素
	printf("%d\n",sizeof(p));//用8个字节表示一个地址
	return 0;
}

p[1]=*(p+1)=*(arr+1)=arr[1] 

逆序

A.奇数:

1122334455
i01234
j43不操作

for(i=0;i<2;i++);2=size/2=5/2=2.5;顾 i<size/2

0+4=3+1=i+j=size-1;顾 j=size-1-i

B.偶数

11

2233445566
i012345
j543

for(i=0;i<3;i++);3=size/2=6/2=3;顾 i<size/2

0+5=4+1=i+j=size-1;顾 j=size-1-i

所以奇数偶数相同

#include<stdio.h>

void initArray(int *parr,int size)
{
	for(int i=0;i<size;i++){
		printf("请输入第%d个元素的数据\n",i+1);
		scanf("%d",parr++);
	}
}
void printfArray(int *parr,int size)
{
	for(int i=0;i<size;i++){
		printf("%d ",*parr++);
	}
	printf("\n");
}
void revangeArry(int *parr,int size)
{
	int i,j,tmp;
	for(i=0;i<size/2;i++)
	{
		j=size-1-i;
		tmp=*(parr+i);
		*(parr+i)=*(parr+j);
		*(parr+j)=tmp;
		
	}
}	
int main()
{
   int arr[6];
   int size=sizeof(arr)/sizeof(arr[0]);
   
   initArray(arr,size);
   puts("交换前:");
   printfArray(arr,size);
   revangeArry(arr,size);
   puts("交换后:");
   printfArray(arr,size);

	return 0;
}

二维数组指针 

int arr[3][4]={{11,22,33,44},{12,13,15,16},{22,66,77,88}};

arr二维数组名就是父地址,行 ,arr+1第2行

printf("arr是父亲地址:%p,偏移1后是%p\n",arr,arr+1);

 

arr[0],arr[1],arr[2],子地址,列 ,arr[0]+1第一行第二列   

printf("arr[0]是子数组地址:%p,偏移1后是%p\n",arr[0],arr[0]+1);

取首地址有两种方式1.名字。2.&(首个元素),顾a[0]=&a[0][0],a[1]=&a[1][0],a[3]=&a[3][0]

其中a[0]是子数组的数组名,等于首个元素取地址 

(a[i] 和 *(a+i) 是无条件等价的)
因此, *(a[i]+j) 或者 *(*(a+i)+j)是a[i][j]的值,或说 *(a[i]+j) =  *(*(a+i)+j) = a[i][j]                            

for(int i=0;i<3;i++){
		for(int j=0;j<4;j++){
			printf("add:0x%p,data:%d\n",&arr[i][j],arr[i][j]);
			printf("add:0x%p,data:%d\n",arr[i]+j,*(arr[i]+j));
			printf("add:0x%p,data:%d\n",*(arr+i)+j,*(*(arr+i)+j));
			printf("=======================================\n");
		}
	}

二维数组指针定义            

数组指针,定义一个指针,指向一个数组;
数组指针才是真正指向二维数组名                                                                                            

#include<stdio.h>
	
int main()
{
	int arr[3][4]={{11,22,33,44},{12,13,15,16},{22,66,77,88}};
	int i,j;
	int (*p)[4]=arr;
	//数组指针,定义一个指针,指向一个数组;
	//数组指针才是真正指向二维数组名
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
		printf("%5d",*(*(p+i)+j));
		}
	}
	printf("\n");
	p=&arr[2];//&arr[2]是第三行的首地址
	for(j=0;j<4;j++){//输出行
		printf("%5d",*(*(p)+j));
	}
	return 0;
}

 应用

#include<stdio.h>
	
void printfArray(int (*p)[4])
{
	int i,j;
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
		printf("%3d",*(*(p+i)+j));//  (*p)[4]=arr所以*(p+i)=arr+i
		}
		printf("\n");
	}
}
void tipsInputRowAndColumn(int *pm,int *pn)
{
	printf("请输入行和列的值:\n");
	scanf("%d%d",pm,pn);
	printf("done!\n");
}
int getTheData(int (*p)[4],int row,int column)
{
	int data;
	data= *(*(p+row)+column);
	return data;
}
int main()
{
	int arr[3][4]={{11,22,33,44},{12,13,15,16},{22,66,77,88}};
	int row,column;
	int data;
	printfArray(arr);
	tipsInputRowAndColumn(&row,&column);//!
	data=getTheData(arr,row,column);
	printf("%d",data);
	return 0;
}

函数指针

#include<stdio.h>
	
int inCData(int data)
{
	return ++data;
	
}
void printWelcome()
{
	puts("你好");
}
int main()
{
	void (*p)();//定义一个函数指针变量	
	p=printWelcome;//函数名等于地址
	(*p)();//调用

//函数指针也讲究类型,函数的原型也要一样	
	int (*pc)(int a);
	pc=inCData;
	printf("%d",(*pc)(10));
	
	return 0;
}
#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 dataHandler(int data1,int data2,int (*pfunc)(int , int ))
{
	int ret;
	ret = (*pfunc)(data1,data2);
	return ret;
}
int main()
{
	int data1,data2;
	int cmd;
	int ret;
	
	int (*pfunc)(int ,int );//定义一个函数指针变量
	
	puts("请输入data1的值");
	scanf("%d",&data1);
	puts("请输入data2的值");
	scanf("%d",&data2);
	printf("请输入1(取最大值),2(取最小值),或者3(求和)\n");	
	scanf("%d",&cmd);
	switch(cmd){
		case 1:
			pfunc=getMax;//将地址传进去
		break;
		case 2:
			pfunc=getMin;
		break;
		case 3:
			pfunc=getSum;
		break;
		default:
		printf("输入错误\n");
		exit(-1);
		break;
	}
	ret = dataHandler(data1,data2,pfunc);
	printf("ret = %d\n",ret);

	return 0;
}

 指针数组

一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。下面定义一个指针数组:
int * p[4];
由于[]比*优先级高,因此p先与[4]结合,形成p[4]形式,这显然是数组形式,表示p数组有4个元素。然后再与p前面的“*"结合,“*"表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。
注意不要写成
int( * p)[4];
//这是指向一维数组的指针变量
定义一维指针数组的一般形式为
类型名*数组名[数组长度];
类型名中应包括符号“*”,如“int*”表示是指向整型数据的指针类型.

#include<stdio.h>	

int main()
{
	int a=10,b=20,c=30,d=40;
	int *p[4]={&a,&b,&c,&d};
	
	for(int i=0;i<4;i++)
	{
		printf("%d\n",*p[i]);
	}
	
	
	return 0;
}

函数指针数组 

函数指针int (*pfunc)(int ,int );的基础上,变成int (*pfunc[3])(int ,int ),再加上包含的函数地址变成int (*pfunc[3])(int ,int )={getMax,getMin, getSum};

#include<stdio.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 data1=20,data2=30;
	int ret;
	
	int (*pfunc[3])(int ,int )={getMax,
								getMin,
								getSum};//定义一个函数指针数组变量

	for(int i=0;i<3;i++)
	{
		ret=(*pfunc[i])(data1,data2);
		printf("ret = %d\n",ret);
	}

	return 0;
}

指针函数 

一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。
其概念与以前类似,只是返回的值的类型是指针类型而已
例如“int *a(int x,int y);”,a是函数名,调用它以后能得到一个int*型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。请注意在*a两侧没有括号,在a的两侧分别为*运算符和()运算符。而()优先级高于*,因此a先与()结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。

有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后能输出该学生的全部成绩。用指针函数来实现。

#include<stdio.h>

int* getPosPerson(int pos,int (*pstu)[4]) //定义指针函数(变量,二维数组指针)
{
	int *p;								//定义指针
	p = (int *)(pstu+pos);//强转(父地址+偏移量)
	return p;
}

int main()
{	
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59}
	}; 
	
	int *ppos;//定义指针
	int pos;
	printf("请输入你需要看的学生号数: 0,1,2\n");
	scanf("%d",&pos);
	
	ppos = getPosPerson(pos,scores); //把输入的组地址+偏移量给指针
	for(int i;i<4;i++){
		printf("%d " ,*ppos++);
	}
		
	return 0;
}

 有a个学生,每个学生有b门课程的成绩。找出其中有不及格的课程的学生以及学生号

#include <stdio.h>
float *search(float (*pointer)[4]){
	float *pt; //定义指针
	pt=NULL;
	/*为了避免“野指针”(即指针在首次使用之前没有进行初始化)的出现,
	  我们声明一个指针后最好马上对其进行初始化操作。
	  如果暂时不明确该指针指向哪个变量,则需要赋予NULL值。*/
	for(int i=0;i<4;i++)
		if(*(*pointer+i)<60)//*(*(a+j)+i)
			pt=*pointer; //*(pointer+0)=a[i+0]
	return pt; //传回地址
}
int main(){
    float score[][4]={{60,70,20,90},{60,89,67,88},{34,78,90,66}};
	float *p; //定义指针
	for(int i=0;i<3;i++){
		p=search(score+i); //将父地址依次传入函数,并传回,//*(pointer+0)=a[i+0]
		if(p==*(score+i)){ //如果父地址被传回,说明有不及格
			printf("%d号学生不及格成绩为:",i);
			for(int j=0;j<4;j++)
				printf("%6.2f",*(p+j)); //子地址++,之所以上面是父地址++是因为上面函数里定义的是二维数组指针,所以和这里不同
			printf("\n");
		}
	}
	return 0;
}

多级指针

定义

#include <stdio.h>

int main(){
	int data = 100;
    int *p;
	p = &data;
	printf("data的地址:%p\n",&data);
	printf("p保存data的地址:%p,内容是:%d\n",p,*p);
	
	int **p2;
	p2 = &p;
	printf("p2保存p的地址:%p\n",p2);
	printf("*p2是:%p\n",*p2);
	printf("**p2来访问data:%d\n",**p2);
	
	int ***p3;
	p3 = &p2;
	printf("p3保存p2的地址:%p\n",p3);
	printf("*p3是:%p\n",*p3);
	printf("***p3来访问data:%d\n",***p3);
	return 0;
}

 二级指针用来存储一级指针的地址

#include<stdio.h>

void getPosPerson(int pos,int (*pstu)[4],int **ppos) //定义常量+二维数组指针+二级指针
{										//二级指针存储一级指针的地址

	*ppos = (int *)(pstu+pos);//强转(父地址+偏移量)

}

int main()
{	
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59}
	}; 
	
	int *ppos;//定义指针
	int pos;
	printf("请输入你需要看的学生号数: 0,1,2\n");
	scanf("%d",&pos);
	
	getPosPerson(pos,scores,&ppos); //把输入的组地址+偏移量给指针+指针的地址
	for(int i;i<4;i++){
		printf("%d " ,*ppos++);
	}
		
	return 0;
}

 const和指针

int data = 100;
    const int *p = &data;
	//*p =66; error C2166:左值指向const对象 不能对p指向的内存空间进行写操作
	printf("%d",*p);

const 在指针类型前面,说明const修饰的指针所指向的内存空间,不能通过指针去修改

各种指定定义

字符串 

定义字符串

#include <stdio.h>

int main(){
	int data[] = {1,2,3,4,5};
	char cdata[] = {'h','e','l','l','o'};
	char cdata2[] = "hello";
	char *pchar = "hello";  //字符串常量,不允许被修改
	for(int i=0;i<5;i++){
		printf("%c",cdata2[i]);
	}
    putchar('\n');
	printf("%s",cdata2);
	putchar('\n');
	puts(cdata2);
	//putchar('\n');
	puts(pchar);
	
	return 0;
}
#include <stdio.h>

int main(){
	int data[] = {1,2,3,4,5};
	char cdata[] = {'h','e','l','l','o'};
	char cdata2[] = "hello"; //字符串的结束标志位‘\0’
	
	//请计算数组data元素的个数
	int len = sizeof(data)/sizeof(data[0]);
	printf("len = %d\n",len);
	
	len = sizeof(cdata)/sizeof(cdata[0]);
	printf("len = %d\n",len);
	
	len = sizeof(cdata2)/sizeof(cdata2[0]);
	printf("len = %d\n",len);
	return 0;
}

 

字符串的结尾会自动补结束标志位‘\0’,顾为6个

sizeof和stelen

#include <stdio.h>
#include <string.h>

void test()
{
	
}

int main(){
	
	char cdata[] = "hello"; 
	void (*ptest)();
	ptest = test;
	printf("sizeof:%d\n",sizeof(cdata));
	printf("strlen:%d\n",strlen(cdata));//计算有效字符的长度
	
	char *p = "hello";
	//p是一个char *,sizeof来计算的时候,得出是计算用多少字节来表示一个地址(只要是地址就是用八个地址表示)
	printf("sizeof:p     :%d\n",sizeof(p));
	printf("sizeof:cahr* :%d\n",sizeof(char *));
	printf("sizeof:int*  :%d\n",sizeof(int *));
	printf("sizeof:cahr  :%d\n",sizeof(char));
	printf("sizeof:ptest :%d\n",sizeof(ptest));
	printf("strlen   	 :%d\n",strlen(p));
	return 0;
}

malloc动态开辟内存空间

前面不管是整形的数组还是字符的数组,通常数组的大小在定义的时候就确定了,定死的,有一种需求不需要浪费那么多的空间,那就是动态的开辟函数

C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针

参数:size -- 内存块的大小,以字节为单位。

在堆上面动态开辟(普通变量在栈开辟空间,函数调用结束后会清除栈里面的数据,但堆只有在程序结束才会释放,有个风险如果是死循环会不断地占用空间 )

C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。 

参数:ptr -- 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。

C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest

需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。

参数:dest -- 指向用于存储复制内容的目标数组。

           src -- 要复制的字符串

C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。

参数: ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。

           size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。

C 库函数 void *memset(void *str, int c, size_t n) 用于将一段内存区域设置为指定的值。

memset() 函数将指定的值 c 复制到 str 所指向的内存区域的前 n 个字节中,这可以用于将内存块清零或设置为特定值。

在一些情况下,需要快速初始化大块内存为零或者特定值,memset() 可以提供高效的实现。

在清空内存区域或者为内存区域赋值时,memset() 是一个常用的工具函数。

参数:

  • str -- 指向要填充的内存区域的指针。
  • c -- 要设置的值,通常是一个无符号字符。
  • n -- 要被设置为该值的字符数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
	
	char *p;//野指针
	p = (char *)malloc(1);//p有了具体的内存指向(返回值是void*,所以要强转char*)
	*p = 'c';
	printf("%c\n",*p);
	
	free(p);//让p重新回到野指针
	p = NULL;//悬挂指针
	p = (char *)malloc(12);
	if(p == NULL)//有失败的风险,不让程序继续往下走
	{
		printf("malloc error\n");
		exit(-1);
	}
	memset(p,'\0',12);//把每个字节初始成'\0'
	printf("扩容地址:%x\n",p);
	strcpy(p,"abcdefghijk");
	puts(p);
	
	int len = strlen("abcdefghijk123456");
	int newLen =len - 12 + 1;//+1是存放'\0'
	realloc(p,newLen);//基于原本大小扩容
	strcpy(p,"abcdefghijk123456");
	printf("扩容后地址:%x\n",p);
	puts(p);
	puts("end");
	return 0;
}

 

 输入输出字符串

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

参数:

  • dest -- 指向用于存储复制内容的目标数组。
  • src -- 要复制的字符串。
  • n -- 要从源中复制的字符数。
#include <stdio.h>
#include <string.h>
int main(){
	char *p = "abcdefg 123456";
	char str[128] = {'\0'};
	
	puts(p);
	printf("%s\n",p);
	
	puts("请输入字符串");
	//scanf("%s",&str);
	//gets(str);
	//strcpy(str,p);
	strncpy(str,p,7);
	puts(str);
	
	return 0;
}

 自己实现字符串拷贝函数

#include <stdio.h>
#include <string.h>

char* myStrcpy(char *des,char *src)
{
	if(des == NULL || src == NULL){
		return NULL;
	}
	char *bak = des;
	while(*src != '\0')
	{
		*des = *src;
		des++;
		src++;
	}
	*des = '\0';//给字符串结束标志(上面des++结束,到达了最后一个地址,顾*des = '\0';给最后一个地址赋'\0')
	return bak;
}

char* myStrncpy(char *des,char *src,int count)
{
	if(des == NULL || src == NULL){
		return NULL;
	}
	char *bak = des;
	while(*src != '\0' && count > 0)
	{
		*des++ = *src++;
		count --;
	}
	if(count > 0)
	{
		while(count >0){
			count --;
			*des++ = '\0';
		}
		return bak;
	}
	*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[128] = {'\0'};
	char *p = "abcdefg 123456";
	myStrncpy(str,p,20);
	puts(str);
	
	
	
	return 0;
}

断言函数assert

断言(assert)的用法 | 菜鸟教程 (runoob.com)

#include <stdio.h>
#include <string.h>
#include <assert.h>

char* myStrcpy(char *des,char *src)
{
	assert(des !=NULL && src != NULL); //断言
	char *bak = des;
	while(*src != '\0')
	{
		*des = *src;
		des++;
		src++;
	}
	*des = '\0';//给字符串结束标志(上面des++结束,到达了最后一个地址,顾*des = '\0';给最后一个地址赋'\0')
	return bak;
}

int main(){
	
	char str[128] = {'\0'};
	char *p = "abcdefg 123456";
	char *pstr = NULL;
	myStrcpy(pstr,p);
	puts(str);
	
	return 0;
}

字符串拼接strcat使用和实现

C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

参数:

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src -- 指向要追加的字符串,该字符串不会覆盖目标字符串。
#include <stdio.h>
#include <string.h>
#include <assert.h>

char* myStrcat(char *des,char *src)
{
	assert(des!=NULL&&src!=NULL);
	char *bak = des;//让地址同步
	while(*des != '\0'){	//先等原函数输入完
		des++;
	}
	while((*des++ = *src++) != '\0'); //复制
	*des = '\0';
	
	return bak;
}

char* myStrcat2(char *des,char *src)
{
	assert(des!=NULL&&src!=NULL);
	char *bak = des;
	strcpy(des+strlen(des),src);
	*des = '\0';
	
	return bak;
}

char* myStrcat3(char *des,char *src)
{
	assert(des!=NULL&&src!=NULL);
	char *bak = des;//让地址同步
	for(;*des!='\0';des++);
	while((*des++ = *src++) != '\0'); //复制
	*des = '\0';
	
	return bak;
}

int main(){
	char str[128] = {"xiaoye"};
	char *p = " handsome";
	char *p2;
	//p2=strcat(str,p);
	p2 = myStrcat3(str,p);
	puts(str);
	puts(p2);
	return 0;
}

【函数】strcat源代码 - github.com/starRTC - 博客园 (cnblogs.com)

 字符串比较strcmp使用及实现

C 库函数 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。

参数:

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。

返回值:

  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。
  • 如果返回值等于 0,则表示 str1 等于 str2。

strncmp() 是一个标准库函数,用于比较两个字符串的前 n 个字符是否相等。

strncmp() 函数通常用于比较两个字符串,以确定它们是否相等或哪个字符串在字典顺序上更小。

C 库函数 int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字符。

参数:

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。
  • n -- 要比较的最大字符数。

返回值:

  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。
  • 如果返回值等于 0,则表示 str1 等于 str2。
#include <stdio.h>
#include <string.h>

int myStrcmp(char *str1,char *str2) //有bug当字符相加相等但是不一样时还是会显示一样
{
	int ret = 0;
	int n_str1 = 0;
	int n_str2 = 0;
	char *bakStr1 =str1;
	char *bakStr2 =str2;
	
	while(*str1 && *str2 &&(*str1 == *str2)){ // (*str1与*str1!='\0'相等) 
		str1++;
		str2++;
	}
	if(*str1 || *str2){
		str1 = bakStr1;  //偏移后回到原来的地址
		str2 = bakStr2;      
		while(*str1){
			n_str1 += *str1; //ASCII码总和
			str1++;
		}
		while(*str2){
			n_str2 += *str2;
			str2++;
		}
	}
	/*while(*str1 && *str2 &&(*str1 == *str2)){  
		str1++;
		str2++;
		n_str1 += *str1; 
		n_str2 += *str2;
	}*/
	ret = n_str1 - n_str2;
	if(ret<0){
		ret = -1;
	}
	if(ret>0){
		ret = 1;
	}
	return ret;
}

int main(){
	char *p1 = "abcdefg 123456";
	char *p2 = "abcdefg 123456";
	
	//int ret = strcmp(p1,p2);
	int ret = myStrcmp(p1,p2);
	if(ret == 0){
		puts("两个字符串一样");
	}
	printf("RET = %d\n",ret);
	return 0;
}

其他

查找字符串:

C 库函数 – strstr() | 菜鸟教程 (runoob.com)

分割字符串:

C 库函数 – strtok() | 菜鸟教程 (runoob.com)

结构体

结构体引入

整型数,浮点型数,字符串是分散的数据表示
有时候我们需要用很多类型的数据来表示一个整体,比如学生信息,这时候就可以用结构体

定义一个结构体

 它算是一个模板,一般不给赋具体的值,每一项在实际应用中并不是都要使用

 结构体的使用

#include <stdio.h>
#include <string.h>

struct student
{
	int num;
	char name[32];
	char sex;
	int age;
	double score;
	char addr[32];
};

int main(){
	
	int            a;
	struct student stu1;
	struct student stu2 = {2,"xiaohe",'g',17,97.5,"上海"};
	struct student max;
	
	a = 10;
	stu1.num = 1;//点运算符来访问结构体中成员变量(域)
	stu1.age = 10;
	stu1.score = 98.5;
	strcpy(stu1.name,"xiaoye");
	strcpy(stu1.addr,"浙江");
	
	printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		stu1.num,stu1.age,stu1.score,stu1.name,stu1.addr);
	printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		stu2.num,stu2.age,stu2.score,stu2.name,stu2.addr);
	//找大小方法一
/*	if(stu1.score<stu2.score){
	printf("成绩比较好的是: \n");
	printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		stu2.num,stu2.age,stu2.score,stu2.name,stu2.addr);
	}else{
		printf("成绩比较好的是: \n");
		printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		stu1.num,stu1.age,stu1.score,stu1.name,stu1.addr);
	}*/
	//方法二
	max = stu1;
	if(stu1.score<stu2.score)
	{
		max = stu2;
	}
	printf("成绩比较好的是: \n");
	printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		max.num,max.age,max.score,max.name,max.addr);
	
	return 0;
}

结构体和数组结合 

#include <stdio.h>
#include <string.h>

struct student
{
	int num;
	char name[32];
	char sex;
	int age;
	double score;
	char addr[32];
};

int main(){

	int 			arr[3] = {1,2,3};
	struct student 	arr2[3] = {
		{2,"张三",'g',17,99.3,"北京"},
		{3,"李四",'m',18,94.5,"上海"},
		{4,"王五",'f',17,96.1,"深圳"}
	};
	
	int len = sizeof(arr2)/sizeof(arr2[0]);
	for(int i=0;i<len;i++){
		printf("学号:%d,年龄:%d,分数:%lf,名字:%s,地址:%s\n",
		arr2[i].num,arr2[i].age,arr2[i].score,arr2[i].name,arr2[i].addr);
	}
	
	
	return 0;
}

选票系统

#include <stdio.h>
#include <string.h>

struct voters
{
	char name[32];
	int tickets;
};

int main(){
	struct voters xm[3];
	struct voters max;
	int waive = 0;
	int len;
	int i;
	int j;
	int mark;
	
	int total = 5;
	char tmpName[32];
	
	//初始化选民信息
	len = sizeof(xm)/sizeof(xm[0]);
	for(i=0;i<len;i++){
		xm[i].tickets=0;
		printf("请输入第%d个选民的名字:\n",i+1);
		scanf("%s",xm[i].name);
	}
	
	//唱票环节
	for(i=0;i<5;i++){
		mark = 0;
		printf("请输入你投给谁:\n");
		memset(tmpName,'\0',sizeof(tmpName)); //每次清空一下
		scanf("%s",tmpName);//输入选中的选民名字
		for(j=0;j<len;j++){//拿到名字对应候选人票数加1
			if(strcmp(tmpName,xm[j].name)==0){
				xm[j].tickets++;
				mark = 1;
			}
		}
		if(mark == 0){
			printf("没有此候选人,放弃\n");
			waive++;
		}
	}
	
	//公布结果
	for(i=0;i<len;i++){
		printf("名字:%s,票数:%d\n",xm[i].name,xm[i].tickets);
	}
	max = xm[0];
	for(i=0;i<len;i++){
		if(max.tickets<xm[i].tickets){
			max=xm[i];
		}
	}
	printf("%s以%d票当选!!弃票人数为%d\n",max.name,max.tickets,waive);
	return 0;
}

结构体指针定义

#include <stdio.h>

struct Test
{
	int idata;
	char cdata;
};

int main(){
	
	int a;
	int *p = &a;
	
	char c = 'c';
	char *pc = &c;
	
	struct Test t1 = {10,'c'}; //变量的四大关键(1.类型2.名3.值4.地址)
	struct Test *ps = &t1;
	
	printf("t1的idata=%d\n",t1.idata);//变量名访问,用点运算符
	printf("t1的idata=%d\n",ps->idata);//指针变量访问,用"->"运算符
	
	ps->cdata = 'R';
	printf("t1的idata=%c\n",t1.cdata);//变量名访问,用点运算符
	printf("t1的idata=%c\n",ps->cdata);//指针变量访问,用"->"运算符
	
	return 0;
}

结构体指针应用01

#include <stdio.h>
#include <string.h>

struct voters
{
	char name[32];
	int tickets;
};
/*
1.把以前的普通变量名,或者下标访问的.运算符,改成结构体指针的->
2.指针++,每次遍历回到数组尾巴,下次遍历之前记得改回来(重新指数组头)!
*/

int main(){
	struct voters xm[3];
	struct voters max;
	struct voters *p = xm;
	int waive = 0;
	int len;
	int i;
	int j;
	int mark;
	
	int total = 5;
	char tmpName[32];
	
	//初始化选民信息
	len = sizeof(xm)/sizeof(xm[0]);
	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++){
		mark = 0;
		printf("请输入你投给谁:\n");
		memset(tmpName,'\0',sizeof(tmpName)); //每次清空一下
		scanf("%s",tmpName);//输入选中的选民名字
		p = xm;
		for(j=0;j<len;j++){//拿到名字对应候选人票数加1
			if(strcmp(tmpName,p->name)==0){
				p->tickets++;
				mark = 1;
			}
			p++;
		}
		if(mark == 0){
			printf("没有此候选人,放弃\n");
			waive++;
		}
	}
	
	//公布结果
	p = xm;
	for(i=0;i<len;i++){
		printf("名字:%s,票数:%d\n",p->name,p->tickets);
		p++;
	}
	p = xm;
	max = xm[0];
	for(i=0;i<len;i++){
		if(max.tickets<p->tickets){
			max=xm[i];
			p++;
		}
	}
	printf("%s以%d票当选!!弃票人数为%d\n",max.name,max.tickets,waive);
	return 0;
}

结构体指针数组函数综合应用改写选票系统 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct voters
{
	char name[32];
	int tickets;
};

struct voters* initXms(struct voters *p,int *pn)
{
	int i;
	if(p == NULL){
	printf("请输入有几个人参选:\n");
	scanf("%d",pn);
	p = (struct voters*)malloc(*pn*sizeof(struct voters));//malloc是void类型的指针需要强转
	//因为struct voters *xm = NULL;顾开辟内存空间指向它
	}
	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 voters *p,int len)
{
	int i;
	for(i=0;i<len;i++){
		printf("名字:%s,票数:%d\n",p->name,p->tickets);
		p++;
	}
}

int doVot(struct voters *p,int len)
{
	int i;
	int j;
	int mark;
	int waive = 0;
	char tmpName[32];
	struct voters *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++){//拿到名字对应候选人票数加1
			if(strcmp(tmpName,p->name)==0){
				p->tickets++;
				mark = 1;
			}
			p++;
		}
		if(mark == 0){
			printf("没有此候选人,放弃\n");
			waive++;
		}
	
	}
	return waive;
}

struct voters* getMax(struct voters *p ,int len)
{
	struct voters* max;
	int i;
	max = p;
	for(i=0;i<len;i++){
		if(max->tickets < p->tickets){
			max = p;	
		}
		p++;
	}
	return max;
}

int main(){
	struct voters *xm = NULL;
	struct voters *final;
	
	int total = 0;
	xm = initXms(xm,&total);
	puts("目前参选人有:");
	printfXms(xm,total);
	
	int waive = doVot(xm,total);
	printf("废票数是:%d\n",waive);
	printfXms(xm,total);
	
	final = getMax(xm,total);
	printf("%s以%d票当选!!弃票人数为%d\n",final->name,final->tickets,waive);
	
	return 0;
}

结构体二级指针

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//结构体二级指针

struct voters
{
	char name[32];
	int tickets;
};

void initXms(struct voters **pxm,int *pt)
{
	//int *pt = &total; //保存的是指针变量的地址
	//struct voters **pxm = &xm; //(*pxm = xm = NULL)
	if(*pxm == NULL){
		printf("请输入有几个人参选:\n");
		scanf("%d",pt);
		*pxm = (struct voters*)malloc(*pt * sizeof(struct voters));
	}
	for(int i=0;i<*pt;i++){
		(*pxm)->tickets == 0;
		printf("请输入第%d个选民的名字:\n",i+1);
		scanf("%s",(*pxm)->name);
		(*pxm)++;
	}
	*pxm = *pxm-*pt;
}

void printfXms(struct voters *p,int len)
{
	int i;
	for(i=0;i<len;i++){
		printf("名字:%s,票数:%d\n",p->name,p->tickets);
		p++;
	}
}

int doVot(struct voters *p,int len)
{
	int i;
	int j;
	int mark;
	int waive = 0;
	char tmpName[32];
	struct voters *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++){//拿到名字对应候选人票数加1
			if(strcmp(tmpName,p->name)==0){
				p->tickets++;
				mark = 1;
			}
			p++;
		}
		if(mark == 0){
			printf("没有此候选人,放弃\n");
			waive++;
		}
	}
	return waive;
}

struct voters* getMax(struct voters *p ,int len)
{
	struct voters *max;
	int i;
	max = p;
	for(i=0;i<len;i++){
		if(max->tickets<p->tickets){
			max=p;		
		}
		p++;
	}
	return max;
}

int main(){
	struct voters *xm = NULL;
	struct voters *final;
	
	int total = 0;
	
	initXms(&xm,&total);
	puts("目前参选人有:");
	printfXms(xm,total);
	
	int waive = doVot(xm,total);
	printf("废票数是:%d\n",waive);
	printfXms(xm,total);
	
	final = getMax(xm ,total);
	printf("%s以%d票当选!!弃票人数为%d\n",final->name,final->tickets,waive);
	
	return 0;
}

联合体 

#include <stdio.h>

struct TestT
{
	int idata;
	char cdata;
	int ddata;
};

union TestU
{
	int idata;
	char cdata;
	int ddata;
};

int main()
{
	struct TestT t1;
	union  TestU u1;
	
	printf("结构体t1的大小是:%d\n",sizeof(t1)); //所有元素相加
	printf("联合体u1的大小是:%d\n",sizeof(u1)); //取决于最大元素的空间(double是8)
	putchar('\n');
	
	t1.idata = 10;
	t1.cdata = 'a';
    t1.ddata = 20;
	printf("idata:%p, %d\n",&t1.idata,t1.idata);
	printf("cdata:%p, %d\n",&t1.cdata,t1.cdata);
	printf("ddata:%p, %d\n",&t1.ddata,t1.ddata);
	putchar('\n');
	
	u1.idata = 10;
	u1.ddata = 20;
	u1.cdata = 'a';
    printf("idata = %d\n",u1.idata);  //数据会覆盖
	printf("idata:%p\n",&u1.idata);
	printf("cdata:%p\n",&u1.cdata);
	printf("ddata:%p\n",&u1.ddata);
}

 一、结构体struct

各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。

二、联合体union

各成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。因而,联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍。不允许对联合体变量名U2直接赋值或其他操作。

 结构体在声明同时定义变量(尽量少用)

联合体应用

#include <stdio.h>

struct Person
{
	char name[32];
	int age;
	char zhiYe;
	char addr[32];
	union {
		int class;
		char keMu[12];
	}mes;
};

int main()
{
	struct Person p[2];
	int i;
	for(i=0;i<2;i++){
		printf("请输入职业:t代表老师,s代表学生\n");
		scanf("%c",&(p[i].zhiYe));
		if(p[i].zhiYe == 's'){
			printf("请输入学生班级:\n");
			scanf("%d",&(p[i].mes.class)); //bug:对应是数组型的不用加&(),其他都要
			printf("请输入学生名字:\n");
			scanf("%s",p[i].name);
		}
		else{
			printf("请输入老师的科目:\n");
			scanf("%s",p[i].mes.keMu);
			printf("请输入老师名字:\n");
			scanf("%s",p[i].name);
		}
		getchar();
	}
	for(i=0;i<2;i++){
	
		if(p[i].zhiYe == 's'){
			printf("学生的名字是:%s,班级是%d\n",p[i].name,p[i].mes.class);	
		}
		else{
			printf("老师的名字是:%s,职业是%s\n",p[i].name,p[i].mes.keMu);
		}
	}
	return 0;
}

实际使用联合体union过程中一句话总结:围绕成员互斥和内存共享这两个核心点去灵活设计你的数据结构——有选择的时候用 

 枚举类型

枚举在C语言中其实是一些符号常量集。直白点说:枚举定义了一些符号,这些符号的本质就是int类型的常量,每个符号和一个常量绑定。这个符号就表示一个自定义的一个识别码,编译器对枚举的认知就是符号常量所绑定的那个int类型的数字。
一般情况下我们都不明确指定这个符号所对应的数字,而让编译器自动分配编译器,自动分配的原则是:从0开始依次增加。如果用户自己定义了一个值,则从那个值开始往后依次增加。

C语言为何需要枚举:C语言没有枚举是可以的。使用枚举其实就是对一些数字进行符号化编码,这样的好处就是编程时可以不用看数字而直接看符号。符号的意义是显然的,一眼可以看出。而数字所代表的含义除非看文档或者注释。

宏定义和枚举的区别:
1、枚举是将多个有关联的符号封装在一个枚举中,而宏定义是完全散的。也就是说枚举其实是多选一,而且只能在这里面选,有时候有效防止其他不符数据的输入。
2、什么情况下用枚举?当我们要定义的常量是一个有限集合时(譬如一星期有7天,譬如一个月有31天,譬如一年有12个月····),最适合用枚举。(其实宏定义也行,但是枚举更好)
3、不适合用枚举的情况下(比如定义的常量符号之间无关联,或者无限的)用宏定义。

宏定义:

#define MON  1
#define TUE   2
#define WED  3
#define THU   4
#define FRI    5
#define SAT   6
#define SUN   7

枚举:

enum DAY
{
     MON=1, TUE, WED, THU, FRI, SAT, SUN
};
#include <stdio.h>

enum WeekDay {mon=1,tus,wed,thi,fri,sat,sun}; //可以指定枚举数的值,默认0往下排
int main()
{
	enum WeekDay day;
	day = sun;
	printf("day=%d\n",day);
	return 0;
}

 可以直接忽略枚举类型名,直接定义枚举变量

#include <stdio.h>
enum {sun,mon,tus,wed,thi,fri,sat}w1,w2;
int main()
{
	
	w1 = mon;
	w2 = tus;
	
	if(w1<tus){      //可以进行比较
		printf("hhhh\n");
	}

	printf("w1= %d\n",w1);
	printf("w2= %d\n",w2);
	return 0;
}

typedef关键字介绍

给已经有的变量起新的名字

#include <stdio.h>

typedef unsigned char u8;
typedef unsigned int  u16;
typedef int arr[10];

struct Test
{
	int data;
	int data2;
};

typedef struct Test T;

typedef struct //TT名字可加可不加
{
	int data1;
	int data2;
}Demo;   //给struct结构体取名Demo 

void printfo(T t)
{
	printf("%d\n",t.data);
}

int main()
{
	arr a;
	a[0] = 10;
	printf("%d\n",a[0]);
	
	struct Test t1;
	t1.data = 100;
	printf("%d\n",t1.data);
	
	T t2;
	t2.data = 1000;
	printfo(t2);
	
	Demo d;
	d.data1 =999;
	printf("%d\n",d.data1);
	
	return 0;
}

#include <stdio.h>

typedef struct 
{
	int num;
	char name[32];
	char sex;
}Person,*pPerson;

void printInfo(Person p)
{
	printf("%d号姐姐:%s %c \n",p.num,p.name,p.sex);
}

void printInfo3(Person *p)//用指针访问
{
	printf("%d号姐姐:%s %c \n",p->num,p->name,p->sex);
}

void printInfo2(pPerson pp) //用指针访问
{
	printf("%d号姐姐:%s %c \n",pp->num,pp->name,pp->sex);
}

int main()
{
  Person p1 = {1,"丽丽",'g'};
  Person p2 = {2,"花花",'g'};
  
  printInfo(p1);
  printInfo(p2);
  
  pPerson pp1 = &p1; //用指针访问
  pPerson pp2 = &p2;
  printInfo2(pp1);
  printInfo2(pp2);
  
  Person *pp3 = &p1;
  Person *pp4 = &p2;
  printInfo3(pp3);
  printInfo3(pp4);
}

链表

1.什么是链表?

数据结构,数据存放的思想

灵活,数据地址相连,可添加减少

引入列表(静态创建):

#include <stdio.h>
struct Text
{
        int data;
        struct Text *next;
};
void printLink(struct Text *head)
{
        struct Text *point;
        point = head;
        while(point != NULL){
                printf("%d ",point->data);
                point = point->next; //把当前结构体的下一个地址给当前地址
        }
        putchar('\n');
}

int getLinkTotalNodeNum(struct Text *head)
{
        int cnt = 0;
        while(head != NULL){
        cnt++;
        head = head->next;
        }
        return cnt;
}

int searchLink(struct Text *head,int data)
{
        while(head != NULL){
                if(head->data == data){
                        return 1;
                }
                head=head->next;
        }
        return 0;
}


int main()
{
        int i;
        int arry[]={1,2,3,4,5,6,7,8,9,10};
        for(i=0;i<sizeof(arry)/sizeof(arry[0]);i++){
                printf("%d",arry[i]);
        }
        putchar('\n');
        struct Text t2 = {1,NULL};
        struct Text t2 = {2,NULL};
        struct Text t3 = {3,NULL};

        t1.next = &t2;
        t2.next = &t3;

        printf("use t1 to printf there nums\n");
       // printf("%d %d %d\n",t1.data,t1.next->data,t1.next->next->data);
        printLink(&t1);

        int ret = getLinkTotalNodeNum(&t1);
        printf("total num = %d\n",ret);

        ret = searchLink(&t1,2);
        if(ret == 0){
                printf("no 2 \n");
        }else{
                printf("have 1 \n");
        }
        ret = searchLink(&t1,5);
         if(ret == 0){
                printf("no 2 \n");
        }else{
                printf("have 1 \n");
        }
      
        return 0;
}

指定节点后方插入新节点

a.找到3

b.new->next = 3.next;//新节点的下一个等于3原本的下一个

c.3->next = new; //三的下一个指向新节点

指定节点前方插入新节点

1.第一个节点是目标节点 

2.目标节点在中间

#include <stdio.h>
struct Text
{
        int data;
        struct Text *next;
};

//在指定节点前插入新节点
struct Text* insertFromfor(struct Text *head, int data,struct Text *new)
{
        struct Text *p = head;//定义新的结构体指向首地址
        //第一个节点是目标节点 
        if(p->data == data){  //(目前p->的值还是首地址的数据)如果新节点在首地址前
                new->next = head;//把首地址串到新节点后
                return new; //返回新节点的地址(即新节点等于首地址)(易错点 return head;现在的head还是原来的地址,除非加一句 head = new;)
        }
        //目标节点在中间
        while(p->next != NULL){   //下一个节点不等于空指针(即没有到最后一个)
                printf("data = %d,point = %d\n",p->next->data,data);
                if(p->next->data == data){    //当前节点的下一数是目标节点
                        new->next = p->next;  //下一个节点的地址串到新节点后
                        p->next = new;       //新节点串到当前节点后
                        printf("insert ok\n");
                        return head;     
                }
                p = p->next;//把下一个地址赋给当前地址(即串起来,实现链表)
        }
        printf("no this data %d\n",data);
        return head;
}
//在指定节点后插入新的链表
int insertFromBehind(struct Text *head, int data,struct Text *new)
{
        struct Text *p = head;//定义新的结构体指向首地址
        while(p != NULL){      //传过来的结构体不等于空指针(即没有到最后一个)
                if(p->data == data){ //如果当前的数等于插入节点
                        new->next = p->next;//把当前的下一个地址给新结构体的地址
                        p->next = new; //把新的地址给当前结构体(即串到当前结构体后)
                }
                p = p->next;//把下一个地址赋给当前地址(即串起来,实现链表)
        }
}

//输出链表
void printLink(struct Text *head) 
{
        struct Text *point;    //定义一个结构体  
        point = head;          //把首地址赋给定义的结构体
        while(point != NULL){  //传过来的结构体不等于空指针(即没有到最后一个)
                printf("%d ",point->data);//输出这个数
                point = point->next;      //把下一个地址赋给当前地址(即串起来,实现链表)
        }
        putchar('\n');
}

//获取链表个数
int getLinkTotalNodeNum(struct Text *head) 
{
        int cnt = 0;
        while(head != NULL){
        cnt++;
        head = head->next;
        }
        return cnt;
}
//查找
int searchLink(struct Text *head,int data)
{
        while(head != NULL){
                if(head->data == data){
                        return 1;
                }
                head=head->next;
        }
        return 0;
}

int main()
{

        struct Text *head = NULL; //定义首地址

        putchar('\n');//换行
        struct Text t1 = {1,NULL}; //定义第一个结构体
        struct Text t2 = {2,NULL}; //定义第二个结构体
        struct Text t3 = {3,NULL}; //定义第三个结构体
        struct Text t4 = {4,NULL}; //定义第四个结构体

        t1.next = &t2; //把第二个地址付给第一个地址的尾巴
        t2.next = &t3; //把第三个地址付给第二个地址的尾巴
        t3.next = &t4; //把第四个地址付给第三个地址的尾巴

        head = &t1;    //把第一个地址给首地址

        struct Text New = {100,NULL};  //定义一个结构体100

        printf("use t1 to printf there nums\n");
        printLink(head); //输出并串联以上结构体

        puts("after insert behead;\n");
        insertFromBehind(head,3,&New);//在第三行后插入新的结构体

        printLink(head);

        struct Text New2 = {101,NULL};
        head = insertFromfor(head,1,&New2);
        puts("after insert head\n");
        printLink(head);

        struct Text New3 = {103,NULL};
        head = insertFromfor(head,2,&New3);
        puts("after insert for:\n");
        printLink(head);
        return 0;

}
     

链表删除指定节点

1.第一个节点是目标节点 

2.目标节点不在第一个

#include <stdio.h>
#include <stdlib.h>

struct Text
{
        int data;
        struct Text *next;
};

struct Text* deletNode(struct Text *head, int data)
{
        struct Text *p = head;
        if(head->data == data){  //如果第一个要被删除
                head = head->next;  //首地址等于首地址的下一个
                free(p); //释放空余出的首地址
                return head; 
        }
        while(p->next != NULL){
                if(p->next->data = data){  //如果当前结构体的下一个要被删除
                        //struct Text *temp = p; 
                        p->next = p->next->next; //当前结构体的下一个变成下下一个
                        //free(tmp);
                        return head;
                }
                p = p->next;
        }
        return head;
}
void printLink(struct Text *head)
{
        struct Text *point;
        point = head;
        while(point != NULL){
                printf("%d ",point->data);
                point = point->next;
        }
        putchar('\n');
}
int main()
{
        struct Text *head = NULL;

        struct Text *p = (struct Text*)malloc(sizeof(struct Text));//动态开辟结构体1

        putchar('\n');
//      struct Text t1 = {1,NULL};
        struct Text t2 = {2,NULL};
        struct Text t3 = {3,NULL};
        struct Text t4 = {4,NULL};

//      t1.next = &t2;
        p->data = 1;  //给结构体1赋值1
        p->next = &t2;//把第二个结构体串到第一个后面
        t2.next = &t3;
        t3.next = &t4;

//      head = &t1;
        head = p;  //把第一个结构体地址当首地址
        printLink(head);

        head = deletNode(head,2);
        printLink(head);
        
        return 0;
}

动态创建 

头插法

#include <stdio.h>
#include <stdlib.h>

struct Text
{
        int data;
        struct Text *next;
};

//在链表前插入数据
struct Text* insertFromHead(struct Text *head,struct Text *new)
{
        if(head == NULL){    //首地址为空
                head = new;    //把新地址赋给首地址
        }else{                    //首地址不为空
                new->next = head;    //把首地址赋给新地址后
                head = new;           //让首地址等于新地址
        }
        return head;
}
//动态创建链表
struct Text *createLink(struct Text *head)
{
        struct Text *new; 
        while(1){
                new = (struct Text*)malloc(sizeof(struct Text)); //动态开辟地址(堆上开辟,连续)
                printf("input your new node data:\n");
                scanf("%d",&(new->data)); 
                if(new->data == 0){
                        printf("0 quit\n");
                        free(new);//清空malloc当前开辟的地址
                        return head;
                         }
                head = insertFromHead(head,new);//在链表前插入地址直到输入0
                }
}

void printLink(struct Text *head)
{
        struct Text *point;
        point = head;
        while(point != NULL){
                printf("%d ",point->data);
                point = point->next;
        }
        putchar('\n');
}


int main()
{

        struct Text *head = NULL;

        head = createLink(head);
        printLink(head);

        struct Text t1 = {100,NULL};
        head = insertFromHead(head,&t1);
        printLink(head);

        return 0;
}

尾插法

#include <stdio.h>
#include <stdlib.h>

struct Text
{
        int data;
        struct Text *next;
};

//在链表后插入数据
struct Text* insertBehind(struct Text *head,struct Text *new)
{
        struct Text *p = head;
        if(p == NULL){  //当首地址为空时
                head = new; 
                return head;
        }
        while(p->next != NULL){
                p = p->next;
        }
        p->next = new;
        return head;
}

struct Text *createLink2(struct Text *head)
{
        struct Text *new;
        while(1){
                new = (struct Text*)malloc(sizeof(struct Text));
                printf("input your new node data:\n");
                scanf("%d",&(new->data));
                if(new->data == 0){
                        printf("0 quit\n");
                        free(new);
                        return head;
                }
                head = insertBehind(head,new);
        }
}
void printLink(struct Text *head)
{
        struct Text *point;
        point = head;
        while(point != NULL){
                printf("%d ",point->data);
                point = point->next;
        }
        putchar('\n');
}
int main()
{
        struct Text *head = NULL;
        head = createLink2(head);
        printLink(head);

        struct Text t1 = {100,NULL};
        head = insertBehind(head,&t1);
        printLink(head);
        return 0;
}

小项目贪吃蛇

keypad这个函数允许使用功能键,例如:F1、F2、方向键等功能键。几乎所有的交互式程序都需要使用功能键,因为绝大多数用户界面主要用方向键进行操作。使用keypad(stdscr,TURE)就为“标准屏幕”(stdscr)激活了功能键。TURE=1代表接收

ncurse上下左右健获取

#include <curses.h>

int main()
{
        int key;

        initscr();
        keypad(stdscr,1);

        while(1){
                key = getch();
                switch(key){
                        case KEY_DOWN:
                                printw("DOWN\n");
                                break;
                        case KEY_UP:
                                printw("UP\n");
                                break;
                        case KEY_LEFT:
                                printw("LEFT\n");
                                break;
                        case KEY_RIGHT:
                                printw("RIGHT\n");
                                break;
                }
        }
        endwin();
        return 0;

}

initscr()用于初始化ncurses数据结构并读取正确的terminfo文件。内存将被分配。

如果发生错误,initscr将返回ERR,否则将返回指针。

此外,屏幕将被删除并初始化。

endwin()将清除ncurses中所有已分配的资源,并将tty模式恢复为调用initscr()之前的状态 。

必须在ncurses库中的任何其他函数之前调用它,并且必须在程序退出之前调用endwin()。

当您想要输出到多个终端时,可以使用 newterm(...)而不是initscr()。

 创建地图

#include <curses.h>

void initNcurse()
{
        initscr();
        keypad(stdscr,1);

}
void gamePic()
{
        int row;
        int column;

        for(row=0;row<20;row++){
                if(row == 0){
                        for(column=0;column<20;column++){
                                printw("--");
                        }
                        printw("\n");
                }
                if(row>=0 && row<=19){
                        for(column=0;column<=20;column++){
                                if(column == 0 || column  == 20){
                                        printw("|");
                                }else{
                                        printw("  ");
                                }
                        }
                        printw("\n");
                }
                if(row == 19){
                        for(column=0;column<20;column++){
                                printw("--");

                        }                       
                        printw("\n");
                        printw("By Xiaoye");
                }
        }
}
int main()
{
        initNcurse();
        gamePic();
        
        getch();        
        endwin();
        return 0;
        
}

显示贪吃蛇身子 

#include <curses.h>
#include <stdlib.h>

struct Snake //创建蛇身结构体
{
        int row;
        int column;
        struct Snake *next;
};

struct Snake *head = NULL; 
struct Snake *tail = NULL;

void initNcurse()
{
        initscr();
        keypad(stdscr,1);

}

int hasSnakeNode(int i,int j)
{
        struct Snake *p;
        p = head;

        while(p != NULL){
                if(p->row == i && p->column ==j){
                        return 1;
                }
                p = p->next;
        }
        return 0;
}

void gamePic()
{
        int row;
        int column;

        for(row=0;row<20;row++){
                if(row == 0){
                        for(column=0;column<20;column++){
                                printw("--");
                        }
                        printw("\n");
                }
                if(row>=0 || row<=19){
                         for(column=0;column<=20;column++){
                                if(column == 0 || column ==20){
                                        printw("|");
                                }else if(hasSnakeNode(row,column)){    //检测到行和列有蛇身
                                        printw("[]");
                                }else{
                                        printw("  ");
                                }
                        }
                        printw("\n");
                }
                if(row == 19){
                        for(column=0;column<20;column++){
                                printw("--");
                        }
                        printw("\n");
                        printw("By Xiaoye");

                }
        }
}
void addNode()    //增添蛇身
{
        struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));
        new->row = tail->row;
        new->column = tail->column+1;
        new->next = NULL;

        tail->next = new;
        tail = new;
}

void initSnake()//初始化蛇头
{
        head = (struct Snake*)malloc(sizeof(struct Snake));
        head->row = 2;
        head->column = 2;
        head->next = NULL;

        tail = head;

        addNode();
        addNode();
        addNode();

}

int main()
{
        initNcurse();

        initSnake();

        gamePic();
        getch();
        endwin();
        return 0;

}

贪吃蛇向右移动 

void addNode()
{
        struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));
        new->row = tail->row;
        new->column = tail->column+1;
        new->next = NULL;

        tail->next = new;
        tail = new;
}

void deleNode()
{

// struct Snake *p;
// p = head;
        head = head->next;
//free(p);

}

void moveSnake()
{

        addNode();
        deleNode();
}

 地图需要每次从0开始,即光标归0

获取按键

   while(1){
                con = getch();
                if(con == KEY_RIGHT){
                        moveSnake();
                        gamePic();
                }
        }

 贪吃蛇撞墙

void initSnake()
{

        struct Snake *p; //中间变量

        while(head != NULL){ //一开始为空不执行
                p = head;
                head = head->next;
                free(p);//释放空间
        }

        head = (struct Snake*)malloc(sizeof(struct Snake));
        head->row = 1;
        head->column = 1;
        head->next = NULL;

        tail = head;

        addNode();
        addNode();
        addNode();

}

void moveSnake()
{

        addNode();
        deleNode();

        if(tail->row==0 || tail->column==0 || tail->row ==20 || tail->column ==20){
                initSnake();
        }//撞墙初始化
}

向右自由行走

 去除按键检测,自动每隔2毫秒向右,并刷新

   while(1){
                moveSnake();
                gamePic();
                refresh(); //更新缓冲区内容,输出之后缓冲区就没了
                usleep(200000); //延时
        }

Linux多线程 

#include<stdio.h>
#include<pthread.h>

void* func1()
{
        while(1){
                printf("this is func 1\n");
                sleep(1);
        }
}

void* func2()
{
        while(1){
                printf("this is func 2\n");
                sleep(1);
        }
}
int main()
{
        pthread_t th1;
        pthread_t th2;

        pthread_create(&th1,NULL,func1,NULL);
        pthread_create(&th2,NULL,func2,NULL);

        while(1);
        return 0;
}

在Unix/Linux系统中,C/C++提供了pthread(POSIX线程)API。它允许我们为并发流程创建多个线程,这可以提高程序在多核处理器或上的执行速度。

想要使用pthreads库的所有功能,我们必须在.c或.c++文件中包含pthread.h头文件,在编译文件时在命令行中使用 -pthread 或 -lpthread。

 a. pthread_create: 用于创建新线程

int pthread_create(pthread_t  * thread,
            const pthread_attr_t * attr,
            void * (*start_routine)(void *),
            void * arg);

参数:
thread:返回创建的线程的线程ID,是一个指向无符号整数值的指针。
attr: 默认值为NULL,目前没有用,不需要修改。是一个指向用于定义线程属性(如分离状态、调度策略等)的结构的指针。
start_routine: 是一个指向线程执行子程序的指针。子程序的返回类型和参数类型必须是 void * 类型。如果同时传递多个指针需要用到结构体。
arg: void类型的指针,其中包含前面参数中定义的函数的参数

b.pthread_exit: 用于终止线程

void pthread_exit(void * retval);

参数:retval: 它是一个指向整型变量的指针,该整数储存线程终止的返回状态。读取的整型变量必须为全局变量,以便让任何等待加入该线程的线程可以读取其返回状态。

支持方向变换

改变增加方式

void addNode()
{
        struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));

        new->next = NULL;

        switch(dir){
                case UP:
                        new->row = tail->row-1;
                        new->column = tail->column;
                        break;
                case DOWN:
                        new->row = tail->row+1;
                        new->column = tail->column;
                        break;
                case LEFT:
                        new->row = tail->row;
                        new->column = tail->column-1;
                        break;
                case RIGHT:
                        new->row = tail->row;
                        new->column = tail->column+1;
                break;
        }
        tail->next = new;
        tail = new;
}

按键改变方向 

void changeDir()
{
        while(1){
                key = getch();
                switch(key){
                        case KEY_DOWN:
                        dir = DOWN;
                        break;
                        case KEY_UP:
                        dir = UP;
                        break;
                        case KEY_LEFT:
                        dir = LEFT;
                        break;
                        case KEY_RIGHT:
                        dir = RIGHT;
                        break;
                }
        }
}

 修复不合理走位

绝对值方式

#define UP    1
#define DOWN  -1
#define LEFT  2
#define RIGHT -2

void turn(int direction)
{
        if(abs(dir) != abs(direction)){
                dir = direction;
        }
}

void changeDir()
{
        while(1){
                key = getch();
                switch(key){
                        case KEY_DOWN:
                        turn(DOWN);
                        break;
                        case KEY_UP:
                        turn(UP);
                        break;
                        case KEY_LEFT:
                        turn(LEFT);
                        break;
                        case KEY_RIGHT:
                        turn(RIGHT);
                        break;
                }
        }
}

bug:按键输入显示错误

增加noech(),不要把输入回显在屏幕上

noecho()是echo模式中的一个函数。ECHO模式即回显模式,ECHO模式用来决定用户的输入是否立即回显。

当ECHO模式设置后,它使得在键盘上输入的每一个字符都在终端屏幕上当前光标处显示出来,在调用某些函数如addch()的时候字符显示后光标的位置将自动的向后移动一个位置。

在非回显模式下,字符的显示必须由程序本身来完成,否则字符不会显示。

非回显模式下按键通常用来控制屏幕的操作而不是用来进行字符输入。

echo()用来设置回显模式,noecho()关闭回显模式。

默认情况下回显模式是打开的。

添加食物

#include <curses.h>
#include <stdlib.h>

#define UP    1
#define DOWN  -1
#define LEFT  2
#define RIGHT -2

struct Snake
{
        int row;
        int column;
        struct Snake *next;
};

struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;

struct Snake food;

void initFood()
{
        int x = rand()%20;
        int y = rand()%20;

        food.row = x;
        food.column = y;
}
void initNcurse()
{
        initscr();
        keypad(stdscr,1);
        noecho();
}

int hasSnakeNode(int i,int j)
{
        struct Snake *p;
        p = head;

        while(p != NULL){
                if(p->row == i && p->column ==j){
                        return 1;
                }
                p = p->next;
        }

        return 0;
}

int hasFood(int i,int j)
{
      if(food.row == i && food.column ==j){
         return 1;
      }

      return 0;
}
void gamePic()
{
        int row;
        int column;

        move(0,0);

        for(row=0;row<20;row++){
                if(row == 0){
                        for(column=0;column<20;column++){
                                printw("--");
                        }
                        printw("\n");
                }
                if(row>=0 || row<=19){
                         for(column=0;column<=20;column++){
                                if(column == 0 || column ==20){
                                        printw("|");
                                }else if(hasSnakeNode(row,column)){
                                        printw("[]");
                                }else if(hasFood(row,column)){
                                        printw("##");
                                }else{
                                        printw("  ");
                                }
                        }
                        printw("\n");
                }
                if(row == 19){
                        for(column=0;column<20;column++){
                                printw("--");
                        }
                        printw("\n");
                        printw("By Xiaoye,key=%d\n",key);

                }
        }
}

void addNode()
{
        struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));

        new->next = NULL;

        switch(dir){
                case UP:
                        new->row = tail->row-1;
                        new->column = tail->column;
                        break;
                case DOWN:
                        new->row = tail->row+1;
                        new->column = tail->column;
                        break;
                case LEFT:
                        new->row = tail->row;
                        new->column = tail->column-1;
                        break;
                case RIGHT:
                        new->row = tail->row;
                        new->column = tail->column+1;
                break;
        }
        tail->next = new;
        tail = new;
}
void initSnake()
{

        struct Snake *p;

        dir = RIGHT;

        while(head != NULL){
                p = head;
                head = head->next;
                free(p);
        }

        initFood();

        head = (struct Snake*)malloc(sizeof(struct Snake));
        head->row = 1;
        head->column = 1;
        head->next = NULL;

        tail = head;

        addNode();
        addNode();
        addNode();

}
void deleNode()
{
        struct Snake *p;
        p = head;
        head = head->next;
        free(p);
}

int ifSnakeDie()
{
        struct Snake *p;
        p = head;

        if(tail->row<0 || tail->column==0 || tail->row ==20 || tail->column ==20){
                return 1;
        }
        while(p->next!=NULL){
                if(p->row == tail->row && p->column == tail->column){
                        return 1;
                }
                p=p->next;
        }
        return 0;
}
void moveSnake()
{

        addNode();

        if(hasFood(tail->row,tail->column)){
                initFood();
        }else{
                deleNode();
        }

        if(ifSnakeDie()){
                initSnake();
        }
}

void refreshJieMian()
{
         while(1){
                moveSnake();
                gamePic();
                refresh();
                usleep(200000);
        }
}

void turn(int direction)
{
        if(abs(dir) != abs(direction)){
                dir = direction;
        }
}
void changeDir()
{
        while(1){
                key = getch();
                switch(key){
                        case KEY_DOWN:
                        turn(DOWN);
                        break;
                        case KEY_UP:
                        turn(UP);
                        break;
                        case KEY_LEFT:
                        turn(LEFT);
                        break;
                        case KEY_RIGHT:
                        turn(RIGHT);
                        break;
                }
        }
}

int main()
{
        pthread_t t1;
        pthread_t t2;

        initNcurse();

        initSnake();

        gamePic();
   pthread_create(&t1,NULL,refreshJieMian,NULL);
        pthread_create(&t2,NULL,changeDir,NULL);

        while(1);

        getch();
        endwin();
        return 0;

}
                                           

持续更新......

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值