复习
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>指令之后,在内存中形成如下图所示内存分布:
在函数结束跳转到一条指令时,PrintArray()会形成相同的内存分布,其中数据并没有被清空,因此可以无缝衔接。
这种技巧可以帮助我们在函数中传递数据。
注:老师说,他写驱动程序的时候,使用过这种技巧。但我想今天可能没有人会继续用这种方式了吧。
printf函数
int printf(const char* control, ...);
... 表示可以传递任意数据类型,任意数量的参数。返回值表示成功解析占位符的个数,出错返回-1。
现在来解释一下,为什么从第0个参数开始压入栈中。
首先,编译器会计算参数个数,并且计算出需要让栈指针减去多少个字节为参数提供空间。
举个例子:
printf("%d + %d = %d\n", 4, 4, 8);
在这种情况下,函数并不知道除了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中指针和内存模型部分就讲完了,后面的内容由于过于陈旧,我就不在继续跟进了。希望这个系列对大家有帮助。完结,撒花!!!