第一道题目:
有如下宏定义和结构定义
#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;
}
。