曲径通幽处——记录MemCpy调用过程

来源:互联网 发布:杭州叁点零正式软件 编辑:程序博客网 时间:2024/06/06 08:30

曲径通幽处——记录MemCpy调用过程

刺猬@http://blog.csdn.net/littlehedgehog

关于MemCpy C语言的调用问题,我在于渊的博客中也看到有读者提出过类似的问题。当时也没想明白,和天之痕仔细探讨了下,把得出的结论种种罗列下来.

MemCpy声明: 摘自《自己动手》 P185

PUBLIC    void * memcpy(void *pDst,void *pSrc,int iSize);
本来是个很普通的C函数声明,没多少可讨论的价值,但坏就坏在函数的调用上,不知会让多少大一当年自认为C语言不过尔尔的年轻人大跌眼镜。

memcpy(  &gdt,  (void*)(*((t_32*)(&gdt_ptr[2]))),  *((t_16*)(&gdt_ptr[0]))+1  ) ;
我们来一个一个地说明。


第一个参数 &gdt

gdt的定义是一个数组,如下所示:

PUBLIC    DESCRIPTOR    gdt[GDT_SIZE];
 可能有人会想gdt既然是数组名,那么gdt就是指向数组首地址的指针了,而&gdt 就是指向前一个指针的指针。那么这个实参就应该是一个二级指针了吧~~ 如果这样认为就错了。我们不妨做这样一个实验:

#include <stdio.h>
int main()
...{
    int a[3]=...{0,1,2};
    printf("%d %d ",a,&a);
    return 0;
}
 把结果打印出来看看吧,可以看到其实两个a和&a打印出来的数值是一样的! 也就是说&a也是数组首地址。我们的第一个结论是&gdt 还是gdt描述符数组的首地址!

 

第二个参数:  (void*)(*((t_32*)(&gdt_ptr[2])))

初看很麻烦,其实我们挨着把它剥离出来分析就觉得很简单了。

最里面一层是&gdt_ptr[2] ,这个还好理解,就是取gdt_ptr第三个元素的地址,然后我们在其前面加上一个(t_32 *),很抽象。如果我们换一个样子这个东西就不抽象了。

 (int *)(&a[2])  这个就好懂了吧~~ 把数组a的第三个元素取出地址然后强制转化为int类型的指针。

(t_32 *)(&gdt_ptr[2]) 把gdt_ptr数组第三个元素取出地址然后强制转化为32位的指针。   但是我们为什么要这么做呢,因为GDT的基址是32位的,我们可以理解为 取第三个元素(gdt_ptr[2])的地址即是找到了一个指向第三个元素的指针,但是这个指针只是指向了第三个元素这个小块内存,而我们要GDT的基址是4小块内存(基址是32位的哦!),所以我们强制加上一个(t_32 *),让它指向4块内存。

好了,再到外面一层,我们又加上了一个*,这个应该很好理解的,一个地址又加上了*,表示是这个内存地址里面的值。如同

int a=3;
int b=*(&a)
到最后一层了,我们再次把问题简化,既然前面提到内存地址又加上一个* 就是该地址里面的值,那么 上面的表达式我们改写成这个 (void *)a   试问这个表示何物?

我们做如下实验:

int a=3;
void *p=(void *)a;
printf("%d ",*p);
打印出来的结果是多少? 是3么?还是打印的是a的地址?这个问题不回答可能反而是对的。因为上面的代码是错误的! 因为p的指向是个不正确的值,我们运行就会报错! 正确的代码如下:

int a=3;
void *p=(void *)a;
printf("%d ",p);
 这个打印结果是3!我当时纳闷了很久,这个为什么是3? p的值是3,也就是说p指向地址3的那个内存单元。我们回头看看这句 void *p=(void *)a;  按照我们普通的理解 int b=a;  把a的值拷贝到b ,那么void *p=(void *)a;  应该就是把a的值拷贝到了p,但是由于p变量的特殊性,它是指针,p得到的值是3,这表示p指向地址3的内存单元。  按照那么第二个参数的意思也不言而喻了:

第二个结论: 

 (void*)(*((t_32*)(&gdt_ptr[2]))) 先找到原来的gdt的基址(也就是 &gdt_ptr[2] ),为了取得后面四小块内容,我们由此引出一个强制转化为t_32的指针,该指针的值是原gdt的值,也就是指向原基址,很简单的,人为地复杂化了~~

另外,书中的第三个参数是错误的,gdt这个数据结构的大小应该是 界限+1 ,而不是直接的gdt界限 *((t_16 *)(&gdt_ptr[0]))   

同时建议各位看官有时间把这个函数调用反汇编看看,可能理解更深入点儿

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/littlehedgehog/archive/2008/03/20/2200212.aspx