今日收获(C语言)

1.更加安全的自定义strlen函数:

在之前的博客中,我写过自定义strlen函数。今天来写一个更加安全的版本:

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* a)     //只需要利用a数组的首地址来求出字符串长度,*a不应该被改变
{
	assert(a != NULL);           //a数组的首地址不应该是一个空指针,如果是的话就报警告
	int count = 0;
	while (*a != '\0')
	{
		count++;
		a++;
	}
	return count;
}
int main()
{
	char a[10] = { 0 };
	gets(a);
	int ret = my_strlen(a);
	printf("%d", ret);
	return 0;
}

2.有关指针的强制类型转换:

看下面的代码:

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	short* p = (short*)a;
	int i = 0;
	for (i = 0;i < 4;i++)
	{
		*(p + i) = 0;
	}
	for (i = 0;i < 5;i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

下面这行代码我使用了强制类型转换,a数组的首地址本来是int*类型的指针,被强制转换成了short*类型。

short* p = (short*)a;

在第一个for循环中,循环内部执行了4次。

但值得注意的是  “p+i” 。p每次只向后移动2个字节,而数组里的数每个都占4个字节,比如说          a[0]=1,占4个字节就是 00 00 00 01(16进制码),而内存中是倒着储存的。

即:01 00 00 00 02 00 00 00 03 00 00 00....

p每次向右移动两个字节,并且*(p+i)被赋为0,所以经过4次循环:

01 00 00 00 02 00 00 00 会变成 00 00 00 00 00 00 00 00

当最后用for循环输出a数组时,得到的结果就是:0 0 3 4 5。

再来看下面的代码:

#include<stdio.h>
int main()
{
	int a = 0x11223344;
	short* p = (short*)&a;
	*p = 0;
	printf("%x", a);
	return 0;
}

我以16进制的形式初始化a,然后取a的地址,强制性转换成short*类型 ,再存到p里面。

此时我让*p=0,其实就是使其前2个字节为0,11223344 在内存中以 44 33 22 11 存储,相当于把“44 33”变成“00 00”。

所以当我以16进制的形式输出a时,结果是11220000。

3.用递归实现“2+22+222+2222+........”(不一定是2):

先来用简单的方法做:

#include<stdio.h>
int main()
{
	int a = 0, n = 0;
	scanf("%d %d", &a, &n);
	int i = 0, ret = 0, sum = 0;
	for (i = 0;i < n;i++)
	{
		ret = ret * 10 + a;        //得出算式中每一个加数
		sum += ret;                //求和
	}
	printf("%d", sum);
	return 0;
}

为了锻炼递归的思维能力,尝试用递归解题:

2+22+222+2222+22222 

可以看作:前4个加数+最后1个加数

而前4个加数又可以看作:前3个加数+当前最后1个加数

.......

不断的拆分前 n 个加数,直到n 等于1,返回2。

这似乎是一个递归的思路。

那么问题来了,每一次拆分的最后1个加数该怎么求?

尝试拆解2222,发现:

2222=222*10+2;

222=22*10+2;

22=2*10+2;

直到只剩下1位时,返回2。

所以我们应该构造两个递归,一个用来求每一个加数,一个用来求和。

代码如下:

#include<stdio.h>
int find(int a, int n)
{
	if (n == 1)
	{
		return a;
	}
	return 10 * find(a, n - 1) + 2;            计算n>1的每一个加数
}
int finding(int a, int n)
{
	if (n == 1)
	{
		return a;
	}
	return finding(a, n - 1) + find(a, n);     前n个加数+第n个加数
}
int main()
{
	int a = 0, n = 0;
	scanf("%d %d", &a, &n);
	int ret = finding(a, n);
	printf("%d", ret);
	return 0;
}

4.超级水仙花数:

求出1到10000内,假设a是一个n位数,如果a的每一位的n次方相加的和等于a,a就是超级水仙花数。

先梳理一下思路:

在遍历1到10000时,首先要判断此时的 i 是几位数,从而确定 n。

再把a的每一位求出来,看n次方相加的和是否等于 i 。

代码如下:

#include<stdio.h>
int my_pow(int a, int n)
{
	if (n == 1)
	{
		return a;
	}
	return a * my_pow(a, n - 1);
}
int main()
{
	int i = 0;
	for (i = 1;i <= 10000;i++)
	{
		int tmp = i, sum = 0, n = 1; ;   // i 至少是一个一位数
		while (tmp > 9)                  //当tmp是一位数时,跳出循环
		{
			n++;
			tmp /= 10;
		}
		tmp = i;
		while (tmp)
		{
			sum += my_pow(tmp % 10, n);       //自定义函数算每一位的n次方
			tmp /= 10;
		}
		if (sum == i)
		{
			printf("%d\n", i);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值