C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)(上):https://developer.aliyun.com/article/1513286
4 找一个单身狗
【题目内容】
一个数组中只有一个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。
解析:
例如数组 1 2 2 3 3 4 1中 4就是单身狗
思路 因为单身狗只有一个 直接全部异或
异或的特点相同为0,相异为1,
如果有重复的数字 异或就等于0 最后只会留一下一个单身狗
看代码:
#include<stdio.h> #include<string.h> int find_dog(int arr[],int sz) { int dog = 0; for (int i = 0;i < sz;i++) { dog ^= arr[i]; } return dog; } int main() { int arr[] = { 1,2,2,3,3,4,1 }; int sz = sizeof(arr) / sizeof(arr[0]); printf("单身狗是%d", find_dog(arr,sz)); return 0; }
5 找两个单身狗
【题目内容】
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。
解析:
如果我们按照之前的思路直接异或 肯定只会出来一个四不像数,
假设数组1 2 3 3 1 4,
我们把 两个单身狗分成两个组,
而组中其他的数字就都不是单身狗,
此时我们在分组异或就分别得到了2个单身狗,
问题是我们以什么为依据分组?
依据 二进制位 异或把相同的数字变成0,不同的数字变成1,
我们根据1在哪位 就说明单身狗这个的二进制位不同 ,按照这个二进制位分,
两个单身狗是不可能进到一组的
① 我们依然把数组中所有数字异或到一起 然后判断这个数字的二进制位 因为有两个单身狗2和4
0010 和0100最后异或完毕得到的二进制位是 0110 说明两个单身狗数字的二进制最后位是相等
我们左移一位得到了1 就说明 两个单身狗数字的倒数第二位二进制数 不相等
② 让数组中所有的数字左移一位 如果等于 1 放进第一个数组中
如果等于0 放进第二个数组中
③ 把数组中的数字全部异或就得到了 2个单身狗
代码:
#include<stdio.h> void find_two_dog(int arr[],int sz,int* px,int* py) { int sum = 0; for (int i = 0; i < sz; i++) { sum ^= arr[i]; } int count = 0; for (int i = 0; i < 32; i++) { if (sum & 1 << i) //循环判断第几位是1 { count = i;//如果是1 记录下来 break; } } int dog1 = 0, dog2 = 0; for (int i = 0; i < sz; i++) { if (arr[i] & 1 << count) { dog1 ^= arr[i]; } else { dog2 ^= arr[i]; } } *px = dog1; *py = dog2; } int main() { int arr[] = { 1,2,3,3,1,4 }; int sz = sizeof(arr) / sizeof(arr[0]); int x = 0, y = 0;//因为不能return两个值,所以设置两个返回型参数 find_two_dog(arr, sz, &x, &y); printf("第一个单身狗%d\n第二个单身狗%d",x, y); return 0; }
6 宏交换二进制奇偶位
【题目内容】
写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。
解题思路:
做交换,直接将二进制位中奇数和偶数部分分别全部提取出来,交换一下位置,
再重新以相加或者位运算或的方式求和,即可得到想要的结果。
首先要做的是:分别提取给出整数的奇数位和偶数位,即:
按从右往左数第一位是1(奇数)
提取奇数位:(N) & 01010101010101010101010101010101 (0x55555555)
提取偶数位:(N) & 10101010101010101010101010101010 (0xaaaaaaaa)
将奇数位左移一位,偶数位右移一位。
相加移位后结果(或者位运算或)。
#include<stdio.h> //a 1010 5 0101 #define SWAP(R) (((R) & 0xaaaaaaaa)>>1) + (((R) & 0x55555555)<<1) int main() { int a = 10; printf("%d\n", SWAP(a)); return 0; }
7 offsetof宏的模拟实现
写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明。
考察:offsetof宏的实现。
offsetof介绍:
格式:offsetof(type, member) 头文件:<stddef.h>功能:返回成员 相对于结构或联合 起始地址的偏移量(以 字节为单位),返回类型是size_t。
这个宏有两个参数:
type 是一个结构体类型或联合类型;member 是结构体或联合的某一个成员;
使用代码:
#include <stdio.h> #include <stddef.h> struct S { int a;//0 1 2 3 char b;//4 //5 short c;//6 7 }; int main() { printf("%d\n", offsetof(struct S, a)); printf("%d\n", offsetof(struct S, b)); printf("%d\n", offsetof(struct S, c)); return 0; }
模拟实现:
结构体起始地址加上该成员相对于起始地址的偏移量就是成员变量的地址。
想要知道成员相对于结构体起始地址的偏移量,假设结构体起始地址位于0地址处,
那么结构体成员变量的地址强制类型转换为size_t后就相当于该成员相对于起始地址的偏移量。
#include <stdio.h> #include <stddef.h> //结构体起始地址加上该成员相对于起始地址的偏移量就是成员变量的地址。 //想要知道成员相对于结构体起始地址的偏移量,假设结构体起始地址位于0地址处, //那么结构体成员变量的地址强制类型转换为size_t后就相当于该成员相对于起始地址的偏移量 #define OFFSETOF(type, member) (size_t)(&(((type*)0)->member))//-0忽略了 struct S { int a;//0 1 2 3 char b;//4 //5 short c;//6 7 }; int main() { printf("%d\n", offsetof(struct S, a)); printf("%d\n", offsetof(struct S, b)); printf("%d\n", offsetof(struct S, c)); printf("%d\n", OFFSETOF(struct S, a)); printf("%d\n", OFFSETOF(struct S, b)); printf("%d\n", OFFSETOF(struct S, c)); return 0; }
C语言完结撒花
下个专栏是C语言实现初阶数据结构吧