#include<stdio.h>
#define exchange(n) \
//这里的 \ 就是换行,就是 你要定义的宏,太长了,使用这个斜杠,可以让你在下一行继续写
n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >> 1)
// 5的奇数位左移一位 5的偶数位右移一位
// 奇数位代替偶数位,偶数位代替奇数位,不就完成了交换了嘛!,再将两者进行 按位或,奇数位和偶数位上的值互不影响。
// 其结果就是我们想要的想要的结果
int main()
{
int a = 5;
exchange(a);
printf("a = %d\n", a);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ret = atoi("1234");
printf("%d\n", ret);
return 0;
}
由程序的输出结果,我们发现,atoi 当遇到 非数字的字符时,它就不再转换字符串了。
由程序的输出结果发现,atoi 函数 不会转换 空格,直接跳过。.
由程序的输出结果发现,atoi 函数 不会转换 符号+ , 直接跳过。.
由程序的输出结果发现,atoi 函数 遇到 负号时,会保留下来。.
由程序的输出结果发现,atoi 函数 遇到 空指针时,无法转换数据,而且程序会崩溃,意味着 atoi 无法转换 空指针。
由程序的输出结果发现,atoi 函数 面对一个只有 字符串结束标识符 '\0’时,会将其转换成它的ASCII码值 0。那么问题来了,如果我想转换一个字符串里只有 字符 '0’时,它也返回0(见下方附图),那我们怎么区分两者呢?
.#### 附图:
可以看出,atoi 函数遇到很大的字符数字串,它无能为力,它只能转换 int 取值范围(-2^31 ~ 2的31次方-1)内的字符数字串
现在我们已将知道 atoi 各种特性,和它的应用结构 :int atoi( const char *string );
现在我们来模拟实现它吧
#include<stdio.h>
#include<ctype.h>
#include<assert.h>
#include<limits.h>
enum STATE
{
legal,// 合法
illegal//不合法
};
enum STATE state = illegal;//判断是否在转换的过程中碰到异常情况
int my_atoi(const char* str)
{
assert(str);// 这里解决 空指针NULL的情况
int flag = 1;//标记正负数
long long ret = 0;//用来判断 是否有溢出的情况(转换的数字字符太大了)
//如果用int 来定义ret,是不行,因为int是放不下,比它大的数,会将转换成自己放的下数,这样的话我们就无法判断,是否有溢出现象
if (!*str)// 这里利用了 枚举 来区分 空字符串 和 0字符串,都返回0的情况
{
//我们认为 "",atoi遇到它,返回 0,是非法的
//因为我一开始就把就 state,设置为 非法。
// 虽然这里只返回 0,但是 state(状态)还是处于非法的,
// 反之如果 0 字符串的话,我们就把 stata 改成合法就行了
// 只要在输出结果后,看一下程序的状态就知道,是谁返回的 0.
return 0;// 所以这时候返回的0,肯定是非法的
}
while (isspace(*str))//这里是跳过空白字符,isspace函数如果发现*str是一个空格,它就返回一个非零值(为真),否,则返回0(为假)
{
str++;//地址自增加一,跳过空白
}
if (*str == '+')// 识别正负数(flag == 1)
{
str++;// 如果它 是 +,flag就不动它,然后跳过它
}
else if (*str == '-')
{
flag = -1;//是负的,我们就把他改成 -1,然后跳过它
str++;
}
while (*str)// 数字字符的识别,如果它数字字符,它肯定是为真的('0' ~ '9' 等价于ASCII码值 48 ~ 57),直到遇到 '\0'(ASCII码值为0),则为假,跳出循环
{
if (isdigit(*str))// isdigit 判断 *str 是否是 数字字符
{
ret = ret * 10 + flag*(*str - '0');// 见附图1
str++;
/* flag,表示符号,如果是正数flag ==1,那么该表达式就是正数相加,如果是负数 flag == -1,那么该表达式就是负数相加*/
// 判断是否溢出
if (ret < INT_MIN || ret > INT_MAX)// INT_MIN,int最小取值,INT_MAX int 最大取值
// ret 定义为 long long 类型的理由就在于此
// 如果产生一个 超出 int 范围的值,而 ret 是一个int 类型,所以会将其截断放进ret
// 那么我们这个 if 语句将毫无意义(因为ret永远不可能超出int 的取值范围),所以我们将 ret 定义成 long long 型(ret放进一个取值范围更大的类型),那么产生一个超出 int 范围的值
// 我们的 ret 能存的下,也就意味着我们能够,进行if语句的判断
// 见 附图 2
{
state = illegal;//非法,这个是可以不用写的,因为 stata一开始就是非法,这是值是提醒你们一下
return 0;// 这个零就是异常返回
}
}
// 异常字符(非数字字符)
else
{
state = legal;//合法的
return ret;
}
}
// 前面如果有任何一项条件满足,程序都不会走到这一步
// 走到这里,说明 模拟实现的 atoi 是 将一个字符串,从头到尾都转换成功了(遇到字符串最后一个停止标识符'\0'结束的)
state = legal;// 那这个肯定是合法的
return ret;
}
int main()
{
char c[1024] = { 0 };
scanf("%s", c);
int ret = my_atoi(c);
printf("%d\n", state);// 输出为 0 是合法, 1 是非法
printf("%d\n", ret);//如果输出为 0,说明是异常返回,是非法的
return 0;
}
#include<stdio.h>
struct S
{
char a;
char b;
int c;
};
offsetof的返回值是int
#define OFFSETOFF(struct_type,struct_member) (int)&(((struct_type*)0)->struct_member)
((struct_type*)0) 假设结构体的地址为0, 真实地址,你们直接调试内存,&结构体变量名就可以知道了
这样做的目的:是结构体后面成员的地址,都成为偏移量,这样就省的我们再去进一步的转它
int main()
{
struct S s = { 0 };
printf("%d\n",OFFSETOFF(struct S, a));
printf("%d\n", OFFSETOFF(struct S,b));
printf("%d\n", OFFSETOFF(struct S, c));
return 0;
}
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6 };
// 找出 5 和 6
// 这里我们用异或的办法(相异出1 ,相同出 0)
// 如果无法将数组元素统一异或,那么我就需要分组
假设
//分组(将两个不同的数字,放进2个组里,其他 成对的数字,都是成对的 按照某种规律,放进对应分组)
//1 1 2 2 5 将这组数异或在一起, 1 和1 异或等于0,0 再和 2异或 等于 2,2 和 2 在异或等于0,最后 0和 5 异或 等于 5,那么我不就得到了该数字
//3 3 4 4 6 与上同理
// 并不一定要这样分,只是假设
//也可以这样分
// 1 1 2 2 3 3 5
// 4 4 6
// 只要你能把那两个不同数,放进不同的分组里,其他的,两个相同的数放进同一个分组,就可以了
// 那怎么分组呢?
// 目标是什么,只出现一次的2个数字,必须在不同的分组里
// 101 5的二进制码
// 110 6的二进制码
//只要是不相同的2个数,它的二进制码里,必定有 某一个二进制位不同(一个是0,一个是1)
// 那么我以这不相同的二进制位,进行分组呢?
// 1 的二进制 001, 2的二进制 010, 3的二进制 011, 4的二进制 100
// 那么想象一下,如果我这里 最低位二进制进行分组
// 分组是不是如下情况所示?
// 1 1 3 3 5
// 2 2 4 4 6
// 那么 第二个进制位呢?
// 分组情况
// 1 1 4 4 5 第二位 是 0
// 2 2 3 3 6 第二位 是 1
// 那么我们开始了
// 其实将数组int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6 };
// 异或在一起,其结果就是 5 和 6 异或的结果,其过程和假设是一样的(相同为 0,相异为1,那么 1 2 3 4 都被抵消了,那剩下就是5 和 6的异或)
int ret = 0;
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
ret = ret^arr[i];
}// 其最后的结果就是 5 和 6 异或的结果
// 5^6 == 101 ^ 110 == 011
// 异或结果为 1,表示 该 1 的二进制位的位置,在 两个数字中 所对应的 二进制码的位置 是不同的,表示我可以 以此 进行分组
// 然后就是 计算 该 1 的二进制位的位置,
int pos = 0;
for (i = 0; i < 32; i++)
{ // 遍历 二进制位
if ((ret>>i) & 1 == 1) // 只要 某个二进制位 与的结果为1, n那么我就要记住 该 1 在 二进制码中位置,以该位置为准,对数组里的数进行分组
// 请注意这里ret并没有 改变,只是移动看了下位置,并没重新赋值
{
pos = i;// 为了 记住 这个 1 的位置(二进制不相同的位 的 位置),创建一个 pos 整形变量
break;
}
}
// 最后按照 pos (不同的位。异或结果为 1 的 位)的 位置,对 数组元素 进行分组。
int m = 0;
int n = 0;
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)// 将数组元素 二进制码 第 pos 位的二进制 为 1 的,分为一组,且异或在一起
异或过程,可参考程序开头的 假设 部分
{
m ^= arr[i];
}
else// 将数组元素 二进制码 第 pos 位的二进制 为 0 的,分为一组,且异或在一起
{
n ^= arr[i];
}
}
printf("%d\n", m);
printf("%d\n", n);
return 0;
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/DarkAndGrey/article/details/120713196
内容来源于网络,如有侵权,请联系作者删除!