第一题:编写一个函数找出这两个只出现一次的数字。
思路:
1.把5和6分到2个组中
2.1 2 1 2 5
3 4 3 4 6
1 3 1 3 5
2 4 2 4 6
作法:
1.计算5和6的哪一位为1-n
2.以第n位为标准,第n位为1的放一组,第n位为0的放一组,(这里的分组每一组个数不一定要相同,只要成对出现就可以约掉了)
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i]; //
}
//ret就是5^6的结果,二进制中一定有1,为1的位就是两个数字不一样的位
int pos = 0;
for (i = 0; i < 32; i++)
{
if (((ret >> i) & 1) == 1) //这里一开始没看懂,00000001跟别人按位与前面都是0,
//只有最后一位可能为1,
//所以如果那一位的值为1,按位与的结果就为1
{
pos=i;//把这个i保存下来,第几位是1
break;
}
}
//ret的第pos位是1
//把arr数组中的每个元素的第pos位为1的数字异或在一起
int num1 = 0;
int num2 = 0;
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)
{
num1 ^= arr[i];
}
else
{
num2 ^= arr[i];
}
}
printf("%d %d", num1, num2);
return 0;
}
第二题:模拟实现atoi
下面是我自己写的函数是有问题的,没有考虑到负数和越界的情况
代码如下
xia'm
int my_atoi(const char* str)
{
int sum = 0; //这个sum就是最后返回的数字
int k = 0; //这个k存放几位数字
int i = 0;
int n = 1; //2位数,n置为10,3位数,n置为100
while (*str == 0) //丢弃空格字符
{
str++;
}
char* p = str; //保存str的地址
while (*p >= '0' && *p <= '9')
{
k++; //数字字符的数量
p++;
}
for (i = 1; i < k; i++) //k为3,n为100,k为4,n为1000
{
n *= 10;
}
do
{
if (*str < '0' || *str>'9') //如果不是数字字符返回0
return sum;
else
{
sum += (*str - '0') * n; //三位数,第一位就乘100,第二位乘10,第三位乘1;
str++;
n /= 10;
}
} while (*str != 0);
return sum;
}
int main()
{
char arr[20] = "-01125ds2a";
int i = 0;
int j = 0;
i = my_atoi(arr);
j = atoi(arr);
printf("%d\n", i);
printf("%d\n", j);
return 0;
}
下面是正确做法:
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h> //isdigit
#include<assert.h>
//思路
//my_atoi(null) //异常
//my_atoi(" ")//异常
//my_atoi(" +123")正常
//my_atoi("-123")正常
//my_atoi("123abc")正常
//my_atoi("111111111111111111111111")正常
//my_atoi("-1111111111111111111111111")异常
VALID表示结果合法
INVILID表示结果非法
enum State
{
VALID,//0
INVALID//1
};
//默认结果可能非法,当正确转换后再改为合法
enum State state = INVALID;
int my_atoi(char* str)
{
int flag = 1;
long long ret = 0;
assert(str);
//跳过空白字符
while (isspace(*str))
{
str++;
}
if (*str == '\0')
{
return 0;
}
//跳过正负号
if (*str == '+')
{
str++;
}
else if (*str == '-')
{
flag = -1;
str++;
}
//开始转换数字字符直到非数字字符
while (isdigit(*str))
{
ret = ret * 10 + flag * (*str - '0');
if ((ret > INT_MAX) || (ret < INT_MIN))
{
return 0;
}
str++;
}
//正常停止
if (*str == '\0')
{
state = VALID;
return (int)ret;
}
else
{
//遇到非数字字符
return (int)ret;
}
}
int main()
{
char* p = "-121212121";
printf("%d\n", my_atoi(p));
return 0;
}
//这个题也来自剑指offer
第三题:写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
考察:offsetof宏的实现
下面先写出offsetof的使用:
struct S
{
char a;
int b;
char c;
};
int main()
{
printf("%d\n", offsetof(struct S, a));
printf("%d\n", offsetof(struct S, b));
printf("%d\n", offsetof(struct S, c));
}
自己实现思路:
假设结构体的起始地址为0,先把0强制类型转换成结构体指针,然后箭头指向结构体中的变量,
之后对这个变量取地址,由于一开始的起始地址为0,所以变量地址就是偏移量,给她转换成int类型就是结果了
其实都是假设的,这个结构体也只是声明,其实内存中没有这个结构体呢还,定义这个宏也是求偏移量罢了,可以假设起始地址
#define OFFSETOF(s_type, m_name) (int)&(((s_type*)0)->m_name)
//这里是假设结构体的起始地址为0,先把0强制类型转换成结构体指针,然后箭头指向结构体中的变量,
//之后对这个变量取地址,由于一开始的起始地址为0,所以变量地址就是偏移量,给她转换成int类型就是结果了
//其实都是假设的,这个结构体也只是声明,其实内存中没有这个结构体呢还,定义这个宏也是求偏移量罢了,可以假设起始地址
struct S
{
char a;
int b;
char c;
};
int main()
{
printf("%d\n", OFFSETOF(struct S, a));
printf("%d\n", OFFSETOF(struct S, b));
printf("%d\n", OFFSETOF(struct S, c));
}
第四题:写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。
思路:想要得到一个整数的二进制位的奇偶互换就得先将偶数位提取出来然后右移一位
再将奇数位提取出来然后左移一位,两者相加
#define SWAP_BIT(n) n=(((n&0xaaaaaaaa)>>1)+((n&0x55555555)<<1))
//这里0xaaaaaaaa为10101010101010101010101010101010,和n按位与之后只剩下偶数位
//这里0x55555555为01010101010101010101010101010101,和n按位与之后只剩下奇数位
int main()
{
int a = 10;
//00000000000000000000000000001010 这是10
//00000000000000000000000000000101 期望得到的奇数位和偶数位置互换,值为5
SWAP_BIT(a);
printf("%d\n", a);
SWAP_BIT(a); //再交换回来
printf("%d\n", a);
}