一、算术运算
1、*、/
+
和-
,CPU在一个周期内可以处理完。
但*
和/
在大多数CPU中都是不支持的。
它们可能需要CPU多个周期才能完成,甚至要利用软件的模拟方法去实现。
在arm开发中,*
和/
很可能会被编译器编辑为软件浮点方法。如果是裸机开发,就实现不了,必须借助第三方乘法库/除法库来实现。
如果CPU不支持*
和/
,会导致程序的可移植性变差。
2、%
0%3 = 0
1%3 = 1
2%3 = 2
3%3 = 0
4%3 = 1
... ...
应用场景:
-
取一个范围的数
例如:给一个任意的数字m,得到一个1到100以内的数字?(m % 100)+1
M进制的数
循环数据结构的下标
二、逻辑运算
1、&&、||
// 001.c
#include <stdio.h>
int main()
{
int a = 10;
int res = 0;
res = ((a == 10) || printf("======\n"));
printf("res is %d\n", res);
return 0;
}
// 002.c
#include <stdio.h>
int main()
{
int a = 10;
int res = 0;
res = ((a != 10) || printf("======\n"));
printf("res is %d\n", res);
return 0;
}
2、!
逻辑运算的!和位运算的~区别
int a = 0x0;
!a : 真
~a :0xffff
三、位运算
C语言中最接近底层和硬件开发的一种运算。
对嵌入式工程师,应用频率非常高。
1、移位:<<(左移)、>>(右移)
<<(左移):左移1位,相当于2*
m<<n:m*2^n
4 —— 0100
8 —— 1000
int a = b*32 ——> int a = b<<5 // C语言会将b*32转化为b<<5,这样CPU在一个周期内就可以处理完了
-1 * 2 = -2
8bit举例:
-1:
原码:1 0 0 0 0 0 0 1
反码:1 1 1 1 1 1 1 0
补码:1 1 1 1 1 1 1 1 // -1在计算机中全是高电平
-2:
原码:1 0 0 0 0 0 1 0
反码:1 1 1 1 1 1 0 1
补码:1 1 1 1 1 1 1 0
// -1左移1位,最后面一位就空了,就用0补位。
// 可看出,-2是-1左移一位得到的
>>(右移):右移1位,相当于/2
m>>n :m/2^n
// 右移与正负符号有关
int a; a>>n
unsigned int a; a>>n
// 这两种情况CPU表现形式是不一样的
右移时,如果是负数,则最左边补1; 如果是正数,最左边补0。
/*003.c*/
#include <stdio.h>
int main()
{
int a = 10;
while(a)
{
a = a>>1; // 当a全0时,就不再循环
}
printf("++++++++++\n");
}
/*004.c*/
#include <stdio.h>
int main()
{
int a = -10;
while(a)
{
a = a>>1; // 因为a为负数,首位永远是1,因此a永远无法为0,该循环为死循环
}
printf("++++++++++\n");
}
2、&(逐位与)、|(逐位或)
(1)&
的应用场景:
-
屏蔽
A & 0 —— 0 int a = 0x1234; a & 0xff00; // 屏蔽低8位(bit),取出了高8bit
硬件中,&也叫清零器(clear)
-
取出
A & 1 —— A
(2)|
的应用场景:
-
设置为高电平
A | 1 —— 1
硬件中,|也叫设置器(set)
-
保留
A | 0 —— A
例1:设置一个资源的bit5为高电平,其它位不变。
int a;
a = a | 100000 // 末尾一位是bit0,倒数第2位为bit1 ...
a = a | (0x1<<5)
a | (0x1<<n):将a的第n位设置为高电平。
例2:清除第5位。
int a;
a = a & 0 1 1 1 1 1 // 这个写法是错误的,因为高位为0,会把a的高位也同时都清零;而且,如果移植到别的平台,前面的0更多的话,把把更多位清零。
a = a & (~(0x1<<5)) // 只有第5位为0,其它位全为1
a & (~(0x1<<n)):清除a的第n位。
例3:某几位设置为某个数。
如,想设置4、5、6bit为101,该怎么处理?
3、^(异或)
1 ^ 1 = 0
0 ^ 0 = 0
1 ^ 0 = 1
硬件上用的不多,主要用在算法上。在设计的数学算法上,会用到^
进行加密、解密(AES、SHA1等)。
例:两数交换。
int a = 20;
int b = 30;
// 方法1:引入中间变量。
int c;
c = a;
a = b;
b =c;
// 方法2:不引入中间变量。
a = a ^ b;
b = a ^ b;
a = a ^ b;
4、~(逐位取反)
~0xf0 (32位常量): 0xffff ff0f
四、内存访问符号
对于连续空间中不同成员变量的访问方法:
->
:地址访问。
.
:变量访问。
&p
:取地址。
*p
:指针。
五、逻辑结构
switch里面必须用整型数字,不能用浮点数。
内核编程中,有的场景不得不用goto。
一定要保证goto不能在不同函数间跳,在同一函数内是没有问题的。