const:
volatile:
不对变量加volatile,编译器会对变量做一些优化:
而加了volatile修饰,生成的汇编是这样:
C++的volatile一般只会用在与硬件通信,平时我们编程几乎用不到。
具体可以看:https://en.cppreference.com/w/cpp/language/cv
四个过程!
1、预处理;主要做了以下工作:
2、编译;把预处理后的文件进行一系列操作生成相应的汇编文件
3、汇编;
4、链接;
想更深入了解的朋友,建议看看《程序员的自我修养》,也可以看看我的总结篇:https://mp.weixin.qq.com/s/PaXLQnaCjGkQGIjnPnqRww
这块涉及到很多知识点:
1、通过fork系统调用创建一个新的进程;
2、通过execve系统调用执行指定的ELF文件,附带环境变量和参数;
3、检查ELF可执行文件的有效性,比如魔数(通过魔数可以确定文件格式)、Segment的数量等;
4、寻找动态链接的段,设置动态链接器路径;
5、根据ELF可执行文件的程序头表描述,对ELF文件进行映射,比如代码、数据、只读数据;
6、初始化ELF进程环境;
7、将系统调用的返回地址修改为ELF可执行文件的入口地址
涉及很多前置知识点:
内存缺页就是要访问的页不在主存中,需要操作系统将页调入主存后再进行访问,此时会暂时停止指令的执行,产生一个页不存在的异常。
缺页中断的处理过程如下:
1、如果内存中有空闲的物理页面,则分配一物理页帧r,然后转第4步,否则转第2步;
2、选择某种页面置换算法,选择一个将被替换的物理页帧r,它所对应的逻辑页为q,如果该页在内存期间被修改过,则需把它写回到外存;
3、将q所对应的页表项进行修改,把驻留位置0;
4、将需要访问的页p装入到物理页面r中;
5、修改p所对应的页表项的内容,把驻留位置1,把物理页帧号置为x;
6、重新运行被中断的指令。
能取地址的就是左值,不能取地址的就是右值。
官方定义:
主要用途:
优点:
缺点:
for (char i = 0; i < 256; ++i)
{
printf("%d\n", i);
}
输出为:
0 1 2 … 127 -128 -127 … -1 0 1…无限循环!
127 的二进制为 0111 1111(补码)
-128的二进制为 1000 0000(补码)
原因:
由上图的结果可以看到,ch在127之后,数值由127变成了-128,继续执行下去当ch为-1时又回到最初的 0 值。就像上文画的闭环一般, 0->127,-128->0->127…
下面我们从二进制的角度分析:
在计算机中,有符号数字都使用补码表示。
对于char类型的1字节8位数字,第一位是符号位,其余位是数值位。
2^7-1 = 127
由于 127 + 1 后进位,使得高位溢出到符号位。
而符号位标志着数组的正负,则数值从原先的正数变为负数。
而 1000 0000(补码) 是-128的补码,则他表示有符号数 -128 。
负数的原码与补码之间的关系是:原码 -> 取反+1 => 补码
。
对于正数,不区分原码/反码/补码,都用自身原码表示,只有负数才进行区分。
对于有符号的数,我们在首位取一位作为符号位,表示数值的正负。
而对于0,我们就有两种表示方法:即 +0 ⇒ 0000 0000
、 -0 ⇒ 1000 0000
,这显然是不合理的。
+0
与-0
应该表示的同一个数,而且我们可以发现:
1000 0000
(原) 作为原码时,它的补码任然是 1000 0000
(补)(高位溢出,丢弃)1111 1111
(原) 减一后(加负一)的二进制值也是1000 0000
。而 -127 - 1
的值为 -128
。(负数用补码运算,1111 1111 +1000 0001 =》1000 0000)则我们可以用 补码 1000 0000 表示 -128 。而且由于我们取消“负0”这个概念,就空出了一个二进制位组合可以表示其他数值(用于表示-128)。
由之前的分析可知,补码 1000 0000 的原码值可以表示 负0, 然而我们并不需要“负0”,因此对于 补码 1000 0000 我们并不研究其原码,它只有补码有意义,表示 -128 。因此,在127加一之后就变成了-128 。