一.冯诺依曼结构的主要思想: 1. 计算机应由运算器、控制器、存储器、输入设备和输出设备五个基本部件组成。 2. 各基本部件的功能是: • 存储器不仅能存放数据,而且也能存放指令,形式上两者没有区别,但计算机应能区分数据还是指令; • 控制器应能自动取出指令来执行; • 运算器应能进行加/减/乘/除四种基本算术运算,并且也能进行一些逻辑运算和附加运算; • 操作人员可以通过输入设备、输出设备和主机进行通信。 3. 内部以二进制表示指令和数据。每条指令由操作码和地址码两部分组成。操作码指出操作类型,地址码指出操作数的地址。由一串指令组成程序。 4. 采用“存储程序”工作方式。 二.计算机如何工作: 计算机基本部件: CPU:中央处理器;PC:程序计数器;MAR:存储器地址寄存器 ALU:算术逻辑部件;IR:指令寄存器;MDR:存储器数据寄存器 GPRs:通用寄存器组(由若干通用寄存器组成,早期就是累加器) [

](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042601-1024x522.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042601-1024x522.png)
先想象一下妈妈是怎样做一桌你喜欢(指定)的菜的? 厨房-CPU,你妈-控制器,盘-GPRs,锅灶等-ALU ,架子-存储器 做菜前: 原材料(数据)和菜谱(指令)都按序放在厨房外的架子(存储器)上, 每个架子有编号(存储单元地址)。 菜谱上信息:原料位置、做法、做好的菜放在哪里等 例如,把10、11号架上的原料一起炒,并装入3号盘 然后,我告诉妈妈从第5个架上(起始PC=5)指定菜谱开始做 开始做菜: 第一步:从5号架上取菜谱(根据PC取指令) 第二步:看菜谱(指令译码) 第三步:从架上或盘中取原材料(取操作数) 第四步:洗、切、炒等具体操作(指令执行) 第五步:装盘或直接送桌(回写结果) 第六步:算出下一菜谱所在架子号6=5+1(修改PC的值) 继续做下一道菜(执行下一条指令) 三.程序的转换处理过程:

//经典的“ hello.c ”C-源程序
#include <stdio.h>
int main()
{
printf(“hello, world\n”);
}

hello.c的ASCII文本表示: [

](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042602.png)](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042602.png) 功能:输出“hello,world”,计算机不能直接执行hello.c! 以下是GCC+Linux平台中的处理过程: [![](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042603.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042602.png)](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042602.png) 功能:输出“hello,world”,计算机不能直接执行hello.c! 以下是GCC+Linux平台中的处理过程: [![](http://www.wjgbaby.com/wp-content/uploads/2018/04/18042603.png)
四.C程序实例:

问题一:

//ISO C90标准下,在32位系统上,以下C表达式的结果是什么?
-2147483648 < 2147483647
//false(与事实不符)!Why?

//以下关系表达式结果呢?
int i = -2147483648;
i < 2147483647
//true!Why?

-2147483647-1 < 2147483647
//结果怎样?

/*
理解该问题需要知道:编译器如何处理字面量;高级语言中运算规则;
高级语言与指令之间的对应;机器指令的执行过程;机器级数据的表示和运算;*/

问题二:

sum(int a[ ], unsigned len)
{
int i,sum = 0;
for (i = 0; i <= len–1; i++)
sum += a[i];
return sum;
}
/*
当参数len为0时,返回值应该是0,但是在机器上执行时,却发生访存异常。
但当len为int型时则正常。Why?访问违例地址为何是0xC0000005?*/

/*
理解该问题需要知道:
高级语言中运算规则;机器指令的含义和执行;
计算机内部的运算电路;异常的检测和处理;虚拟地址空间;*/

问题三:

/*
若x和y为int型, 当x=65535时, y=x*x; y的值为多少?
y=-131071。Why?
现实世界中,x2≥0,但在计算机世界并不一定成立。
对于任何int型变量x和y,(x>y) == (-x<-y) 总成立吗?
当x=-2147483648,y任意(除-2147483648外)时不成立,Why?
在现实世界中成立,但在计算机世界中并不一定成立。*/

问题四:

//main.c:
int d=100;
int x=200;
int main()
{
p1( );
printf (“d=%d, x=%d\n”, d, x );
return 0;
}
//p1.c:
double d;
void p1( )
{
d=1.0;
}
/*
打印结果是什么?
d=0,x=1 072 693 248
Why?*/

/*
解该问题需要知道:
机器级数据的表示;变量的存储空间分配;
数据的大端/小端存储方式;链接器的符号解析规则;*/

问题五:

/* 复制数组到堆中,count为数组元素个数*/
int copy_array(int *array, int count) {
int i;
/* 在堆区申请一块内存*/
int *myarray = (int *) malloc(count*sizeof(int));
if (myarray == NULL)
return -1;
for (i = 0; i < count; i++)
myarray[i] = array[i];
return count;
}
//当count=230+1时,,程序会发生什么情况?

/*
当参数count很大时,则count*sizeof(int)会溢出。
如count=230+1时,count*sizeof(int)=4。
堆(heap)中大量数据被破坏!*/

/*
理解该问题需要知道:
乘法运算及溢出;虚拟地址空间;存储空间映射;*/

问题六:

//代码段一:
int a = 0x80000000;
int b = a / -1;
printf(“%d\n”, b);
//运行结果为-2147483648

//代码段二:
int a = 0x80000000;
int b = -1;
int c = a / b;
printf(“%d\n”, c);
//运行结果为“Floating point exception”,显然CPU检测到了溢出异常

//为什么两者结果不同?

/*
objdump反汇编代码,得知除以-1被优化成取负指令neg,故未发生除法溢出;*/

/*
a/b用除法指令IDIV实现,但它不生成OF标志,那么如何判断溢出异常的呢?
实际上是“除法错”异常#DE(类型0)Linux中,对#DE类型发SIGFPE信号*/

/*
理解该问题需要知道:
编译器如何优化;机器级数据的表示;
机器指令的含义和执行;计算机内部的运算电路;除法错异常的处理*/

问题七:

#include <stdio.h>
main()
{
double a = 10;
printf(“a = %d\n”, a);
}

/*
在IA-32上运行时,打印结果为a=0
在x86-64上运行时,打印出来的a是一个不确定值
为什么?*/

/*
IEEE 754 的表示;X87 FPU的体系结构;IA-32和x86-64中过程;
调用的参数传递;计算机内部的运算电路*/

问题八:

double fun(int i)
{
volatile double d[1] = {3.14};
volatile long int a[2];
a[i] = 1073741824; /* Possibly out of bounds */
return d[0];
}
//对于上述C语言函数,i=0~4时,fun(i)分别返回什么值?

fun(0)  3.14
fun(1)  3.14
fun(2)  3.1399998664856
fun(3)  2.00000061035156
fun(4)  3.14, 然后存储保护错
/*
理解该问题需要知道:
机器级数据的表示;
过程调用机制;栈帧中数据的布局*/

问题九:

void copyij (int src[2048][2048],
int dst[2048][2048])
{
int i,j;
for (i = 0; i < 2048; i++)
for ( j = 0; j < 2048; j++)
dst[i][j] = src[i][j];
}
void copyji (int src[2048][2048],
int dst[2048][2048])
{
int i,j;
for ( j = 0; j < 2048; j++)
for (i = 0; i < 2048; i++)
dst[i][j] = src[i][j];
}

//以上两个程序功能完全一样,算法完全一样,因此,时间和空间复杂度完全一样,执行时间一样吗?
//事实是第一段代码比第二段代码快了21倍!

/*
理解该问题需要知道:
数组的存放方式Cache机制;访问局部性*/

视频链接:

计算机系统基础(一):程序的表示,转换与链接

计算机系统基础(二):程序的执行和存储访问

计算机系统基础(三):异常、中断和输入/输出