C语言学习练习代码

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include <errno.h>

//练习54:ferror函数使用
int main()
{
	int c;//注意:int ,非char,要求处理EOF
	FILE* fp=fopen("test,txt","r");
	if(!fp)
	{
		perror("File opening failed");
		return -1;
	}
	//fgetc当读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while((c = fgetc(fp)!= EOF))//标准C I/O读取文件循环
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if(ferror(fp))
		puts("I/O error when reading");
	else if(feof(fp))
		puts("End of file reached successfully");
	//关闭文件
	fclose(fp);
	fp == NULL;
	return 0;
}

//练习53:perror函数使用,报错信息提示自定义
int main()
{
	//strerror-把错误码对应的错误信息的字符串地址返回
	//printf("%s\n",streeror);

	//perror
	FILE* pf = fopen("test2.txt","r");
	if(pf == NULL)
	{
		perror("hehe");//不需要传送错误码信息,hehe:错误信息
		return 0;
	}
	//读文件

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

练习52:文件的随机读取
//fseek和ftell和fwind
int main()
{
	char ch;
	//打开文件
	FILE* pf = fopen("test.txt","r");
	if(pf == NULL)
	{
		return 0;
	}
	//定义文件指针
	fseek(pf,2,SEEK_CUR);
	//读取文件
	ch= fgetc(pf);
	printf("%c\n",ch);//%c打印字符
	//关闭文件
	fclose(pf);
	pf=NULL;

	return 0;
}
int main()
{
	int pos = 0;
	//打开文件
	FILE* pf=fopen("test.txt","r");
	if(pf == NULL)
	{
		return 0;
	}
	//定位文件指针
	fseek(pf,-2,SEEK_END);
	pos = ftell(pf);
	printf("%d\n",pos);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}



练习51:二进制读写函数fread和fwrite
struct S 
{
	char name[20];
	int age;
	double score;
};
int main()
{
	struct S s={"张丹",20,21.5};
	FILE* pf=fopen("test.txt","wb");//以二进制形式写入
	if(pf == NULL)
	{
		return 0;
	}
	//二进制形式写文件
	fwrite(&s,sizeof(struct S),1,pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

int main()
{
	struct S temp={0};
	FILE* pf=fopen("test.txt","rb");//以二进制形式读取
	if(pf == NULL)
	{
		return 0;
	}
	//二进制形式读文件
	fread(&temp,sizeof(struct S),1,pf);
	printf("%s %d %lf\n",temp.name,temp.age,temp.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

//练习50:sscanf和sprintf
struct S
{
	int n;
	float score;
	char arr[10];
};

int main()
{
	struct S s={100,3.14f,"abcdef"};
	struct S tmp={0};
	char buf[1024]={0};

	sprintf(buf,"%d %f %s",s.n,s.score,s.arr);
	printf("%s\n",buf);
	sscanf(buf,"%d %f %s",&(tmp.n),&(tmp.score),&(tmp.arr));
	printf("%d %f %s\n",tmp.n,tmp.score,tmp.arr);
	return 0;
}


//练习49:文件操作2
//格式化输入输出函数fprintf();fscanf();
struct S
{
	int n;
	float score;
	char arr[10];
};
 int main()
 {
	 struct S s={100,3.14f,"bit"};
	 //打开文件
	 FILE* pf=fopen("test.txt","w");
	 //判断
	 if(pf == NULL)
	 {
		 return 0;
	 }
	 //格式化形式写文件
	 fprintf(pf,"%d %f %s",s.n,s.score,s.arr);
	 //关闭文件
	 fclose(pf);
	 pf = NULL;
	 return 0;
 }

//练习48:文件操作1
int main()
{
	int a=10000;
	FILE* pf=fopen("text.text","wb");
	fwrite(&a,4,1,pf);//二进制的形式写到文件中
	fclose(pf);
	pf=NULL;
	return 0;
}
int main()
{
	//打开二进制文件test.text.fopen打开
	//相对路径
	fopen("test.txt","r");
	//绝对路径,\要再加一个\进行转义
	fopen("D:\\C\\Review\\test11_24\\Project1\\test.txt","r");
	//..表示上一级路径
	//.表示当前路径
	fopen("..\\..\\test.txt","r");
	return 0;
}

int main()
{
	FILE* pf=fopen("text.txt","r");//文件指针接收返回值
	//判断是否打开成功,打开失败会返回null空指针
	if(pf == NULL)
	{
		printf("%s\n",strerror(errno));
		return 0;
	}
	else
	{
		printf("打开文件成功!\n");
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

//顺序读文件
int main()
{
	//打开或建立文件
	FILE* pfwrite = fopen("test.txt","w");
	//判断文件是否打开成功
	if(pfwrite == NULL)
	{
		printf("%s\n",strerror(errno));
	}
	//写文件
	fputc('b',pfwrite);
	fputc('i',pfwrite);
	fputc('t',pfwrite);

	//关闭文件
	fclose(pfwrite);
	pfwrite = NULL;
	return 0;
}

//顺序写文件
int main()
{
	FILE* pfread = fopen("test.txt","r");
	if(pfread == NULL)
	{
		printf("%s\n",strerror(errno));
		return 0;
	}
	//读文件
	printf("%c",fgetc(pfread));
	printf("%c",fgetc(pfread));
	printf("%c",fgetc(pfread));
	printf("\n");
	//关闭文件
	fclose(pfread);
	pfread = NULL;
	return 0;
}

//键盘-标准输入设备-stdout
//屏幕-标准输出设备-stdin
//是一个程序默认打开的两个流设备
//程序默认打开3个。分别是
//stdin FILE*//stdout FILE*//stderr FILE*
int main()
{
	int ch = fgetc(stdin);
	fputc(ch,stdout);
	return 0;
}

读取一行字符串
int main()
{
	//读取字符串的送往地址
	char buf[1024]={0};
	//打开文件
	FILE* pf=fopen("test.txt","r");
	//判断
	if(pf == NULL)
	{
		printf("%s\n",strerror(errno));
		return 0;
	}
	//读取
	fgets(buf,1024,pf);
	printf("%s\n",buf);

	fclose(pf);
	pf=NULL;

	return 0;
}

int main()
{
	//打开文件
	FILE* pf=fopen("test.txt","w");
	//判断
	if(pf == NULL)
	{
		printf("%s\n",strerror(errno));
		return 0;
	}
	//写入
	fputs("hello\n",pf);
	fputs("hello\n",pf);

	fclose(pf);
	pf=NULL;

	return 0;
}

int main()
{
	//从键盘读取一行文本信息
	char buf[1024]={0};
	fgets(buf,1024,stdin);//标准输入读取
	fputs(buf,stdout);//输出到标准输出流
//---------------
//	gets(buf);
//	puts(buf);
	return 0;
}

//练习47:柔性数组的使用2
struct S
{
	int n;
	int* arr;
};
int main()
{
	int i=0;
	int * ptr;
//动态开辟空间,分别两次
	struct S* ps=(struct S*)malloc(sizeof(struct S));
	ps->arr = (int *)malloc(5*sizeof(int));
	
	for(i=0;i<5;i++)
	{
		ps->arr[i]=i;
	}
	for(i=0;i<5;i++)
	{
		printf("%d ",ps->arr[i]);
	}
	printf("\n");
	//调整大小
	ptr=(int *)realloc(ps->arr,10*sizeof(int));
	if(ptr !=NULL)
	{
		ps->arr =ptr;
	}
	for(i=5;i<10;i++)
	{
		ps->arr[i]=i;
	}
	for(i=0;i<10;i++)
	{
		printf("%d ",ps->arr[i]);
	}
	//释放内存
	free(ps->arr);
	ps->arr =NULL;
	free(ps);
	ps=NULL;
	return 0;
}

//练习46:柔性数组的使用1
//优点:流程不易出错,方便内存释放,连续开辟一块动态内存,访问效率更高
struct S
{
	int n;
	int arr[];//大小可以调整,或者写为int arr[0];
};
int main()
{
	struct S*ps=(struct S*)malloc(sizeof(struct S)+5*sizeof(int));
	ps->n=100;
	int i=0;
	for(i=0;i<5;i++)
	{
		ps->arr[i]=i;
	}
	struct S* ptr=realloc(ps,44);
	if(ptr != NULL)
	{
		ps=ptr;
	}
	for(i=5;i<10;i++)
	{
		ps->arr[i]=i;
	}
	for(i=0;i<10;i++)
	{
		printf("%d",ps->arr[i]);
	}
	return 0;
}

//练习45:动态内存使用错误---非法访问内存
//返回栈空间地址的问题,局部变量
char *GetMemory(void)
{
	char p[]="hello world";//局部数组,在函数外之外生命周期结束了
	return p;
}
void Test(void)
{
	char* str=NULL;
	str=GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

//练习44:动态内存空间没有释放,内存泄露(错误示例)
void GetMemory(char* p)
{
	p=(char*)malloc(100);
}
void Test(void)
{
	char *str=NULL;
	GetMemory(str);
	strcpy(str,"hello world");
	printf(str);//没有错误,同printf("abcdef");
}
int main()
{
	Test();
	return 0;
}
//1、运行代码程序会出现崩溃现象
//2、程序存在内存泄漏的问题
//str以值传递的形式给p
//p是GetMemory函数的形参,只能函数内部有效,等GetNemory函数返回之后,动态开辟内存尚未释放并且
//无法找到,所以会造成内存泄露

改正1---传地址
void GetMemory(char** p)
{
	*p=(char*)malloc(100);
}
void Test(void)
{
	char *str=NULL;
	GetMemory(&str);
	strcpy(str,"hello world");
	printf(str);//没有错误,同printf("abcdef");
}
int main()
{
	Test();
	return 0;
}
改进2:使用return

//练习43:模拟实现memmove,处理内存重叠情况
//根据src和dest的大小来选择从前向后拷贝还是从后向前拷贝
//这个会报错,有问题,可修改的左值,迷惑,,,
void* my_memmove(void* dest, const void* src,size_t num)
{
	void* ret=dest;
	//
	assert(dest);
	assert(src);
	//判断
	if(dest < src)//从前向后
	{
		while(num--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++((char*)src);
		}
	}
	else
	{
		while(num--)
		{
			(*(char*)dest+num) = *((char*)src+num);
		}
	}

	return ret;
}
int main()
{
	int i=0;
	int arr1[]={1,2,3,4,5,6,7,8,9,10};
	my_memmove(arr1,arr1+3,3);
	for(i=0;i<10;i++)
	{
		printf("%d ",arr1[i]);
	}
	printf("\n");
	return 0;
}

//练习42:模拟实现memcpy
void* my_memcpy(void* dest,const void* src,size_t num)//size_t就是unsigned int
{
	void* ret = dest;
	//指针判断
	assert(dest);
	assert(src);
	//void*不能进行解引用,必须先强制转化
	while(num--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;
		++(char*)src;
	}
	return ret;
}
struct S{
	char name[20];
	int age;
};
int main()
{
	struct S arr1[]={{"zhangsan",20},{"lisi",25}};
	struct S arr2[3]={0};
	my_memcpy(arr2,arr1,sizeof(arr1));//num为整个数组的长度,单位为字节
	return 0;
}

//练习41:strtok函数的使用,分割1048916893@qq.com
int main()
{
	char str[]={"1048916893@qq.com"};
	char p[]={"@."};
	char* ret=0;
	//调用strtok函数会破坏原字符串,因此需要赋值
	char buf[256]={0};
	strcpy(buf,str);
	//调用切割
	ret=NULL;
	for(ret = strtok(str,p);ret != NULL;ret = strtok(NULL,p))
	{
		printf("%s\n",ret);
	}
	return 0;
}

//练习40:模拟实现strstr寻找子串函数
//难!!考虑全面
char * my_strstr(char* p1,char* p2)
{
	char* s1 = p1;
	char* s2 = p2;
	char* cur = p1;
	assert(p1 !=NULL);
	assert(p2 !=NULL);
	if(*p2 == '\0')
	{
		return p1;
	}
	while(*cur)
	{
		s1 = cur;
		s2 = p2;
		while((*s1 != '\0')&&(*s2 !='\0')&&(*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if(*s2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;

}

int main()
{
	char p1[]={"abcedf"};
	char p2[]={"ced"};
	char* ret=0;
	ret=my_strstr(p1,p2);
	if(ret != NULL)
	{
		printf("成功\n");
	}
	return 0;
}

//练习39:模拟实现strcmp函数
int my_strcmp(const char* str1,const char*str2)
{
	assert(str1);
	assert(str2);
	while(*str1 == *str2)
	{
		if(*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if(*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
return (*str1 - *str2);
int main()
{
	int ret=0;
	char p1[]={"zbcedfg"};
	char p2[]={"xyz"};
	ret=my_strcmp(p1,p2);
	printf("%d\n",ret);
	return 0;
}

//练习38:实现追加字符串函数模拟实现
//strcat
char* my_strcat(char* dest,const char* src)
{
	char* ret=dest;
	//1、找到目标字符串的\0
	while(*dest != '\0')
	{
		dest++;
	}
	//2、复制
	while(*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[30]={"hello"};
	char arr2[]={"world"};
	char* temp=0;
	temp=my_strcat(arr1,arr2);
	printf("%s\n",temp);
	return 0;
}

//练习37:在杨氏矩阵中寻找一个数字
//杨氏矩阵:每行每列都是递增
//要求:时间复杂度小于0(N)
//使用矩阵右上角或者左下角元素进行收缩,每次判断去掉一行或一列
//形参处必须声明,接收数组的大小,行列
int FindNum(int arr[3][3],int k,int row,int col)
{
	int x=0;
	int y=col-1;
	//确定右上角元素坐标
	while((x<=2)&&(y>=0))
	{
		if(arr[x][y]<k)
		{
			x++;
		}
		else if(arr[x][y]>k)
		{
			y--;
		}
		else
		{
			return 1;
		}
	}
	return 0;
}
int main()
{
	int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}};
	int ret=0;
	//封装一个函数,先设定再实现
	ret=FindNum(arr,7,3,3);
	if(ret==1)
	{
		printf("找到了!\n");
	}
	else
	{
		printf("找不到!\n");
	}
	return 0;
}
//改进:可以通过&x,&y返回型参数来带回坐标值


//练习36:判断两个字符串,是否由左旋关系
//abcdefgabcdefg包含左旋后的所有情况--因此寻找此字串
//
int is_move(char* arr1,char* arr2)
{
	int len1=0;
	int len2=0;
	char* temp=NULL;
	//空指针判断
	assert(arr1);
	assert(arr2);
	//1、追加字符串
	len1=strlen(arr1);
	len2=strlen(arr2);
	if(len1 != len2)
	{
		return 0;
	}
	strncat(arr1,arr1,len1);
	//2、判断子串
	temp=strstr(arr1,arr2);
	if(temp==NULL)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}
int main()
{
	//不能给常量字符串,要以数组形式,可以改变
	char arr1[20]={"abcdef"};//arr1[]不能空着,一定要开辟足够大的空间
	char arr2[]={"defabc"};
	int ret=0;
	ret=is_move(arr1,arr2);
	if(ret==1)
	{
		printf("YES!\n");
	}
	else
	{
		printf("NO!\n");
	}
	return 0;
}


练习35:左旋字符串
不能使用常量字符串,因为不能被修改,应该使用数组

//1、暴力求解法
void l_rotate(char *arr,int k)
{
	int len=0;
	char temp='0';
	int i=0;
	//传入参数有指针,判断assert
	assert(arr);
	len=strlen(arr);//到0停止,不包含0
	for(i=0;i<k;i++)
	{
		int j=0;
		//1----把第一位拿出来
		temp=*arr;
		//2----后面依次往前移
		for(j=0;j<len-1;j++)
		{
			*(arr+j)=*(arr+j+1);
		}
		//3----把第一位放最后
		*(arr+len-1)=temp;
	}
}
int main()
{
	char arr[]={"abcdef"};
	int k=0;
	l_rotate(arr,3);
	printf("%s\n",arr);
	return 0;
}
//2、三步逆序法
//abcdef
//ba fedc
//cdefab
void reverse(char* left,char* right)
{
	//判断空指针
	assert(left);
	assert(right);
	while(left<right)
	{
		char temp=0;
		temp=*left;
		*left=*right;
		*right=temp;
		left++;
		right--;
	}
}
void left_rotate(char *arr,int k)
{
	int len=0;
	//判断空指针
	assert(arr);
	len=strlen(arr);
	reverse(arr,arr+k-1);//逆序左边,输入需要字符串的左边右边指针
	reverse(arr+k,arr+len-1);//逆序右边
	reverse(arr,arr+len-1);//整个逆序
}
int main()
{
	char arr[]={"abcdefg"};
	left_rotate(arr,3);
	printf("%s\n",arr);
	return 0;
}


//练习34:预测比赛结果
//A:B第二;我第三
//B:我第二,E第四
//C:我第一,D第二
//D:C最后,我第三
//E:我第四,A第一
//比赛结束后,每位选手都说对了一半,请编程确定比赛的名次

int main()
{
	int a=0;
	int b=0;
	int c=0;
	int d=0;
	int e=0;
	for(a=1;a<=5;a++)
	{
		for(b=1;b<=5;b++)
		{
			for(c=1;c<=5;c++)
			{
				for(d=1;d<=5;d++)
				{
					for(e=1;e<=5;e++)
					{
						if(((b==2)+(a==3)==1)
							&&((b==2)+(e==4)==1)
							&&((c==1)+(d==2)==1)
							&&((c==5)+(d==3)==1)
							&&((e==4)+(a==1)==1))
						{
							if(a*b*c*d*e==120)
							{
								printf("a==%d,b=%d,c=%d,d=%d,e=%d\n",a,b,c,d,e);
							}
						}
					}
				}
			}
		}
	}
	return 0;
}

//练习33:判断凶手
//A说:不是我
//B说:是C
//C说:是D
//D说:C在胡说
//已知3个人说了真话,1个人说的是假话
//判断谁是凶手

int main()
{
	char killer ='0';
	//依次假设ABCD为凶手
	for(killer='a';killer<='d';killer++)
	{
		if((killer!='a')+(killer=='c')+(killer=='d')+(killer!='d')==3)
		{
			printf("killer = %c\n",killer);
		}
	}
	return 0;
}



练习32:打印杨辉数列
1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1

int main()
{
	int arr[10][10]={0};
	int i=0;
	int j=0;
	//首先将第一列和对角列全部置1
	for(i=0;i<10;i++)
	{
		for(j=0;j<=i;j++)
		{
			//第一列和对角列
			if((j==0)||(i==j))
			{
				arr[i][j]=1;
			}
			//中间
			if((i>=2)&&(j>=1))
			{
				arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
			}
		}
	}
	//打印
	for(i=0;i<10;i++)
	{
		for(j=0;j<=i;j++)
		{
			printf("%d ",arr[i][j]);
			
		}
		printf("\n");
	}
	return 0;
}

//练习31:调整数组内容使奇数全部位于偶数前面
//有一些问题,arr[left]和arr[right]
//数组:1 2 3 4 5 6 7 8 9 10

void move(int *arr,int size)
{
	int *left=arr;
	int *right=arr+size-1;
	while(left<right)
	{
		//从左边找偶数
		while((left<right)&&((*left)/2==1))
		{
			left++;
		}
		//从右边找奇数
		while((left<right)&&((*right)/2==0))
		{
			right--;
		}
		//交换
		if(left<right)
		{
			int temp=0;
			temp=*left;
			*left=*right;
			*right=temp;
		}
	}
}

void print(int *arr,int size)
{
	int i=0;
	for(i=0;i<size;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
}

 int main()
 {
	 int arr[]={1,2,3,4,5,6,7,8,9};
	 int sz=0;
	 sz=sizeof(arr)/sizeof(arr[0]);
	 move(arr,sz);
	 print(arr,sz);
	 return 0;
 }

//练习30:换汽水喝
int main()
{
	//汽水分为买的+换的
	int money=0;
	int total=0;
	int empty=0;

	printf("请输入金额:\n");
	scanf("%d",&money);
	//买的汽水
	total=money;
	//换的汽水
	empty=money;
	while(empty>=2)
	{
		total += empty/2;
		empty=empty/2+empty%2;
	}
	printf("汽水数为:%d\n",total);
	return 0;
}


//练习29:找出1-10000的水仙花数
int main()
{
	int i=0;
	//1、产生1-10000数字
	for(i=0;i<100000;i++)
	{
		int n=1;//记录每个i的位数
		int tmp=i;
		int ret=0;
		//2、判断i
		//123/10 n++
		//12/10 n++
		//1/10=0
		while(tmp/=10)
		{
			n++;
		}
		//3、计算每位数的n次方之和
		//得到每位数字%10
		tmp=i;
		while(tmp)
		{
			ret=ret+pow(tmp%10,n);
			tmp/=10;
		}
		
		//4、判断是否等于i,即为水仙花数
		if(ret==i)
		{
			printf("%d ",i);
		}
	}
	return 0;
}

//练习28:计算实现a+aa+aaa+aaaa+……
int main()
{
	int a=0;
	int n=0;
	int sum=0;
	int i=0;
	int ret=0;
	printf("请输入a和n:\n");
	scanf("%d%d",&a,&n);	
	for(i=0;i<n;i++)
	{
		ret=a+10*ret;
		sum=sum+ret;
	}
	printf("结果为:%d\n",sum);
	return 0;
}

//练习27:函数实现,字符串逆序
void reverse(char *str)
{
	
	int len=strlen(str);
	char *left=str;
	char *right=str+len-1;

	assert(str);

	while(left<right)
	{
		char temp=0;
		temp=*left;
		*left=*right;
		*right=temp;
		left++;
		right--;
	}
}
int main()
{
	char arr[256]={0};
	printf("请输入字符串:\n");
	//scanf("%s",arr);//abcdef
	gets(arr);
	reverse(arr);//逆序函数

	printf("逆序后的字符串:%s\n",arr);
	return 0;
}

//二维数组
int main()
{
	int a[3][4]={0};
	printf("%d\n",sizeof(a));//48,sizeof(数组名)计算的是整个数组的大小,
	printf("%d\n",sizeof(a[0][0]));//4,计算首元素大小,单位字节
	printf("%d\n",sizeof(a[0]));//16,a[0]代表第一行作为一维数组的数组名
	//sizeof(arr[0])把数组名单独放在sizeof()内,计算的是第一行的大小
	printf("%d\n",sizeof(a[0]+1));//4/8,没有单独方a[0],此时a[0]代表的是一维数组的首元素地址,
	//把二维数组看做一维数组,首元素就是第一行数组,a[0]+1就是第2行数组地址,地址大小为4/8
	printf("%d\n",sizeof(*(a[0]+1)));//4,a[0]+1解引用是第一行第二个元素,大小为4个字节
	printf("%d\n",sizeof(a+1));//4/8,没有单独,所以是首元素地址,二维数组的首元素就是第一行数组,a+1就是第2行数组地址,地址大小为4/8
	printf("%d\n",sizeof(*(a+1)));//16,对第2行的数组地址解引用,计算的是数组的大小
	printf("%d\n",sizeof(&a[0]+1));//4/8,地址,第2行的地址
	printf("%d\n",sizeof(*(&a[0]+1)));//16,计算第2行数组长度
	printf("%d\n",sizeof(*a));//16,a是首元素地址,第一行地址,*a就是第一行,sizeof(*a)就是计算第一行的大小
	printf("%d\n",sizeof(a[3]));//16,不会真的访问第4行,只知道是一个一维4列数组

	return 0;
}

//字符数组
//strlen计算字符串长度,给地址,到\0为止
int main()
{
	char arr[]={'a','b','c','d','e','f'};
	printf("%d\n",strlen(arr));//随机值
	printf("%d\n",strlen(arr+0));//随机值
	printf("%d\n",strlen(*arr));//不合法
	printf("%d\n",strlen(arr[1]));//不合法
	printf("%d\n",strlen(&arr));//数组地址,随机值
	printf("%d\n",strlen(&arr+1));//随机值
	printf("%d\n",strlen(&arr[0]+1));//随机值

	return 0;
}

//字符串数组
int main()
{
	char arr[]="abcedf";

	printf("%s\n,sizeof(arr)");//7,sizeof(arr)计算的是数组的大小,单位是字节
	printf("%s\n,sizeof(arr+0)");//4/8,代表的是首元素地址,地址大小是4/8
	printf("%s\n,sizeof(*arr)");//1,计算的是首元素的大小,1字节
	printf("%s\n,sizeof(arr[1])");//1,首元素大小,1字节
	printf("%s\n,sizeof(&arr)");//4/8,取地址是整个数组的地址,但还是地址,地址大小为4/8字节
	printf("%s\n,sizeof(&arr+1)");//4/8,&arr+1是跳过整个数组的地址,还是地址,大小是4/8字节
	printf("%s\n,sizeof(&arr[0]+1)");//4/8,第二个元素的地址,地址大小为4/8字节

	return 0;
}


//一维数组
//数组名是首元素的地址
//1、sizeof(数组名)-数组名表示整个数组
//2、&数组-数组名表示整个数组
int main()
{
	int a[]={1,2,3,4};
	printf("%d\n",sizeof(a));//16,sizeof(数组名)-计算的是整个数组的总大小,sizeof的单位都是字节
	printf("%d\n",sizeof(a+0));//4/8,数组名这里表示首元素的地址,a+0还是首元素地址,地址的大小就是4/8字节
	printf("%d\n",sizeof(*a));//4,a表示首元素地址,*a解引用表示首元素,首元素大小是4字节
	printf("%d\n",sizeof(a+1));//4/8,a+1表示第2个元素的地址,为4/8
	printf("%d\n",sizeof(a[1]));//4,表示第2个元素,大小为4字节
	printf("%d\n",sizeof(&a));//4/8,&a表示整个数组地址,但也是地址,所以是4/8
	printf("%d\n",sizeof(*&a));//16,对整个数组地址解引用,即为整个数组,整个数组大小为16字节
	printf("%d\n",sizeof(&a+1));//4/8,&a是数组的地址,&a+1虽然地址跳过整个地址,但是还是地址,大小为4/8字节
	printf("%d\n",sizeof(&a[0]));//4/8,对首元素取地址,&a[0]是首元素地址
	printf("%d\n",sizeof(&a[0]+1));//4/8,&a[0]+1是第2个元素
	return 0;
}



//练习26:使用函数指针数组来进行计算器实现
方式2:使用回调函数,使用函数指针

void menu()
{
	printf("**************************\n");
	printf("***1.Add         2.Sub****\n");
	printf("***3.Mul         4.Div****\n");
	printf("*********0.exit***********\n");
	printf("**************************\n");
}
int Add(int x,int y)
{
	return x+y;
}
int Sub(int x,int y)
{
	return x-y;
}
int Mul(int x,int y)
{
	return x*y;
}
int Div(int x,int y)
{
	return x/y;
}

void cal(int (*p)(int,int))
{
	int x=0;
	int y=0;
	int ret=0;
	printf("请输入两个操作数:");
	scanf("%d%d",&x,&y);
	ret=p(x,y);
	printf("结果是%d\n",ret);
}
int main()
{
	int input=0;
	
	do{
		menu();
		printf("请选择数字:");
		scanf("%d",&input);
		switch(input)
		{
		case 0 : 
			printf("退出\n");
			break;
		case 1 :
			cal(Add);
			break;;
		case 2 :
			cal(Sub);
			break;
		case 3 :
			cal(Mul);
			break;
		case 4 :
			cal(Div);
			break;
		default:
			printf("选择错误\n");
		}
	}while(input);
	return 0;
}



//方式1:使用函数指针数组

void menu()
{
	printf("**************************\n");
	printf("***1.Add         2.Sub****\n");
	printf("***3.Mul         4.Div****\n");
	printf("*********0.exit***********\n");
	printf("**************************\n");
}
int Add(int x,int y)
{
	return x+y;
}
int Sub(int x,int y)
{
	return x-y;
}
int Mul(int x,int y)
{
	return x*y;
}
int Div(int x,int y)
{
	return x/y;
}
int main()
{
	int input=0;
	int x=0;
	int y=0;
	int ret=0;
	int(*pa[])(int,int)={0,Add,Sub,Mul,Div};
	do{
		menu();
		printf("请选择数字:");
		scanf("%d",&input);
		if(input <=4 && input >=1)
		{
			printf("请输入两个操作数:");
			scanf("%d%d",&x,&y);
			ret = pa[input](x,y);
			printf("结果是:%d\n",ret);
		}
		else if(input == 0)
		{
			printf("退出\n");
			break;
		}
		else
		{
			printf("选择错误\n");
		}
	}while(input);
	return 0;
}

//函数指针数组
int Add(int x,int y)
{
	return x + y;
}
int Sub(int x,int y)
{
	return x - y;
}
int Mul(int x,int y)
{
	return x * y;
}
int Div(int x,int y)
{
	return x / y;
}

int main()
{
	//指针数组
	int * arr[5];
	//需要一个数组,这个数组可以存放4个函数的地址-函数指针的数组
	int (*pa)(int,int)=Add;
	int (*parr[4])(int,int)={Add,Sub,Mul,Div};//函数指针的数组
	int i=0;
	for(i=0;i<4;i++)
	{
		printf("%d\n",parr[i](2,3));
	}
	return 0;
}


//二级指针传参
void test(char **p)
{
}
int main()
{
	char c='b';
	char*pc=&c;
	char**ppc=&pc;
	char* arr[10];
	test(&pc);//可以传一维指针的地址
	test(ppc);//一维指针的指针
	test(arr);//一维指针数组的数组名
	return 0;
}

二维数组传参
void test(int *arr)//err
{
}
void test(int* arr[5])/err
{
}
void test(int (*arr)[5])//right
{
}
void test(int **arr)//err
{
}
int main()
{
	int arr[3][5]={0};
	test(arr);
}

//一维数组传参
#include <stdio.h>
 void test(int arr[])
 {
 }
void test(int arr[10])
{
}
void test(int *arr)
{
}
void test2(int *arr[20])
{
}
void test2(int **arr)
{
}
int main()
{
	int arr[10]={0};
	int *arr2[20]={0};
	test(arr);
	test2(arr2);
	return 0;
}


练习25:判断大端小端字节序存储模式
指针类型决定指针解引用可以访问的字节数
版本1
int main()
{
	int a=1;
	char* p=(char*)&a;
	if(*p == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}
//版本2:使用函数,精简
int check_sys()
{
	int a=1;
	return *(char*)&a;
}

int main()
{
	int ret=0;
	//定义一个函数,返回值为1即为小端;返回值为0即为大端
	ret=check_sys();
	if(ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

//练习24:使用指针实现strcpy
void my_strcpy(char* des,char*src)
{
	while(*src!='\0')
	{
		*des=*src;
		des++;
		src++;
	}
	*des=*src;
}
///改进1:更加精简,判断条件更精妙
void my_strcpy(char* des,char*src)
{
	while(*des++=*src++)
	{
		;	
	}
}
///改进2:先判断指针的有效性,避免程序出问题,但是容易隐匿错误
void my_strcpy(char* des,char*src)
{
	if(des != NULL && src!= NULL)
	{
		while(*des++=*src++)
		{
			;	
		}
	}
}
///改进3:使用assert()断言,发现错误时,及时报错
//包含头文件
#include <assert.h>
void my_strcpy(char* des,char*src)
{
	assert(des != NULL);//断言,符合条件时继续执行,不符合时报错提示
	assert(src != NULL);//断言判断!!

	while(*des++=*src++)
	{
		;	
	}
}

改进4:使用const,char*
包含头文件,加上返回函数,函数功能更丰富
#include <assert.h>
char* my_strcpy(char* des,const char*src)
{
	char* ret=des;
	assert(des != NULL);//断言,符合条件时继续执行,不符合时报错提示
	assert(src != NULL);//断言判断!!

	while(*des++=*src++)
	{
		;	
	}
	return ret;
}

int main()
{
	char arr1[]={"#####################"};
	char arr2[]={"bit"};
	my_strcpy(arr1,arr2);
	printf("%s\n",arr1);
	return 0;
}


//练习23:递归计算N的K次方
//n^k=n*n^(k-1)
double Power(int n,int k)
{
	if(k<0)
		return 1.0/Power(n,(-k));
	else if(k==0)
		return 1.0;
	else
		return n*Power(n,k-1);
}
int main()
{
	int n=0;
	int k=0;
	double ret=0.0;
	scanf("%d%d",&n,&k);
	ret=Power(n,k);
	printf("%lf\n",ret);
	return 0;
}

//练习22:使用递归来计算一个非负整数各位数字之和
//1729
//DigitSum(172)+9%10
//DigitSum(17)+2%10+9%10
//DigitSum(1)+7%10+2%10++9%10
//1%10+7%10+2%10++9%10
int DigitSum(int num)
{
	int count=0;
	if(num>9)
		return count=DigitSum(num/10)+num % 10;
	else
		return num;
}

int main()
{
	int num=0;
	int sum=0;
	printf("请输入数字:");
	scanf("%d",&num);
	sum=DigitSum(num);
	printf("%d\n",sum);
	return 0;
}

//练习21:分别打印二进制数中的奇数位和偶数位
//32:0000000000000000000000000000000011

void print(int m)
{
	int i=0;
	printf("偶数位:\n");
	for(i=30;i>=0;i=i-2)
	{
		printf("%d ",(m>>i)&1);
	}
	printf("\n");
	printf("奇数位:\n");
	for(i=31;i>=0;i=i-2)
	{
		printf("%d ",(m>>i)&1);
	}
	printf("\n");
}
int main()
{
	int num=0;
	printf("请输入数字:\n");
	scanf("%d",&num);
	print(num);
	return 0;
}


//练习20:数一个二进制数中的1的个数
//这个是最优解!!!!!
//n=n&(n-1)的次数,直至n!=0
//这个循环可以执行的次数=二进制中的1的个数

//计算子函数
//int count_bit_one(int n)
//{
//	int count=0;
//	while(n)
//	{
//		n=n&(n-1);
//		count++;
//	}
//	return count;
//}

//练习19:数组练习
//自己写函数实现1、数组元素初始化
//2、打印数组元素
//3、reverse逆序排列数组元素

//初始化函数
void Init(int arr[],int size)
{
	int i=0;
	for(i=0;i<size;i++)
	{
		arr[i]=i;
	}
}
//打印数组元素
void Print(int arr[],int size)
{
	int i=0;
	for(i=0;i<size;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
}
//逆序数组元素
void Reverse(int arr[],int size)
{
	int left=0;
	int right=size-1;
	while(left<=right)
	{
		int temp=0;
		temp=arr[right];
		arr[right]=arr[left];
		arr[left]=temp;
		left++;
		right--;
	}
}

int main()
{
	int arr[10]={0};
	int size=0;
	size=sizeof(arr)/sizeof(arr[0]);
	//初始化数组元素
	Init(arr,size);
	//打印数组元素
	Print(arr,size);
	//逆序数组
	Reverse(arr,size);
	//打印数组元素
	Print(arr,size);
	return 0;
}

//练习18:实现strlen,用指针-指针
//
int my_strlen(char* arr)
{
	char* str=arr;
	char* ptr=arr;
	while(*ptr != '\0')
	{
		ptr++;
	}
	return ptr-str;
}
int main()
{
	char arr[]={"bityyyy"};
	int num=0;
	num=my_strlen(arr);
	printf("数组长度为%d\n",num);
	return 0;
}

//练习17:一个二进制数在内存中1的个数
//方式一:模2除2在处理负数时有问题(可以转换为unsigned int)可以解决负数问题
//方式二:依次右移,与1
int main()
{
	int num=-1;
	int count=0;
	int i=0;
	/*while(num)
	{
		if(num%2 == 1)
		{
			count++;
		}
		num=num/2;
	}*/
	for(i=0;i<32;i++)
	{
		if(1 == ((num>>i)&1))
		{
			count++;
		}
	}
	printf("%d\n",count);
	return 0;
}


//练习16:冒泡排序法,升序排序
//冒泡排序函数
//数组传过去的是数组首元素的地址,&arr[0]
//arr[]为一个指针
void bubble_sort(int arr[],int size)
{
	//这个数组元素为size=9,需要排8趟,size-1
	int i=0;
//采用flag对冒泡排序进行算法优化
	for(i=0;i<size;i++)
	{
		//每趟需要比较
      int flag=1;
	    int j=0;
		for(j=0;j<size-1-i;j++)
		{
			if(arr[j]>arr[j+1])
			{		
				int temp=0;
				temp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=temp;
                flag=0;
			}
		}
      if(flag==1)
      {
           break;
       }
	}
}
//主函数
int main()
{
	int arr[]={9,8,11,6,5,4,20,2,1};
	int size=0;
	int i=0;
	size=sizeof(arr)/sizeof(arr[0]);
	//排序函数
	bubble_sort(arr,size);
	for(i-0;i<size;i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}


//练习15:求N的阶乘
//方式一:循环
//方式二:递归调用
int fac1(int n)
{
	int i=0;
	int ret=1;
	for(i=1;i<=n;i++)
	{
		ret=ret*i;
	}
	return ret;
}
int fac2(int n)
{
	if(n<=1)
	{
		return 1;
	}
	else
	{
		//!!!!!!!!!!!!!!!
		return n*fac2(n-1);
	}
}
int main()
{
	int n=0;
	int ret=0;
	printf("请输入n:\n");
	scanf("%d",&n);
	ret=fac2(n);
	printf("%d\n",ret);
	return 0;
}


//练习14:求字符串的长度
//方式一:
int my_len(char* arr)
{
	int count=0;
	while(*arr !='\0')
	{
		count++;
		arr++;
	}
	return count;
}

方式二:不使用创建临时变量,函数递归!!
分解;
!!!!!大事化小!!!!!!
1+my_len("it")
1+1+my_len("t")
1+1+1+my_len("\0")
1+1+1+0
函数的递归使用
int my_len(char* arr)
{
	if(*arr != '\0')
	{
		return 1+my_len(arr+1);
	}
	else
	{
		return 0;
	}
}

主函数体
int main()
{
	//字符型要用char
	char arr[]="bit";
	int num=0;
	num=my_len(arr);
	printf("%d\n",num);
	return 0;
}



//练习13:函数的递归
//打印出输入数字的每一位数字,使用函数递归的方式
//递归的两个条件:1、存在限制条件,2、每次递归越接近这个条件
void print(int input)
{
	if(input>10)
	{
		print(input/10);
	}
	printf("%d ",input%10);
}
int main()
{
	int input;
	printf("请输入数字:\n");
	scanf("%d",&input);
	//函数调用
	print(input);
	return 0;
}



//练习12:
//猜数字游戏

//游戏(子函数)
void game()
{
	int ret=0;
	int guess=0;
	printf("欢迎来到猜数字游戏!\n");
	printf("请输入猜测数字:\n");
	//系统随机生成随机数
	//利用不断随时间变化的时间戳来作为生成随机数的变化变量
	//time()函数可以获取系统时间戳,NULL为空指针
	srand((unsigned int)time(NULL));
	ret=rand();
	ret = ret%100;	
	//printf("%d\n",ret);
	do
	{
		scanf("%d",&guess);
		if(guess>ret)
		{
			printf("猜大了\n");
		}
		else if(guess<ret)
		{
			printf("猜小了\n");
		}
		else
		{
			printf("恭喜你!猜对了!\n");
			break;
		}
	}
	while(1);
}

//主界面(子函数)
void win()
{
	printf("*******************************\n");
	printf("**********猜数字游戏***********\n");
	printf("*******************************\n");
	printf("请输入1或0,1开始游戏,0结束游戏\n");
}

int main()
{
	int input=0;	
	do
	{
		win();
	    scanf("%d",&input);
		switch(input)
		{
			case 1 :
				game();
				break;
			case 0 :
				printf("游戏结束!\n");
				break;
			default:
				printf("选择错误,请重新选择!\n");
				break;
		}
	}
	while(input);
	return 0;
}


练习11:九九乘法表
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
,,,
9*1=9 ,,,9*9=9

int main()
{
	int i=0;
	//确定行数
	for(i=1;i<=9;i++)
	{
		//确定每行项数(列数)
		int j=0;
		for(j=1;j<=i;j++)
		{
			//%-2d可以更好地左对齐,%2d为右对齐,不够2位空格补齐
			printf("%d*%d=%-2d ",i,j,i*j);
		}
		printf("\n");
	}
	return 0;
}




//练习10:分数求和
//1/1-1/2+1/3。。。。+1/99-1/100
int main()
{
	int i=0;
	double sum=0.0;
	int flag=1;
	for(i=1;i<=100;i++)
	{
		sum += flag*(1.0/i);
		flag=-flag;
	}
	printf("%lf\n",sum);
	return 0;
}



//练习9:判断输出100-200的素数
//试除法
//一个一个除->到sqrt->到在非偶数中找素数
//注意提高代码的效率,培养自己的代码思想
int main()
{
	int i=0;
	int j=0;
	int count=0;
	for(i=101;i<=200;i+=2)
	{
		for(j=2;j<= sqrt(i);j++)
		{
			if(i%j==0)
			{
				break;
			}
		}
		if(j> sqrt(i))
		{
			printf("%d ",i);
			count++;
		}
	}
	printf("\ncount=%d ",count);
	return 0;
}



//练习8:判断输出1000到2000内的闰年
//闰年的判断
//1、被4整除同时不被100整除
//2、被400整除
int main()
{
	int count=0;
	int i=0;
	for(i=1000;i<=2000;i++)
	{
		if(((i%4==0) && (i%100!=0))||(i%400==0))
		{
			count++;
			printf("%d ",i);
		}
	}
	printf("\n,count=%d",count);
	return 0;
}

//练习7:求出两个数的最大公约数
//辗转相除法
int main()
{
	int m=0;
	int n=0;
	int r=0;
	printf("请输入2个数字:\n");
	scanf("%d%d",&m,&n);
///程序效率会更高
wile(r=m%n)
	while(m%n)
	{
		r=m%n;
		m=n;
		n=r;
	}
	printf("最大公约数为%d",n);
}


//练习6:输出1至100中3的倍数
//!!!!!!!有一点疑问,怎么控制逢5换行
int main()
{
	int i=0;
	int j=0;
	for(i=0;i<=100;i++)
	{
		if(i%3 ==0)
		{
			j++;
			printf("%d ",i);
		}
		if(j%5 == 0)
			printf("\n");
	}
	printf("\n");
	return 0;
}





//练习5:比较3个数中的最大,中,最小
//按从大到小的顺序输出

int main()
{
	int a=0;
	int b=0;
	int c=0;
	printf("请输入3个数字:\n");
	scanf("%d%d%d",&a,&b,&c);

	if(a<b)
	{
		int temp=a;
		a=b;
		b=temp;
	}
	if(a<c)
	{
		int temp=a;
		a=c;
		c=temp;
	}
	if(b<c)
	{
		int temp=b;
		b=c;
		c=temp;
	}
	
	printf("%d %d %d",a,b,c);

	return 0;
}



//练习题4 
//演示多个字符从两端移动,向中间汇聚

int main()
{
	char arr1[]={"Welcome to C world !"};
	char arr2[]={"*********************"};
	int num1=sizeof(arr1)/sizeof(arr1[0]);
	int num2=sizeof(arr2)/sizeof(arr2[0]);
	
	int left=0;
	int right=num1-1;

	printf("%s\n",arr1);

	while(left <= right)
	{
		arr1[left]=arr2[left];
		arr1[right]=arr2[right];
		printf("%s\n",arr1);
		left++;
		right--;
	}

	return 0;
}






//练习题3,使用二分法查找
int main()
{
	int arr[]={1,2,3,4,5,6,7,8,9};
	int m=0;
	int mid=0;
	int num=sizeof(arr)/sizeof(arr[0]);
	int left=0;
	int right=num-1;

	printf("请输入查找的数字:");
	scanf("%d",&m);
		
	while(left<=right)
	{
		mid=(left+right)/2;
		if(m>arr[mid])
		{
			left=mid+1;
		}
		else if(m<arr[mid])
		{
			right=mid-1;
		}
		else
		{
			printf("成功找到数字!下标为%d\n",mid);
			break;
		}
	}
	if(left>right)
	{
		printf("找不到!\n");
	}
	return 0;
}




//练习题3,在一个有序数组中查找某一个数字,
//第一种方法:按序查找
int main()
{
	int arr[]={1,2,3,4,5,6,7,8,9};
	int i=0;
	int m=0;
	int num=0;
	num=sizeof(arr)/sizeof(arr[0]);//确定数组元素个数
	printf("请输入需要查找的数字:");
	scanf("%d",&m);
	for(i=0;i<num;i++)
	{
		if(m==arr[i])
		{
			printf("已经成功找到数字,下标为%d\n",i);
			break;
		}
	}
	if(i==num)
	{
		printf("查找失败!\n");
	}
	return 0;
}



//练习题2,计算1!+2!+3!+...+n!
int main()
{
	//int i=1;
	int s=0;
	int n=1;
	int sum=1;
	int sum_0=0;
	printf("请输入数字s:");
	scanf("%d",&s);
	for(n=1;n<=s;n++)
	{		
		sum=sum*n;

		sum_0 = sum_0 + sum;
	}
	printf("n!和为:%d\n",sum_0);
	return 0;
}


练习题1-计算N的阶乘n!

int main()
{
	int i=1;
	int n=0;
	int sum=1;
	printf("请输入数字n:");
	scanf("%d",&n);

	for(i=1;i<n+1;i++)
	{
		sum=sum*i;
	}
	printf("n!为:%d\n",sum);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值