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