C语言学习记录—作业7

1. 计算下面各值分别是多少

需要注意区分前置++和后置++

前置++,先++,后使用。后置++,先使用,后++

int main()
{
	int a, b, c;
	a = 5;
	c = ++a;//c=6 a=6
	b = ++c, c++, ++a, a++;//b=7
	//   7    7    7    7  运算时
	//        8         8  运算后
	b += a++ + c;
	//    8  运算时
	//    9  运算后
	printf("a = %d b = %d c = %d\n:", a, b, c);//a=9 b=23 c=8
	return 0;
}

2. 统计二进制中1的个数

自己的方法

思路:1的二级制序列最低位是1,让n和1按位与(只要有0就是0,同时为1才是1),这样就可以得到n的最低位。然后把n往右移,继续和1按位与,直到遍历完32位。

//自己方法
int main()
{
	int n = 0;
	scanf("%d", &n);

	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if ((n & 1) == 1)
			count++;
		n >>= 1;
	}
	printf("%d\n", count);
	return 0;
}

老师方法1

11 - 10进制:11;2进制:1011
11%2=1 这里余数1就是上面11二进制(1011)最后一个1
11/2=5
101 - 是5的二进制 相当于把11二进制(1011)的最后一位(1)去掉

5%2=1 这里余数1就是上面5二进制(101)最后一个1
5/2=2 
10 - 是2的二进制 相当于把5二进制(101)的最后一位(1)去掉

2%2=0 这里余数0就是上面2二进制(10)最后一个0
2/2=1
1 - 是1的二进制 相当于把2二进制(10)的最后一位(0)去掉

1%2=1 这里余数1就是上面1二进制(1)的最后一个1
1/2=0
0 - 0是的二级制 相当于把上面1二进制(1)最后一位(1)去掉

结论:先用n%2获取二进制的最后一位(即余数的二进制序列),再用n/2去掉被求数二进制最后一位(即商的二进制序列)
然后再把商当做新的n重复上述步骤直到商为0

//int count_num_of_1(int n)//此方法对负数无效
//-1
//10000000000000000000000000000001 - -1原码
//11111111111111111111111111111110 - 反码(符号位不变,其他按位取反)
//11111111111111111111111111111111 - 补码(反码+1)
//如果是-1,那么-1%2余-1(不等于1,所以count没有++),然后-1/2商0。循环结束

//传过来的是有符号数,如果把负数当无符号数来看就可以。
//因为无符号数不会把最高位1当做符号位
int count_num_of_1(unsigned int n)
{
	int count = 0;
	while (n)
	{
		if ((n % 2) == 1)//n模2的值就是二进制的最后一位,有1才++
		{
			count++;
		}
		n /= 2;
	}
	return count;
}

老师方法2

思路与自己的方法类似,优化了代码和一部分逻辑。遍历的条件改为向右移动多少位。

int count_num_of_1(int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((n >> i) & 1) == 1)//n向右移动i位(第一次移动0)在按位与1
		{
			count++;
		}
	}
	return count;
}
老师方法3

n=15
n = n&(n-1) 此表达式每运行一次就让n的二进制序列最右边的1去掉 
1111 n(15)
1110 n-1(14)
1110 n(15)和n-1(14)按位与结果(14) 相当于把倒数第1位置成0

1110 n(14)
1101 n-1(13)
1100 n(14)和n-1(13)按位与结果(12) 相当于把倒数第2位置成0

1100 n(12)
1011 n-1(11)
1000 n(12)和n-1(11)按位与结果(8) 相当于把倒数第3位置成0

1000 n(8)
0111 n-1(7)
0000 n(8)和n-1(7)按位与结果(0) 相当于把倒数第4位置成0

n=0的时候,它的二进制序列一定没有1了。所以当n=0之前,表达式执行过几次说明有几个1

从二进制的角度讲,n相当于在n - 1的最低位加上1
所以n和n-1的最低位必定是(0和1)或(1和0),按位与后最低位必定是0

int count_num_of_1(int n)
{
	int count = 0;
	while (n)//n=0,条件为假,循环结束。同时0的二进制中没有1。
	{
		n = n & (n - 1);
		count++;
	}
	return count;
}

扩展n & (n - 1),判断一个数是不是2的n次方

2^1 - 10
2^2 - 100
2^3 - 1000
2的n次方二进制序列只有一个1,n&(n-1)就把二进制序列的1去掉了,结果就是0

if (n & (n - 1) == 0)
{

}

3. 求两个数二进制中不同位的个数

自己的方法

思路:与求二进制中1的个数类似,拿到两个数的最后一位进行比较,然后向右移动,直到遍历完32位。

//自己方法
int main() {
	int n = 0;
	int m = 0;
	scanf("%d %d", &n, &m);
	int count = 0;
	int i = 0;

	for (i = 0; i < 32; i++)
	{
		if ((n & 1) != (m & 1))
			count++;
		n >>= 1;
		m >>= 1;
	}
	printf("%d\n", count);
	return 0;
}

老师方法1
//老师方法1
int count_diff_bit(int m, int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((m >> i) & 1) != ((n >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

老师方法2

思路:使用按位异或(^)。两个数按位异或,二进制位相同为0,相异为1,所以统计按位异或后的二级制中有多少个1即可。

//老师方法2
int count_diff_bit(int m, int n)
{
	int count = 0;
	int i = 0;
	//^ 异或操作符
	//相同为0,相异为1
	int ret = m ^ n;
	//m和n的二进制位只要不同结果就为1
	//所以统计ret二进制位有多少个1
	while (ret)
	{
		ret = ret & (ret - 1);
		count++;
	}
	return count;
}

4. 打印整数二进制的奇数位和偶数位

自己的方法

思路:奇数位,从1开始,每次把n向右移动2位;偶数位,先向右移动一位(即第二位开始),买次把n向右移动2位。

//自己方法
int main()
{
	int n = 2863311530;
	int i = 0;
	for (i = 0; i < 16; i++)//奇数位
	{
		printf("%d ", n & 1);
		n >>= 2;
	}
	printf("\n");

	n >>= 1;
	for (i = 0; i < 16; i++)//偶数位
	{

		printf("%d ", n & 1);
		n >>= 2;
	}
	return 0;
}

老师方法

思路:假设最低位是第1位,第1位向右移动0位到最低位。那么第31位向右移动30位到最低位;第32位向右移动31位到最低位。

//老师方法
int main()
{
	int num = 0;
	scanf("%d", &num);
	//获取奇数位的数字
	int i = 0;
    //从最高位开始打印,只需移动30位,所以从30开始
	for (i = 30; i >= 0; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	printf("\n");
	//获取偶数位的数字
    //从最高位开始打印,只需移动31位,所以从31开始,
    //i=1是向右移动一位,即把偶数位移动到最低位
	for (i = 31; i >= 1; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	return 0;
}

5. 下方代码打印结果是什么

int i;//0
int main()
{
	i--;//-1

	if (i > sizeof(i))
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	
	return 0;
}

分析:全局变量和静态变量不初始化的时候,默认会被初始化为0。所以i--后值为-1。

由于sizeof这个操作符计算返回的结果是size_t类型,是无符号整形。当有符号数和无符号数比较时,int要转换为unsigned int(即算术转换:如果某个操作数的类型排名较低,那么首先要转换为另外一个操作数的类型后执行运算)。

当i(即-1)被转换为无符号整形时,会被理解为一个很大的数。假设int是-1,那么内存的中二进制是32个1(补码),将它转换为无符号数时,直接把补码当做原码。

所以结果是>

补充:

变量部分
全局变量和静态变量都是放在静态区
全局变量和静态变量不初始化的时候,默认会被初始化为0
局部变量(放在栈区)不初始化,默认值是随机值

算术转换
操作数的类型排名
long double
double
float
unsigned long int
long int
unsigned int
int
 

表达式求值先看是否存在整形提升或算术转换,再进行计算
表达式真正计算的时候先看相邻操作符的优先级决定先算谁
相邻操作符的优先级相同的情况下,看操作符的结合性决定计算顺序

6. KiKi学习了循环,BoBo老师给他出了一系列打印图案的练习,该任务是打印用“* ”组成的X形图案。
输入描述:多组输入,一个整数(2~20),表示输出的行数,也表示组成“X”的反斜线和正斜线的长度。
输出描述:针对每行输入,输出用“ * ”组成的X形图案。

int main() {
	// *   *
	//  * *
	//   *
	//  * *
	// *   *
	//   0 1 2 3 4
	// 0 * _ _ _ *
	// 1 _ * _ * _
	// 2 _ _ * _ _
	// 3 _ * _ * _
	// 4 * _ _ _ *
	// 可以看做矩形,i是行,j是列,可以看到规律
	// 左上右下对角线当i=j的时候才打印*
	// 右上坐下对角线当i+j=4时(n-1)才打印
	int n = 0;
	while (scanf("%d", &n) == 1) 
	{
		int i = 0;
		int j = 0;
		for (i = 0; i < n; i++) 
		{
			for (j = 0; j < n; j++) 
			{
				if (i == j) 
					printf("*");
				else if (i + j == n - 1)
					printf("*");
				else
					printf(" ");
			}
			printf("\n");
		}
	}

	return 0;
}

7. KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。
输入描述:多组输入,一行有两个整数,分别表示年份和月份,用空格分隔。
输出描述:针对每组输入,输出为一行,一个整数,表示这一年这个月有多少天。

自己的方法

逐一判断的笨办法

//自己方法
int main() {
	int y = 0;
	int m = 0;
	while (scanf("%d %d", &y, &m) != EOF) {
		if ((((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) && m == 2) 
		{
			printf("%d\n", 29);
		}
		else if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) 
		{
			printf("%d\n", 31);
		}
		else if (m == 4 || m == 6 || m == 9 || m == 11) 
		{
			printf("%d\n", 30);
		}
		else
			printf("%d\n", 28);
	}
	return 0;
}

老师方法

思路:将每个月的天数放到数组中(月份作为下标),只需要对闰年的2月做单独处理。

//老师方法
int is_leap_year(int y)
{
	//下方表达式(&&逻辑与 ||逻辑或)只关注真假。真返回1;假返回0
	//逻辑操作符&& || ! 的结果如果是真就是1;如果是假就是0
	return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}
int main()
{
	int y = 0;
	int m = 0;
	int d = 0;//每月有多少天
	//0没有实际意义,占位用。这个数组表示每月天数,为了让下标1和一月对齐
	int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//两个%d(整数)之间可加可不加空格,但实际输入需要加空格(获取字符就一定要加空格)
	while (scanf("%d%d", &y, &m) == 2)//年和月都获取到返回2
	{
		int d = days[m];//有多少天就用m(月份)作下标去days数组找
		//但是闰年2月要加1
		if ((is_leap_year(y) == 1) && (m == 2))//如果是闰年,就让它等于1
		{
			d++;
		}
		printf("%d\n", d);
	}
	return 0;
}

8. KiKi想知道已经给出的三条边a,b,c能否构成三角形,如果能构成三角形,判断三角形的类型(等边三角形、等腰三角形或普通三角形)
输入描述:题目有多组输入数据,每一行输入三个a,b,c(0 < a, b, c < 1000),作为三角形的三个边,用空格分隔。
输出描述:针对每组输入数据,输出占一行,如果能构成三角形,等边三角形则输出“Equilateral triangle!”,等腰三角形则输出“Isosceles triangle!”,其余的三角形则输出“Ordinary triangle!”,反之输出“Not a triangle!”。
输入:
2 3 2    
3 3 3
输出:
Isosceles triangle!
Equilateral triangle!

自己的方法

思路:任意两边之和大于第三边,任意两边之差小于第三边。

//自己方法
int main() {
    int a = 0;
    int b = 0;
    int c = 0;
    while (scanf("%d %d %d", &a, &b, &c) != EOF) {
        if (((a + b > c) && (a > c - b)) && ((b + c > a) && (b > a - c)) && ((a + c > b) && (c > b - a))) {
            //等边三角形
            if (a == b && b == c) {
                printf("Equilateral triangle!\n");
            }
            //等腰三角形
            else if (a == b || b == c || a == c) {
                printf("Isosceles triangle!\n");
            }
            else {
                printf("Ordinary triangle!\n");
            }
        }
        else {
            printf("Not a triangle!\n");
        }
    }

    return 0;
}

老师方法

其实任意两边之和大于第三边和任意两边之差小于第三边是同时存在的,可以用数学公式证明。所以只需要判断任意两边之和是否大于第三边

//老师方法
int main() {
	int a = 0;
	int b = 0;
	int c = 0;
	while (scanf("%d %d %d", &a, &b, &c) != EOF) {
		//a+b>c 等价a>c-b
		if ((a + b > c) && (b + c > a) && (a + c > b))//只判断任意两边大于第三边
		{
			//等边三角形
			if (a == b && b == c) 
			{
				printf("Equilateral triangle!\n");
			}
			//等腰三角形
			else if (a == b || b == c || a == c) 
			{
				printf("Isosceles triangle!\n");
			}
			else 
			{
				printf("Ordinary triangle!\n");
			}
		}
		else 
		{
			printf("Not a triangle!\n");
		}
	}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值