C语言学习回顾(作业)

又很久没有写总结了,每次打开CSDN的时候都有点抵触,有很多原因,其一是不知道这样的总结对自己到底有没有作用,或者对有缘看到这些破烂的朋友们有没有帮助,其次是由于上网课和写总结总是差了一到两节的内容,有时候要翻前两章左右的代码,找起来麻烦,另外还有自己之前写的什么狗屁总结(1)、(2)、(3)具体是什么内容也不知道,只有点进去慢慢看才晓得。自己深刻反省,并尽量修改以后这些回顾的标题,希望这个系列可以一直坚持下去,作为自己学习的监督。

这次的回顾是老师上课讲的一些习题作业,有些是偏重理论知识的选择题,有些是代码实操题,此外,代码题在之前的课上有时候提及过,选择题没有做截图和笔录,就不再一一去找了,接下来的内容全是代码题,由于内容很多,可能会分两次来写完。

/*创建一个整型数组,完成对数组的操作*/

void Init(int arr[], int sz) // 实现Init函数,初始化数组为全0
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = 0;
	}
}

void print(int arr[], int sz) // 实现print函数,打印数组的每一个元素
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

上面的两个函数只是为了实现一个内容,没有完整的框架,这两个函数与数组相关,其实接下来有很多习题都与数组有关,而实现数组习题的核心思想(我认为的核心思想)主要靠循环操作。这两个代码块很简单,大家就看一看罢了。

void reverse(int arr[], int sz) // 实现reverse函数,完成数组每个函数的逆置
{
	int left = 0;
	int right = sz - 1; // 最右边下标
	while (left < right)
	{
		int temp = arr[left]; // 将暂定值设置为最左边的值
		arr[left] = arr[right]; //将最左边的值设置为最右边的
		arr[right] = temp; // 将最右边的值设置为最左边的值
		left++;
		right--;
}
}


int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	reverse(arr, sz);
	//Init(arr,sz); // 将数组初始化为0
	print(arr, sz);
	return 0;
}

在最早的几篇学习回顾中(由于我那该死的标题设置,我也不记得是哪篇),里面详细提到了冒泡排序和左右下标寻找数组某值的方法,在这道习题中得以应用。此外,这个-reverse函数内部设置了一个局部临时变量作中间值,方便将左右值交换。

在这里回顾一下上次提到的,如果不第三个变量,将两数交换的方法。需要对异或这种运算有非常敏感的认识,如果忘记了,可以再看一眼,另外值得一提的是,一般做这种交换操作,都会需要使用第三个变量,因为这样方便观察,逻辑也清晰明了,而那种(不需要第三个变量)的操作,更多会出现在考核里面,大家不用刻意牢记。

/*将数组A中的内容和数组B中的内容进行交换*/

int main()
{
	int arr1[] = { 1,3,5,7,9 };
	int arr2[] = { 2,4,6,8,0 };
	int tmp = 0;
	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[1]);
	for (i = 0; i < sz; i++)
	{
		tmp = arr1[i];
		arr1[i] = arr2[i];
		arr2[i] = tmp;
	}
	printf("arr1 = ");
	print(arr1, sz);
	printf("\n");
	printf("arr2 = ");
	print(arr2, sz);
}

这道题与上面交换左右下标的题目相联系,所有实现都在for循环内实现,在此便不再赘述了。需要注意的是,tmp已经被赋值为arr1[i],不要出现arr[tmp]这样的操作。

/*求代码结果*/

int main()
{
	int arr[] = { 1,2,(3,4),5 };
	printf("%d\n", sizeof(arr)); // arr内只有4个元素,由于类型是int 所以长度为4*4 = 16
	return 0;
}

这个题原本是个选择题,但我觉得挺有意思的,就抄录下来了,注意这里的(3,4)其实是一个运算,中间的逗号是单目运算符,这里只取逗号右边的值,也就是4,因此整个长度就是16。

int main()
{
	int arr[] = { 1,2,3,4,5 };
	short* p = (short*)arr; // 将arr首元素的地址强制转换为short类型赋值给short* p
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		*(p + i) = 0; // 由于p此时是short类型的指针,向前一个单位,只能前进2byte
                      // 而最开始int类型的arr则是4byte一个元素
		// 因此循环结束之后,虽然p前进了4个单位,但相当于前进了8byte
        // 对应arr里也就是两个元素的长度
	}
	for (i = 0; i < 5; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

这里需要回顾指针章节讲的内容,关于解引用操作*对于各个类型例如int和short的区别,注意看注释,在for循环内,每次增加的长度,取决于此时p的类型,由于这时第一个for循环内只循环4次(i = 0、1、2、3、,在这种情况下,p最多前进3个次,也就是6byte,而第二个for循环中,我们打印的是arr,它的类型是int,而int 一个元素4个单位,因此在这情况下,通过short改变的长度只有6byte,只够arr的前两个元素,因此只有2个元素变为0,打印出来最终是:

0 0 3 4 5

int main()
{
	int a = 0x11223344;
	char* pc = (char*)&a; // 将a的地址强制转化为char类型
                          // 并放入char类型的指针 pc 中
	*pc = 0; // 由于pc是char类型的指针
             // 解引用操作并赋值0只能赋值一个byte
             // 因此内存内为:00332211
	printf("%x\n", a); // 将内容以16进制呈现出来
                       // 需要将内存内的信息反转
                       // 因此打印出来为:11223300
	return 0;
}

如果可以简要的理解第一个代码块,那么这个就是另一种运算方式,注释也写的很清楚了。

int i; // 全局变量不初始化,默认为0

int main()
{
	i--; // -1
	// 10000000000000000000000000000001 -- 原码
	// 11111111111111111111111111111110 -- 补码
	// 11111111111111111111111111111111 -- 反码
	// 如果为无符号位,那么第一位就不是符号位
    // 全为有效位,此时为正数,那就原、反、补相同
	if (i > sizeof(i)) // sizeof() -- 计算变量/类型所占内存的大小 
                       // >= 0  (无符号数) -- (unsigned int)
	{
		// 此时会把i转化为无符号数进行比较
        // 而这样根据二进制,i会是一个很大的二进制数
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	return 0;
}

这道题原本也是个选择题,我抄录下来了,问的是最终打印的是>还是<,大家如果有兴趣,可以试试看,原因是,因为sizeof计算的是变量\类型所占内存的大小,因此这个结果一定是大于等于零。所以sizeof将会返回一个无符号数,而将i与sizeof(i)相比,则会将i转化为无符号数,因此得出一个很大的数,最后答案是大于。

int main()
{
	int a, b, c;
	a = 5;
	c = ++a; // c = 6
	b = ++c, c++, ++a, a++; // c = 8, b = 7 a = 8
	b += a++ + c; // b = b + a++ + c  --> 7+8+8  = 23 a = 9
	printf("a = %d b = %d c = %d", a, b, c);
	return 0;
}

这题有点取巧的意思,主要考察了++前置后置的区别。一题就能贯穿所有情况。到底是先赋值还是先++。另外还要考虑逗号运算符的规则。

比如b = ++c,c++,++a,a++,这里,因为++前置,所以先算++c = 7然后是逗号运算符,逗号表达式将会把最后边的值赋值给b,因此在++c,c++,++a,a++之后,将最后值赋值给b,注意++a,a++,这里是前置++a再逗号表达式再后置a++,所以b的值是++a之后的值,再最后计算a++,a=8,但b=7。

同理可以看最后一个b+=这里,拆开来就是b = b + …,在此b = 7,a = 8,c = 8,在计算完b=23后再将a++,得出a = 9。

/*统计二进制中1的个数*/

int count_bit_one(unsigned int a) // 此时将int 改为unsigned int 
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((a >> i) & 1) == 1)
		{
			count++;
		}
	} // 第一种算法
	while (a)
	{
		if (a % 2 == 1)
		{
			count++;
		}
		a /= 2;
	} // 第二种算法
	while (a)
	{
		a = a & (a - 1);
		count++;
	} // 最精简的算法
	return count;
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	// 1101 -- 通过模2除2来获得每一位的二进制数
	// 00000000000000000000000000001101
	// 写一个函数,求a的二进制表示(补码)中,有几个1
	int count = count_bit_one(a); // 如果是负数怎么办
	printf("count = %d\n", count);
	return 0;
}

这道题共有三种算法处理方式,第一种最简陋,没有考虑到输入的数是负数的情况,此时算法会崩溃。第二、第三种方法适用于任何情况,在这里计算二进制中,个人得出的心得是,一定要处理好模与除出现的结果。合理运用模与除,事半功倍。

另外在这里解释一下第三种算法:将数本身与数减1相与,再赋给这个数,这样此数字最右边的1就会消失,因此我们通过这种方法,每按位与一次,计数加1,直到按位与为全零为止


这样,本次作业的第一部分就讲完了,过段时间再总结接下来的内容,一会要去上课了。

在此我有个感悟,对于函数模块的实现,其实方法千千万万,但为满足一个功能而编写一个函数,如何最大性价比这个函数,才是我们学习C语言的一大难点,如何将一种算法从四五六七行,逐步缩减到一二行,不但可行,又适用范围广,还方便阅读,才是最终目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值