2022-07-20-106期-作业讲解

第一道题目:

有如下宏定义和结构定义

#define MAX_SIZE A+B
struct _Record_Struct
{
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);
当A=2, B=3时,pointer分配( )个字节的空间。


作业内容
A.20
B.15
C.11
D.9

分配多少个字节,我们进行分析:

1:我们首先看这段代码的最主题的部分

struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);

这里表示动态内存开辟一部分空间,强制类型转化为结构体指针类型赋给结构体指针pointer

2:我们看位段部分

struct _Record_Struct
{
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;

我们可以知道,对应的位段所占空间的大小应该是3个字节 

3:#define部分

#define MAX_SIZE A+B

这里发生替换

4:计算结果

sizeof(struct _Record_Struct) * MAX_SIZE

位段所占空间大小是三个字节,MAX_SIZE的结果为2*3,所以对应的结果为3*2+3=9.

第二道题目:

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}

打印的结果是什么呢?

答:我们进行分析,首先,我们创建一个数组puc,puc有4个元素,我们使用memset把0填充给到数组中去,对应的图像为:

struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;

pstPimDate表示表示一个位段指针,这个位段占用2个字节,把数组puc强制类型转化为(struct tagPIM*)类型,再通过位段指针进行初始化

  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;

2对应的二进位制是010    3对应的二进位制是011    4对应的二进位制是100         5对应的二进位制是101

又因为我们是小端存储,低位放在低地址处。

对应的图像为

%x表示以16进位制打印,%02x表示以16进位制打印,打印两位,如果不够,以0填充。

所以对应的结果为02  29 00 00

第三道题目

#include<stdio.h>
int main()
{
  union
  {
    short k;
    char i[2];
  }*s, a;
  s = &a;
  s->i[0] = 0x39;
  s->i[1] = 0x38;
  printf(“%x\n”,a.k);
  return 0;
}

 我们进行解析

 union
  {
    short k;
    char i[2];
  }*s, a;
  s = &a;

这里表示匿名联合,短整型k和数组i公用两个字节,s指向a的地址。

我们画出对应的图像

 短整型k和数组i公用一个空间。

s->i[0] = 0x39;
  s->i[1] = 0x38;

这里表示进行初始化

0x39一共占用一个字节

我们画出对应的图像

 又因为我们是小端存储,低位放在低地址处,所以打印出来的结果为

0x38 0x39

下一道题目

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

答案:

void find_single_dog(int arr[], int sz, int *pd1, int *pd2)
{
	int i = 0;
	int ret = 0;
	for (i = 0; i < sz; i++)
	{
		ret ^= arr[i];
	}
	int pos = 0;
	for (pos = 0; pos < 32; pos++)
	{
		if (((ret >> pos) & 1) == 1)
		{
			break;
		}
	}
	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
		{
			*pd1 ^= arr[i];
		}
		else
		{
			*pd2 ^= arr[i];
		}
	}
}
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int dog1 = 0;
	int dog2 = 0;
	find_single_dog(arr, sz,&dog1,&dog2 );
	printf("%d %d\n", dog1, dog2);
	return 0;
}

我们进行分析:创建一个数组,其中只有5和6是单身狗,我们查找数组元素的个数,调用函数find_single_dog,因为我们需要打印出两个数,而我们只有一个返回值,所以我们可以通过地址的方法修改变量值。

进入函数内部:

int i = 0;
	int ret = 0;
	for (i = 0; i < sz; i++)
	{
		ret ^= arr[i];
	}

任何数字与0异或的结果都为他本身

任何数组与他本身异或的结果都为0

所以我们这里把数组的全部元素进行异或,最后的结果就是两个单身狗的异或值,求出单身狗异或的结果ret

查找异或结果的最右侧的1

int pos = 0;
	for (pos = 0; pos < 32; pos++)
	{
		if (((ret >> pos) & 1) == 1)
		{
			break;
		}
	}

因为我们的两个单身狗的值不同,所以异或的结果肯定不为0,我们要找到最右侧的1,把这两个单身狗分到两个组中。

因为两个组中,每一组中都有成对出现的元素和一个单身狗,我们再对每一组进行异或,就可以求出每一组的单身狗。

for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
		{
			*pd1 ^= arr[i];
		}
		else
		{
			*pd2 ^= arr[i];
		}
	}

下一道题目:

模拟实现atoi

 表示把字符串转换为整型。

我们先使用atoi函数

#include<stdlib.h>
int main()
{
	char arr[20] = "123456";
	int ret = atoi(arr);
	printf("%d\n", ret);
	return 0;
}

我们进行编译:

这个函数的确把字符串中的内容转换成整型了。

当我们遇到非数字呢?

例如

 

#include<stdlib.h>
int main()
{
	char arr[20] = "123abc456";
	int ret = atoi(arr);
	printf("%d\n", ret);
	return 0;
}

我们进行编译

所以:当我们的atoi遇到字母时,就停止转换了。

 当我们在前面放上空格呢?

#include<stdlib.h>
int main()
{
	char arr[20] = "      123456";
	int ret = atoi(arr);
	printf("%d\n", ret);
	return 0;
}

我们进行编译‘

可以发现,空格并不会影响atoi函数发挥作用。

 接下来,我们来模拟实现atoi

我们需要考虑以下因素

1:空指针

2:空字符串

3:空格

4:加减

5:越界

6:非数字字符。

#include <stdlib.h>
#include <assert.h>


#include <ctype.h>
#include <limits.h>

enum Status
{
	VALID,
	INVALID
}sta = INVALID;//默认非法


int my_atoi(const char* str)
{
	int flag = 1;
	assert(str);
	if (*str == '\0')
		return 0;//非法0
	/*跳过空白字符*/
	while (isspace(*str))
	{
		str++;
	}

	if (*str == '+')
	{
		flag = 1;
		str++;
	}
	else if(*str == '-')
	{
		flag = -1;
		str++;
	}

	long long ret = 0;
	while (*str)
	{
		if (isdigit(*str))
		{
			/*越界*/
			ret = ret * 10 + flag*(*str - '0');
			if (ret > INT_MAX || ret < INT_MIN)
			{
				return 0;
			}
		}
		else
		{
			return (int)ret;
		}
		str++;
	}
	if (*str == '\0')
	{
		sta = VALID;
	}
	return (int)ret;
}

int main()
{
	char arr[200] = "1234";
	int ret = my_atoi(arr);
	if (sta == INVALID)
	{
		printf("非法返回:%d\n", ret);
	}
	else if (sta == VALID)
	{
		printf("合法转换:%d\n", ret);
	}
	r

我们对代码进行分析:我们先进入函数部分

const char* str表示我们的函数的功能是从字符串取出数字,并不会将字符串的取值发生改变。

assert(str)防止我们函数接收的str是空指针。

if (*str == '\0')
		return 0;

 这里表示如果我们的字符串为空字符串的时候,退出函数。另外,我们在这里要注意:这里起始分为两种情况:第一种情况,我们的字符串是空字符串,第二种情况,我们的字符串中只有一个’0‘。所以我们要在这里分情况讨论,这时候我们引入枚举常量sta,把他置为INVALUED,表示非法输出。

while (isspace(*str))
	{
		str++;
	}

表示假如我们的字符串前面有空格,我们跳过对应的空格。


 

if (*str == '+')
	{
		flag = 1;
		str++;
	}
	else if(*str == '-')
	{
		flag = -1;
		str++;
	}

表示判断我们的字符串是否有正负号。

long long ret = 0;

这里的long long ret在以后能够方便我们对是否越界进行检查。

while (*str)
	{
		if (isdigit(*str))
		{
			/*越界*/
			ret = ret * 10 + flag*(*str - '0');
			if (ret > INT_MAX || ret < INT_MIN)
			{
				return 0;
			}
		}
		else
		{
			return (int)ret;
		}
		str++;
	}

这个while循环才真正的表示进行读取

while (*str)表示知道*str等于\0的时候读取结束。

if (isdigit(*str))

这里是判断我们解引用访问的字符串是否为数字,为的话,我们就继续执行,不为的话我们直接返回return (int)ret,这里需要强制类型转化,因为我们函数的参数类型是int类型的。

ret = ret * 10 + flag*(*str - '0');

表示把对应的字符串中的元素转化为整型数字。

if (ret > INT_MAX || ret < INT_MIN)
			{
				return 0;
			}

这里是判断ret是否越界。

下一道题目

 我们进行分析:

#define就相当于替换,所以就等价于int*a,b. int*一次只能定义一个变量,而第二个类型会被变成int类型。

而对int*进行重命名的话,int_ptr就表示一种新的类型,这种类型是可以定义两个变量的,所以只有b不是int*

下一道题目:

作业标题(1588)
交换奇偶位

作业内容
写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

我们可以使用&的方法

对于一个二进位制数字的每一位,当他&1时,对应的结果就是他本身,当他&0时,对应的结果就是0

假设我们写出一个二进位制数字10001001 10001001 10001001 10001001,设其为a

我们该怎样交换奇偶位。

我们假设首位为奇数,在末位为偶数。

我们可以先求出奇数位对应的二进位制的数字

我们让n&10101010 10101010 10101010 10101010

也就是n&aa aa aa aa

求出的就是奇数位对应的二进位制的数字。

接下来,我们来求偶数位对应的二进位制的数字

我们可以让n&01010101 01010101 01010101 01010101

也就是n&55 55 55 55

求出这两个数之后,我们让奇数位>>1,让偶数位<<1,最后相加得到就可以了。

#define SWAP(n) n=(((n&0xaaaaaaaa)>>1)+((n&0x55555555)<<1))

int main()
{
	int n = 0;
	scanf("%d", &n);
	SWAP(n);
	printf("%d\n", n);
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值