cs107 编程范式(十三)

339 阅读2分钟

复习

int main() {
    DeclareAndInitArray();
    PrintArray();
}

void DeclareAndInitArray() {
    int array[100];
    int i;
    for(i = 0; i < 100; i++)
        array[i] = i;
}

void PrintArray() {
    int array[100];
    int i;
    for(i = 0; i < 100; i++)
        printf("%d\n", array[i]);
}

大家可以能会觉得这代代码有错,但是它确实可以输出正确的结果。原因在于当执行call <DeclareAndInitArray>指令之后,在内存中形成如下图所示内存分布:

捕获2.PNG

在函数结束跳转到一条指令时,PrintArray()会形成相同的内存分布,其中数据并没有被清空,因此可以无缝衔接。

这种技巧可以帮助我们在函数中传递数据。

注:老师说,他写驱动程序的时候,使用过这种技巧。但我想今天可能没有人会继续用这种方式了吧。

printf函数

int printf(const char* control, ...);

... 表示可以传递任意数据类型,任意数量的参数。返回值表示成功解析占位符的个数,出错返回-1。

现在来解释一下,为什么从第0个参数开始压入栈中。

首先,编译器会计算参数个数,并且计算出需要让栈指针减去多少个字节为参数提供空间。

举个例子:

printf("%d + %d = %d\n", 4, 4, 8); 

捕获3.PNG

在这种情况下,函数并不知道除了char*参数上面还有多少个参数。这时候,函数会对字符串进行分析,来判断参数类型和个数,因此这个字符串就好像是栈帧上方数据类型的导航图。当读取到第一个%d时,就会将char*上方四个字节的内容解释成int类型,之后依次进行解析。

一个小技巧

struct base {
    int base;
};

struct type_one {
    int code;      // 1
    ...
};

struct type_two {
    int code;
    ...
};

在结构体中,第一位总是在地址最下的位置,这里的code表示这两种可能的结构体。举个例子。

void func(struct base* b) {
    if(b->base == 1) {
        struct type_one* one = (struct type_one*)b;
    } else if(b->base == 2) {
        struct type_two* two = (struct type_two*)b;
    }
}

int main() {
    struct type_one* one = (struct type_one*)malloc(sizeof(struct type_one));

    func((struct base*)one);
}

这种编程技巧在网络编程中很常见,以后有机会在具体说说。

注:内存模型是老师自己定义的,32位机器。

总结

自此,cs107中指针和内存模型部分就讲完了,后面的内容由于过于陈旧,我就不在继续跟进了。希望这个系列对大家有帮助。完结,撒花!!!