例 /
int a = 1;
int b = 2;
printf("%d",a/b);
结果 为 0 ,因为 a 和 b都 为int 型相处 结果保留整数部分
如果想要 得到 0.5
float = 1.0f;
float = 2.0f;
printf("%d",a/b);
结果就是0.5
例 %
1. 使用 % 两边必须是整数
int a = 10;
int b = 3;
printf("%d",a%b);
结果就为 1
移位规则 :
左边抛弃、右边补0
例 <<
int a = 10;
00000000 00000000 00000000 00001010 ---- a 的二进制 位
a << 1
(0)0000000 00000000 00000000 00001010
括号里的 0被丢弃,
00000000 00000000 00000000 0001010(0)
右边补上 0
最终为
00000000 00000000 00000000 00010100 ---- 20
左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8。
移位规则: 首先右移运算分两种:
- 逻辑移位 左边用0填充,右边丢弃
- 算术移位 左边用原该值的符号位填充,右边丢弃。
注 绝大部分编译器 使用的 都是 算术移位
1. 算术移位
int a = 10;
00000000 00000000 00000000 00001010 ---- a 的二进制位
00000000 00000000 00000000 0000101(0) --- 括号里是将要丢弃的
(0)0000000 00000000 00000000 00000101 --- 括号里补符号位,0 结果 5
int a = -10;
10000000 00000000 00000000 00001010 ---- a 的二进制位 原码
11111111 11111111 11111111 11110101 ---- a 的反码
11111111 11111111 11111111 11110110 ---- a 的补码
11111111 11111111 11111111 1111011(0) --- 右移丢弃
(1)1111111 11111111 11111111 11111011 --- 补符号位 1 补码,打印需要原码
11111111 11111111 11111111 11111010 --- 反码
10000000 00000000 00000000 00000101 --- 原码 结果 -5
可以见 右移 是 除以 2 ,
与 左移 相反 1位 除 2, 2 位 4 3 位 8 ......
2. 逻辑右移
int a = -10;
10000000 00000000 00000000 00001010 ---- a 的二进制位 原码
11111111 11111111 11111111 11110101 ---- a 的反码
11111111 11111111 11111111 11110110 ---- a 的补码
11111111 11111111 11111111 1111011(0)
(0)1111111 11111111 11111111 11111011
01111111 11111111 11111111 11111011 ----符号位为 0 正数 原返补相同
所以逻辑右移 一个负数 会变成一个挺大的正数
& - 按 (二进制) 位与
| - 按 (二进制) 位或
^ - 按 (二进制) 位异或
计算机中的整数有三种2进制表示方法,即原码、反码和补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位
正数的原、反、补码都相同。
负整数的三种表示方法各不相同。
原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码
将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码
反码+1就得到补码
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统
一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程
是相同的,不需要额外的硬件电路。
如 1-1
1的原码反码补码相同
0000000000000000000000000000000000000001
-1
原码
1000000000000000000000000000000000000001
反码
1111111111111111111111111111111111111110
补码
1111111111111111111111111111111111111111
由补码到反码
反码减一
由反码到原码
符号位不变 补码取反
按位与规则为
对应的 二级制位 只要 有 0 就为 0
2个都为 1 结果 为1
int main()
{
int a = 3;
int b = -5;
正数 的原码补码反码相同
00000000 00000000 00000000 00000011 - 3的补码
10000000 00000000 00000000 00000101 - -5的原码
11111111 11111111 11111111 11111010 - -5的反码
11111111 11111111 11111111 11111011 - -5的补码
00000000 00000000 00000000 00000011 - 3的补码
对应的 按位与只要 有 0 就为 0 2个都为 1 结果 为1
3 & -5
00000000 00000000 00000000 00000011 --- 3
%d 打印 一个 有符号整数
printf("%d", a & b);
}
int main()
{
int a = 3;
int b = -5;
正数 的原码补码反码相同
00000000 00000000 00000000 00000011 - 3的补码
10000000 00000000 00000000 00000101 - -5的原码
11111111 11111111 11111111 11111010 - -5的反码
11111111 11111111 11111111 11111011 - -5的补码
00000000 00000000 00000000 00000011 - 3的补码
对应的二进制位只要有 一 才为一 2个 同时为 0 才为 0
3 & -5
11111111 11111111 11111111 11111011 --- 补码
11111111 11111111 11111111 11111010 --- 反码
10000000 00000000 00000000 00000101 --- 原码 -5
%d 打印 一个 有符号整数
printf("%d", a | b);
}
int main()
{
int a = 3;
int b = -5;
正数 的原码补码反码相同
00000000 00000000 00000000 00000011 - 3的补码
10000000 00000000 00000000 00000101 - -5的原码
11111111 11111111 11111111 11111010 - -5的反码
11111111 11111111 11111111 11111011 - -5的补码
00000000 00000000 00000000 00000011 - 3的补码
对应的二进制位只要不同就 为1 相同为0
3 ^ -5
11111111 11111111 11111111 11111000 --- 补码
11111111 11111111 11111111 11110111 --- 反码
10000000 00000000 00000000 00001000 --- 原码 -8
%d 打印 一个 有符号整数
printf("%d", a ^ b);
}
第一种方法
#include
// 这种方法会有溢出的问题
int main()
{
int a = 10;
int b = 20;
printf("a = %d b = %d\n", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("a = %d b = %d\n", a, b);
}
第二种方法
结论 异或 支持 交换律
int main()
{
int a = 10;
int b = 20;
printf("a = %d b = %d\n", a, b);
a = a ^ b;
相同的两个数字异或还是原来的值
例 3 ^ 3
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000011
异或 规则 相异为 1 相同 为 0
所有 3 ^ 3 为 0
00000000 00000000 00000000 00000101 ---5 的补码
00000000 00000000 00000000 00000000 ---0 的补码
异或一下结果 为 5
得出结论 0 不管与谁异或都得本身 0 ^ 0 除外
所以 a ^ b ^ b == a ^ 0 == a
0 ^ 5 == 5
b = a ^ b; // a ^ b ^ b
a = a ^ b;// a ^ b ^ a == a ^ b ^ b ^b ^ a
printf("a = %d b = %d\n", a, b);
return 0;
}
翻译 题目 求整数二进制补码在内存1得个数
方法一
a & 1
int a = 3
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000001
得到最后一位 有一位 1:
然后 然 a 像 右移动一位
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000001
说明 第二位 也是 1
继续右移
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
按位与 &
00000000 00000000 00000000 00000000
说明得 三位 不为 0
依次类推 就能得到 1 得个数
代码如下
int main()
{
int num = 648;
int count = 0;// 计数器
int i = 0;
for (i = 0; i < 32; i++)
{
if (num & 1 == 1)
{
count++;
num = num >> 1;
}
else
{
num = num >> 1;
}
}
printf("%d", count);
return 0;
}
方法 二 : 我们可以了解 一下奇数的 二进制 最后一位必定为一,我们让 a % 2 == 1 就说明a 为奇数 ,a/2 得到一个新的数 在继续
代码如下
int main()
{
int a = 10;
int count = 0;// 计数器
while (a)
{
if (a % 2 == 1)
{
count++;
}
a /= 2;//得到一个新的数,直到为0
}
printf("%d", count);
return 0;
}
但这种方法只能计算正数 有缺陷
方法三 (最优解)
思路
假设num的二进制序列为1111
num-----------1111
num-1-------- 1110
num=num&(num-1)
num-----------1110
num-1 --------1101
num=num&(num-1)
num-----------1100
根据以上例子,我们不难看出每次num=num&(num-1)之后,num的二进制序列都会少一个1,直至循环到num为0时,count就是二进制中1的个数
代码如下
int main()
{
int a = 10;
int count = 0;
while (a)
{
count++;
a = a & (a - 1);
}
printf("%d", count);
return 0;
}
加深印象题
第一题 输入两个整数,求两个整数二进制格式有多少个位不同
1.最优解 方法一
#include
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
int c = a^b;
int count = 0;
while(c)
{
count++;
c = c&(c-1);
}
printf("%d",count);
return 0;
}
2. 方法二
#include
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int c = a ^ b;
int count = 0;
while (c)
{
if (c % 2 == 1)
{
count++;
}
c /= 2;
}
printf("%d", count);
return 0;
}
3. 方法三
#include
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int c = a ^ b;
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (c & 1 == 1)
{
count++;
c = c >> 1;
}
else
{
c = c >> 1;
}
}
printf("%d", count);
return 0;
}
第二题 获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
#include
int main()
{
int n = 0;
int i = 0;
scanf("%d",&n);
for (i = 0; i < 32; i+=2)
{
printf("%d ", (n >> i) & 1);
}
printf("\n");
for (i = 1; i < 32; i += 2)
{
printf("%d ", (n >> i) & 1);
}
return 0;
}
赋值操作符 其实就 是为变量赋值的
如
int a = 10;
如果 你觉得 10 不行 可以改
a = 20;
int b = 0;
int c = 0;
连续赋值
a = b = c = 10;
这样也是可以得,赋值操作符没有太多东西 了解这些就差不多了
+= 如 a += 10 与 a = a + 10 等价
-= a -= 10 a = a - 10
*= a *=10 a = a * 10
/= a /= 10 a = a / 10
%= a &= 10 a = a & 10
>>= a >>= 10 a = a >> 10
<<= a <<= 10 a = a<< 10
&= a &= 10 a = a & 10
|= a |= 10 a = a| 10
^= a ^=10 a = a^10
int main()
{
int a = 10;
if(!1) 此时 不会打印 a if 条件 为真 但 ! 把 1 转化为 0 条件为假就不会进入 if语句中
{
printf("%d",a);
}
return 0;
}
正值 与 负值 其实就与数学中得 + - 相同 在这就不讲解 只是让大家知道 他们 为单目操作符
取地址操作符 一般与指针 那块息息相关
我们 来了解 一下 & 得作用
int a = 10;
&a 就是拿到变量在内存中得起始地址
int* p = &a; p 就为一个指针变量
printf("%p",&a); 打印 a 得地址
int arr[10] = {0};
&arr 此时 拿出得数组得地址 指向第一个元素
int(*p)[10] = &arr;
sizeof 也是一个 单目操作符
int n =10;
int sz = sizeof(n); sizeof 计算得是 n 所占内存得大小,单位是字节
计算变量所占内存空间得大小
运用sizeof 计算数组大小
int arr[10] = { 0 };
int sz = sizeof(arr)/sizeof(arr[0]);
注意 sizeof(arr) 此时计算整个数组所占内存空间大小
int a = 0;
~ 是按二进制位取反
00000000 00000000 00000000 00000000 --- 0 原反补相同
~ 按位取反后
11111111 11111111 11111111 11111111 --- 补码
11111111 11111111 11111111 11111110 --- 反码
10000000 00000000 00000000 00000001 --- 原码
printf("%d",~a); ---- -1
前置++ 与 --
后置++ 与 --
int main()
{
int a = 10;
int b = ++a;
int c = --a;
int d = a++;
int f = a--;
printf("%d %d %d %d",b,c,d,f);
结果为 11 9 10 10
为啥呢 前置++ 和 -- 就相当于 b = a + 1; c = a - 1
后置 ++ 和 -- 相当于 d = a; a = a + 1; f = a; a = a-1
可以看出 前置 是 (先 ++ 或 --) 使用 在赋值
而 后置 是 先赋值 在使用
}
( * )功能 :取该指针指向的存储单元的值
int main()
{
int a = 10;
int* p = NULL;
p = &a;
//*p;
// p 存放的地址 ,* 就是 找到他所指向的对象的值
printf(" %d ",*p);
}
int a = 10;
printf("%f",(float)a);
我们知道%f 是打印 浮点型数据的 这里我们想要打印
a 可以 强制类型转换 (float) a
强制类型转换 其实就是在要转换的变量前 加上一个() 括号里输入要转换的类型
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
关系运算符 就是一些比大小 等的操作符 ,但要注意
不能 把 == 写 做 = 要不 会出现 错误
如循环 把 == 写成 = 那么循环 大概率会死循环
还有 if 语句 如果 写成 = 那么 不管 条件成不成立 都会进入语句。
&& 相当于 并且 ,两边都为真 才能为真 ,一个为假 都为假
int main()
{
int a = 3;
int b = 5;
if(a && b)
{
printf("%d",a+b);
}
else
{
printf("%d",b-a);
}
return 0;
}
运行后 我们发现结果 为 8 为什么呢
&& 只关注真 与 假
如果 两边都为真 则返回 1
反之 返回 0
如 3 && 5 结果 就为 1
3 && 0 结果 就为 0
|| 相当于 或者 只要有一个为真就为真,
int main()
{
int a = 3;
int b = 0;
if(a || b)
{
printf("%d",a+b);
}
else
{
printf("%d",b-a);
}
return 0;
}
结果 为 3
b 虽然 为 0 但 || 了 也可以进入 if 语句
注
a = 3, b = 0
if(a || ++b)
请问 此时 b 的值为多少
这里有一个大坑 || 会先判断 a 是否为真 如果为真
就会直接 进入 语句 ,++b 就不会执行 ,
所以b 还是 0.
#include
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0; }
逻辑与 时 a == 1,b == 2 c == 3, d == 4
a++ 后置 ++ 会先 使用 a == 0,后 ++ 然后 && 第一个为假 后面就不会继续计算
逻辑或 a == 1,b == 3,c == 3, d == 4
a ++ 后置 ++ 会先 使用 a == 0 后 ++ 然后 || 第一个 为假, 判断第二个表达式 ++b == 3为真 后面就不用判断了
exp1 ? exp2 : exp3
表达式1 ? 表达式 2 :表达式 3
int main()
{
int a = 3;
int b = 0;
if(a > 5)
{
b = 3;
}
else
{
b = -3;
}
}
运用三目操作符 简化以上代码
b = ( a > 5 ) ? ( b = 3 ): ( b = -3 );
a > 5 吗 不 大于 那将 b = -3 赋予 b
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
c 为 13 从 左边 算到 右边,结果 取最右边的
操作数:一个数组名 + 一个索引值`
int arr[10] = { 0 };
arr[7] = 6; 就是通过下标引用操作符 为第8个元素赋值
4 + 5 = 5 + 6
这个为 加法的交换律那么 下标引用操作符遵守这个吗
arr[7] = 4;
7[arr] = 4;
大家可以尝试一下
结果 发现这样 也没有 错误,更能体现 [] 是一个操作符
但 不能 这样 定义数组
补 arr[7] == *( arr + 7 ) == *( 7 + arr )== ( 7[arr] )
arr 指向数组首元素的地址 ,+7 向右移动 7位 就为第 8个元素的地址 * 找到 这个地址所代表的值
int Add(int x,int y)
{
return x+y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a,b); 这里的 () 就是函数调用操作符 ,操作数 Add , a , b
return 0;
定义一个结构体
struct Stu
{
char name[20];
int age;
double score;
}
void set_stu(struct Stu s)
{
s.name = "zhangsan";
name 是一个数组 ,数组是一个地址,不能将一个字符串赋给一个地址
随意要用 strcpy () 库函数
strcpy(s.name,"zhangsan");
s.age = 20;
s.score = 100.0;
}
void print_stu(struct Stu s)
{
printf("%s %d %f",s.name,s.age,s.score);
}
int main()
{
struct Stu s = { 0 };
set_stu ( s );
}
上面 会打印不出来 因为 是值传递 ,形参的改变 并不会影响实参,我们要使用 址传参
struct Stu
{
char name[20];
int age;
double score;
}
void set_stu(struct Stu *ps)
{
strcpy((*ps).name,"zhangsan");
(*ps).age = 20;
(*ps).score = 100.0;
strcpy(ps->name,"zhangsan");
ps->age = 20;
ps->score = 100.0;
这里使用 -> 简洁一点
(*ps).age 等价于 ps->age
}
void print_stu(struct Stu s)
{
printf("%s %d %f",s.name,s.age,s.score);
}
int main()
{
struct Stu s = { 0 };
set_stu ( &s );
print_stu (s);
}