C语言基础篇(二)运算符
导航:
2.1 算数运算符
2.2 逻辑运算符
2.3 位运算
2.4 赋值运算
2.5 内存访问符号
----->x<------------->x<--------------->x<--------------->x<------------->x<-----
2.1 算数运算符
-- +, -
在 +(-) 两边的数据类型,尽量一致.比如: A + B, A和B的类型尽量一致, 在定义加法的时候,可以加小括号(),这样
可以避免语法逻辑错误 .
-- *, /, %
在cpu中, 其实是没有 乘法 和 除法 的. cpu只能通过软件模拟的方式, 来翻译这种操作.
比如 : int a = b *10; cpu会用很多的模拟算法, 来解释 * 这种算法. 效率是非常低的 .
如果是我们自己开发逻辑程序的话, * 和 / 是无法实现的. 所以 * 和 / 尽量少用 .
% 是求模. 实际上就是取余数 .
求模的特性就是
res = x % n; 那么 res 的范围就是 0 ~ (n-1)
什么时候用呢?
给一个任意的数字, 可能就是随机数, 这个数字我们要得到一个 1 ~ 100 里面的数,
res = x % 100; //0 ~ 99;
res += 1; // 1 ~ 100;
2.2 逻辑运算符
真 和 假 的选择. ( 非0 和 0 )
-- |, ||; &, &&
问: A || B 是否等价于 B || A ??
答: 不等价. cpu 看到 A 已经是真了, 就不会去计算B的值. 同样 (A && B) != (B && A)
----------------------->
int main (void){
int a = 10;
int res;
res = ((a == 10) || printf("我并不会被执行,因为(a == 10)成立,为真,就不会来判断我了. \n"));
return 0;
}
-----------------------<
-- >>, >=, <, <=
略 .
-- !
原本如果是真, 加了!号后就是假. 对比取反~ .
!: char a = 0x01; !a ==0x00; //原本非零的真,!后就是为0的假 .
~: char b = 0x01; ~b == 11111110 == 0xfe; //按位取反,0~1 1~0.
-- ?, :
常用与三元运算符 .
2.3 位运算!!!
位运算是硬件计算必须使用的方法, 要重点掌握!!
-- <<, >>
移位.移位是在硬件里面常用到的方法.移位分为 有符号 和 无符号 的移位.
unsigned char a = oxff;
左移 n 位, 就是 乘以 2的n次方 . a << 3 -> a* (2的3次方) .
右移 n 位, 就是 除以 2的n次方 . a >> 3 -> a/ (2的3次方) .
这种算法速度非常快.
但是对于有符号位来说,
第一位是符号位,无论怎么移动, 符号位 是不会改变的 .
-- &, |, ^
!!!硬件 设置位 和 屏蔽位 的操作 .
(1) 屏蔽位 & .
char a = 0x34; --> 0011 0100
a & 0xf0:
0011 0100
& 1111 0000
--------------
0011 0000
从这边可以知道,因为 a & 0xf0,那我的目的就是保留高4位,屏蔽低 4位.(又称作清零)
(2) 设置位. | .
char a = 0x34; --> 0011 0100
a | 0x02:
0011 0100
| 0000 0010
--------------
0011 0110
通过或操作,我已经将 0011 0100 设置成了 0011 0110 .
(3) 实际应用 .(建议对以下算法用笔算理清楚)
有一个硬件资源,8位(0~7) . 它的第2位控制led的开关 ,它的第5位,控制蜂鸣器的开关 .其他位是未知,但是很重要并且不能改变的数据位 .
现在,我要让第2位置1打开, 第5位置0关闭 .
现在 char a = xx(fm)x x(led)xx
第一步: 清零. 无论led现在是什么状态, 先清零, 再置位 .
将 0000 0001 << 2 --> 0000 0100
取反. 1111 1011
&运算 . a & 1111 1011 --> xx(fm)x x0xx (置零成功)
第二步: 置数.
将 1 << 2 --> 0000 0100
|运算 . xx(fm)x x0xx | 0000 0100 --> xx(fm)x x1xx (置数成功)
现在,我们已经在不影响 其它位 的情况下,成功地将 第2位 置1 .
同样 将第5位置零,可以参考清零做法 .
将 1 << 5 --> 0010 0000
取反. 1101 1111
&运算 . xx(fm)x x1xx & 1101 1111 --> xx0x x1xx
(4) 总结: << >> & | 这四种运算搭配工作就是 (清零) 和 (置数) !!!!!
先清零 ---> 再置数
清零: 我要清零第 n 位. char a ;
a &= ~(1<< n)
置数: 在清零的基础上,将第n位置 1.
a |= (1<< n)
-- ~
按位取反 .
2.4 赋值运算
=, +=, -+, &=, |= ...
2.5 内存访问符号
其实 程序 就是cpu 跟 内存 打交道,但是怎么打交道呢?就是访问内存的意思.于是就有了访问内存的符号 .
-- ()
()有两种用法,一种是限制符,用来划分运算的优先级 3*(2+4)
另一种就是内存访问的意思,一般用于函数. int func().
-- []
很多人看到这个,就能想到数组.其实在我们看来,没有所谓的数组,数组的本质实际上就是一段内存空间 .
多余的描述只会让人限制了它的意义 .
int a[16]; a[0] = 1; []的作用是通过 a 的地址,找到a的内存空间,然后根据下标0进行偏移,
最终锁定a[0]这个内存空间,大小是 int 32位,最后将这个内存 置 1. a[0] = 0x0001;
-- {}
(1) 函数体的限制符,限制一个函数体的内存空间大小.
(2) 结构体 共用体等的空间限制.
-- ->和.
->和. 是 自定义空间(结构体) 不同变量的访问方法 .
-> 是地址访问 .
. 是变量访问 .
举例:
struct school {
int 校长工资;
struct teacher 李老师;
struct teacher 王老师;
};
struct teacher {
int 工资;
};
上面是两个结构体,其中,teacher结构体在school这个结构体中被声明 .
使用:
struct school 希望小学;
希望小学.校长工资 = 1w; //结构体的直接成员变量可以用 . 来访问 .
希望小学 -> 李老师.工资 = 5000; //如果是跳转到另一个结构体,实际上是访问另一个内存空间,要用地址访问符 ->
希望小学 -> 王老师.工资 = 5000; //同上.
-- &和*
&后面跟数字是与运算 ,后面跟变量是取地址.
比如 int a; &a就是取a得地址, 常用于 scanf("%d", &a); 就是从键盘获取一个整形数值,然后赋给a指向的哪个内存空间,也就是a的值.
* 这可能是最恐怖的,但其实也是最亲切的.竟然有一种看到*我就放心了的感觉...
* 要区别两种场景 声明 和 使用.
--------------------------------------------------
首先我要解释一下,什么是 声明 和 使用.
声明 就是在内存空间中为一个变量 和常量 开辟一个内存空间.
int a;
char b;
char name[5]; 这些都是声明.叫做 声明变量,也有声明函数的.因为函数也是一段内存空间,这里就先不谈.
使用 就是将声明好的变量进行使用 .
a = 100;
b = 'k';
strcpy(name,"kmist",5);等等...
----------------------------------------------------
指针也分为 声明 和 使用 .
(1) 声明
声明指针变量的时候,我们用到*,其意义是告知,这是一个指针变量.
int *p;
char *chp; //区别 char ch; 多了一个*号,就是告知,这是一个指针变量 .
int func(char *p){....} //多了一个*号,就是告知,这是一个指针变量 .
(2) 使用
在声明中,有*告知这个一个指针,但是在使用中,有 *号,是 "访问 指针 指向 的 内存地址" 的意思.
int *age;
*age = 100;
注意:这里应该这样解读: 我声明的一个指针, 这个指针指向了一个int类型的空间.
这个空间的地址,叫做age,这个地址本身是一个内存地址,只不过我为他赋予了一个叫age的名字.
至于这个空间里面的具体内容, 就是 *age .