-
整数类型(如int、short等)对于公式来说是不一定成立的。因为会有溢出的存在。
short a_2 = 32768; std::cout << a_2 << std::endl; ----------------------------------- 结果:-32768
-
float类型的结合律不一定成立。
计算机中,float的取值范围特别大,当两个相差特别大的数进行运算时,可能会使得较小的数字消失,所以不满足结合律。
float x=1e20, y=-1e20, z = 3.14; std::cout<<"(x+y)+z="<<(float)((x+y)+z)<<", x+(y+z)="<<(float)(x+(y+z))<<'\n'; ----------------------------------- 结果:(x+y)+z=3.14, x+(y+z)=0.0
-
C程序中通常会有内存引用错误。
C和C++并没有提供内存保护,比如有超出边界的数组、非法的指针以及滥用的malloc/free通常都会造成bug,这个bug是否出现,还取决于你的系统和编译器,并且这个bug可能要运行很多次才会发现。这里你可能就需要根据内存的排列方式来对某个数据结构进行修改。
//比如我们创建一个结构体并对其进行赋值 typedef struct { int a[2]; double d; }demo; double fun(int i) { volatile demo s; s.d = 3.14; s.a[i] = 10773741824; // possibly out of range return s.d; } 进行测试: printf("fun(0)=%.10f,fun(1)=%.10f,fun(2)=%.10f,fun(3)=%.10f\n",fun(0),fun(1),fun(2),fun(3)); printf("fun(4)=%.10f\n",fun(4)); printf("fun(5)=%.10f\n",fun(5)); ----------------------------------- 结果: fun(0)=3.1400000000,fun(1)=3.1400000000,fun(2)=3.1400003595,fun(3)=-0.0000000000 fun(4)=3.1400000000 //这里到fun(5)时,程序停止运行,而且没计算出fun(5)的值
这其实和数组如何在内存中布局,以及内存是如何访问的相关。我们首先看一下这个结构是如何实现和排列的:
这里每一行表示4个字节,其中:int是4个字节,double是8个字节。
当我们运行
fun(0)
和fun(1)
时,修改的就是数组a中的值;但是但我们运行
fun(2)
后,修改的就是浮点数d中的内容了,所以就会出现上面奇怪的数字。后面的
fun(3)≠3.14
,是因为double类型8字节,而int类型4字节,所以,int类型需要两次才能走完这8字节,即这一次还是在修改d的内容。fun(4)
时,已经过了d。所以,此次输出为3.14。-
但当在
fun(5)
时,程序停止运行,当我们输入到某个值后,我们就修改了某些用来维持该程序运行的状态,这就导致了程序崩溃。上图中是在fun(6)时,程序崩溃。这个不同机器结果不同。
所以内存引用错误主要是因为C和C++没有提供边界检查,所以很容易编写出非法的代码。
-
There's more to performance than asymptotic complexity
课程的第四个主题是要从程序角度增加它的性能。CS课程的其他部分,更多强调在正确的数据结构中获得正确的算法,但是他们需要进行不同层次的优化:算法、数据表示、过程和循环。这时候你需要知道系统到底是如何运行的,是什么让它运行很好,以及是什么让它运行不好。
比如以下两个函数:
# 代码一 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]; }
上面代码的目的都是一样的,都是:都是将一个矩阵或数组从原地址
src
复制到目标地址dst
。不同点是:唯一的不同仅在于嵌套顺序不同,代码一进行的是行优先,而代码二进行的是列优先的。
性能方面:在普通的系统中,代码一运行速度为4.3ms,而代码二运行速度为81.8ms,性能相差差不多20倍。
可以看以下这张图,它显示了四种不同的内存访问模式,这里不会细究,我们可以发现这两个函数在这个内存访问模式中处于不同的位置,
copyij
代表以行优先形式进行访问,而copyji
代表以列优先形式进行访问, 并且copyij
比copyji
性能好很多,这和内存层次结构中的缓存有关。 -
计算机能做的事情远不止执行程序
课程的最后一部分将更多讨论不仅让计算机孤立运行小程序,而且能够通过网络来彼此交谈,实现像web服务器这样的服务。
参考: