深入理解计算机系统

文件是对I/O的抽象,虚拟存储器是对程序存储器的抽象,进程是对一个正在运行的程序的抽象,虚拟机是对整个计算机的抽象。

并发和并行的区别就是一个处理器同时处理多个任务和多个处理器同时处理多个不同的任务的区别。并发指的是逻辑上同时发生,而并行是物理上同时发生。并发性指的是处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生。并行是指同时发生的两个事件,并发不一定并行。

当执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么C语言会隐式地将有符号参数强制类型转换为无符号数,并假设这两个数都是非负的来执行这个运算。e.g. -1 < 0U => false.

扩展一个数的位表示

当把short转换成unsigned int时,我们先要改变大小,之后要完成从有符号到无符号的转换。也就是说,当转换涉及数据位大小以及无符号和有符号数字之间的转换时,先做数据位的扩展,其次做无符号和有符号数字之间的转换。

判断无符号数相加是否溢出

//unsigned x,y;

s=x+y;

判定s是否等于x+y。当且仅当s<x(或者等价地s=x。而另一方面如果s确实溢出,我们就有s=x+y-2w(w为数值二进制表示的位数)。又因为y<2w,所以y-2w<0,因此s=x+(y-2w)<x。

可以使用如下函数判断无符号数相加是否溢出:

/*Determine whether arguments can be added without overflow.
if no overflow return 1, else return 0.*/
int uadd_ok(unsigned x,unsigned y)
{
  unsigned s=x+y;
  return s>=x;
}

除以2的幂

对于非负数来说,算数右移k位与除以2k是一样的;而对于负数却不同,e.g. -7/2=-3, -7>>1=-4.对于负数,如果在右移之前,先将x加上2k-1,那么我们就会得到正确的舍入结果。

x/2k=(x<0?(x+(1<>k.

练习:

写一个函数div16,对于整数参数x返回x/16的值。你的函数不能使用除法、模运算、乘法、任意条件语句(if或者?:)、任何比较运算符或任何循环。你可以假设数据类型int为32位长,使用补码表示,而右移是算数右移。

int div16(int x){
  /*compute bias to be either 0(x>=0) or 15(x<0).*/
  int bias=(x>>31)&0xF;
  return (x+bias)>>4;
}

main函数执行之前,主要就是初始化系统相关资源:



1.设置栈指针



2.初始化static静态和global全局变量,即data段的内容



3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容



4.运行全局构造器,估计是C++中构造函数之类的吧



5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数

BSS段、数据段、代码段、堆与栈

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

在《Programming ground up》里对.bss的解释为:There is another section called the .bss. This section is like the data section, except that it doesn’t take up space in the executable.

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

原文链接: https://www.cnblogs.com/freewater/archive/2012/07/28/2600768.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/57192

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午8:01
下一篇 2023年2月9日 上午8:01

相关推荐