程序的表示、转换与链接

笔记内容来自:程序的表示、转换与链接

作为理解计算机基础概念的指导路径。理解如下核心概念。


用“系统思维"分析问题

例子1

  • ISO C90标准下,在32位系统上以下C表达式的结果是什么?
    -2147483648 < 2147483647

false(与事实不符)! Why?

  • 以下关系表达式结果呢?
    int i = -2147483648;
    i < = 2147483647

true! Why?

  • -2147483648 - 1 < 2147483647,结果怎样?
如何理解该问题呢?
  • 编译器如何处理字面量
  • 高级语言中运算规则
  • 高级语言与指令之间的对应
  • 机器指令的执行过程
  • 机器级数据的表示和运算

例子2


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?

如何才能理解?
  • 高级语言中运算规则
  • 机器指令的含义和执行
  • 计算机内部的运算电路
  • 异常的检查和处理
  • 虚拟地址空间

例子3

若x和y为int型,当x=65535时,y=x*x;y的值为多少呢?

#include <stdio.h>
 
static int multi(int a, int b)
{
    return a*b;
}
int main(int argc, const char *argv[])
{
    int a = 65535;
    int b = 65535;
    int c;
	
    c = multi(a, b);
    printf(" %d * %d = %d\n", a, b, c);
	
    return c;
}

结果是65535 * 65535 = -131071
现实世界中,x的平方是大于等于0的,但是在计算机接收中并不一定成立。

对于任何int型变量x和y,(x>y) == (-x < -y>)总成立吗?
当x=-2147483648,y任意(除-2147483648)时不成立,Why?

现实世界中成立,但是在计算机世界中是不一定成立的。

如何才能理解
  • 机器级数据的表示
  • 机器指令的执行
  • 计算机内部的运算电路

例子4

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?

如何才能理解
  • 机器级数据的表示
  • 变量的存储空间分配
  • 数据的大端/小端存储方式
  • 连接器的符合解析规则

例子5

/*复制数组到堆中,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等于2的30次方加一的时候,程序会发生什么情况?

当参数count很大时,则countsizeof(int)会溢出。如count等于2的30次方加一时,countsizeof(int)=4。则堆(heap)中大量数据被破坏。

如何才能理解
  • 乘法运算及溢出
  • 虚拟地址空间
  • 存储空间映射

例子6

int a = 0x80000000;
int b = a / -1;
printf("%d\n", b);

运行结果为-2147483648

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

int a = 0x80000000;
int b = -1;
int c= a/b;
printf("%d\n", c);

运行结果为Flfoating point exception,显然CPU检测到了溢出异常。

为什么两者结果不同

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

如何理解
  • 编译器如何优化
  • 机器级数据的表示
  • 机器指令的含义和执行
  • 计算机内部的运算电路
  • 除法异常的处理

例子7

main()
{
    double a = 10;
    printf("a = %d\n", a);
}

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

如何才能理解
  • IEEE 754的表示
  • X87 FPU的体系结构
  • IA-32和x86-64中过程调用的参数传递
  • 计算机内部的运算电路

例子8

double fun(int i)
{
    volatile doubel 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,然后存储保护错
为什么?

如何才能理解
  • 机器级数据的表示
  • 过程调用机制
  • 栈帧中数据的布局

例子9

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 copyij(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机制
  • 访问局部性

例子10

int main(int argc, char* argv[])
{
    int a=10;
    double *p(double*)&a;
    printf("%f\n",*p);              //结果为0.0000000
    println("%f\n",(double(a)));    //结果为10.000000

    return 0;
}

关键差别在于一条指令:fldl 和 fildl

如何理解
  • 数据的表示
  • 编译(程序的转换)
  • 局部变量在栈中的位置

小结

  1. 关键是使用例子引入的核心概念。
  2. 这些概念作为理解的核心。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值