引用,实际上是C++为了简化指针操作,对指针的操作进行了封装,产生了引用类型。实际上引用类型就是指针类型,只不过它用于存放地址的内存空间对使用者而言是隐藏的,而且对引用的任何操作都会被重定向到其指向的变量。
一句话,引用是一种特殊指针。
参数传递:参数传递时候,必然会发生从实参到形参的拷贝。其中:
·普通值做参数时,直接拷贝值从实参到栈上作为形参;
·指针做参数时,拷贝的是指针变量中的值,也就是其指向的变量的地址;
·引用做参数时,拷贝的是其指向的变量的地址;
·浮点数做参数时,拷贝其值到栈上,中间采用浮点指令完成;
·数组做参数时,拷贝的是数组首地址,也是作地址用;
·结构体和类做参数时,若无自定义拷贝构造函数,则依次拷贝对象域内的数据到形参(默认拷贝构造),若有自定义的拷贝构造函数,则调用构造函数在栈上构造形参。
返回值:
·普通值做返回值时,直接用eax返回;
·指针做返回值时,用eax返回指针值;
·引用做返回值时,用eax返回其指向的变量的地址,同指针;
·浮点数做返回值时,将返回值存储到浮点数栈的栈顶,也就是ST0做返回值;
·数组做返回值时,用eax返回其数组的首地址,同指针;
·结构体和类做返回对象时,
若无自定义的拷贝构造函数,
1.对象大小不大于(<=)4字节,直接用eax返回;
2.对象大小大于(>)4字节,且不大于(<=)8字节,用eax和edx返回;
若存在自定义的拷贝构造函数或赋值操作符重载,或者无自定义的拷贝构造函数和赋值操作符重载但对象大小大于(>)8字节时,则隐含传入一个返回对象(可能是临时对象或者接收对象)的地址,在函数内部,对该临时对象的各成员依次赋值(默认拷贝构造),eax返回临时对象的地址。
当返回对象被用作拷贝构造的参数去构造新对象时,直接把新对象的地址做返回对象;
当返回对象被用作assignment操作时,需要生成一个临时对象去过渡,该临时对象会被作为返回对象。
本系列结尾:
本系列文章中的重要分析部分都在反汇编代码后面的注释上,而且重点的理解也要结合代码调试去完成。
总之,实践才是检验真理的唯一标准。
作者署名:cr09philip
参考文献:钱林松《C++反汇编与逆向分析技术揭秘》
原文网址:http://www.nynaeve.net/?p=186
上次,我描述了编译器和链接器为访问__declspec(thread)扩展类变量所使用的生成代码的机制。尽管此时它们已经为隐式TLS布置了舞台,但为了使整体能够工作,仍然需要加载器这个组件来提供必需的运行时支持。
具体的,加载器将负责为每个模块分配TLS索引值,为每个线程的TEB中的ThreadLocalStoragePointer分配内存空间。此外加载器还需要为每个模块分配TLS存储空间。
概念上,加载器中和TLS相关的分配和管理职责可以被划分为四个方面:(注意这是在Windows Server 2003版本和比其早的版本;之后将分析下Vista中所做的修改)
1. 进程初始化阶段,为变量_tls_index分配索引值,确定每个模块所需的TLS空间内存的大小,然后调用TLS和DLL初始化函数(同一模块,先调用TLS初始化函数,后调用DllMain初始化函数)。
2. 在线程初始化阶段,为每一个使用了TLS的模块分配TLS内存并初始化,根据使用TLS的模块数目为当前线程分配ThreadLocalStoragePointer数组,然后将各个模块的TLS内存和ThreadLocalStoragePointer数组中的对应项相关联。然后为当前线程调用TLS初始化函数和DLLMain初始化函数。
3. 在线程终止的时候,调用TLS初始化函数和DLLMain函数(根据参数确定是线程终止),释放当前线程中每个模块对应的TLS内存,然后释放ThreadLocalStoragePointer数组。
4. 在进程终止时,调用TLS和DLlmain初始化函数。
当然,加载器在完成上面所列工作的同时也做了其他事情;以上所列的只是TLS支持中的关键部分。
除了进程初始化以外,其它大部分操作都非常直观。进程初始化主要是由ntdll中的LdrpInitializeTls和LdrpAllocateTls两个例程来完成的。
当所有静态连接的dll文件被载入之后,所有其它初始化例程被调用之前,LdrpInitializeTls被调用(说明优先级比较高,是关键的部分)。基本上,该函数要遍历所有加载模块,为每一个具有有效TLS目录的模块统计出它使用的TLS内存的大小。对每一个使用了TLS的模块,会分配一个数据结构来记录该模块所使用的TLS内存大小并为其分配的索引号(_tls_used)。(早在Xp系统中,LDR_DATA_TABLE_ENTRY结构中的TlsIndex域貌似就没有使用了。而在WINME系统中将该值误用为模块的TLS索引,因此假定该值为-1在WINME系统中是不可靠的)
使用了TLS的模块在调用LdrpInitializeProcess的过程中将被标记为始终位于内存当中(这种模块的LoadCount值为0xFFFF)。实际中,这个不是什么问题,因为这种模块必须是静态链接的或是被主模块隐式依赖,不可能中途退场。
在函数LdrpInitializeTls为模块分类了TLS索引之后,将调用LdrpAllocateTls为初始线程初始化TLS值。
这时,进程继续初始化,最后每个模块的TLS初始化和DLLmain初始化函数会被调用。(注意应用程序主模块可以有多个TLS回调函数,但是没有DLLmain函数)
一个有意思的事情是同一个DLL模块的TLS初始化函数始终在DLL初始化函数之前调用。(这个过程按顺序进行,例如先A.dll的TLS初始化,A.dll的DLLmain初始化,B.dll的TLS初始化,B.dll的Dllmain初始化,以此类推)。这意味着在TLS初始化函数中要慎重使用CRT的函数((as the C runtime is initialized before the user’s DllMain routineis called, by the actual DLL initializer entrypoint, such that the CRT will notbe initialized when a TLS initializer for the module is invoked).)。这将非常危险,因为全局数据还没有被创建;除非导入被跳过,否则模块将处于一个完全未初始化的状态。
另一个值得一提的有关加载器对TLS支持的方面是PE文件格式标准中,IMAGE_TLS_DIRECTORY结构中的SizeOfZeroFill域并没有被链接器和加载器使用。这意味着在现实中,所有TLS模板数据都将初始化,TLS内存块的大小不像PE文件格式标准所陈述的的那样包含域SizeOfZeroFill。
一些软件滥用TLS回调来用于反调试的目的(通过创建一个TLS回调项来在入口函数获得执行权之前执行代码),虽然可以,但是实际中这点将非常明显,因为大部分PE文件都不会使用TSL回调。
直到Windows Server 2003,上述就是加载器对__declspec(thread)存储类的所有支持。这个方法看起来工作的很好,但事实上存在些问题(如果你一直看到现在,你也许会发现是什么问题)。更多关于以上方法的限制的讨论将在下一周为大家讲述。
这个函数是在ViewGroup里定义的,主要用于控制child View获取焦点的能力,比如是否阻止child View获取焦点。
他有三个常量可供设置
public void setDescendantFocusability(int focusability) { switch (focusability) { case FOCUS_BEFORE_DESCENDANTS: case FOCUS_AFTER_DESCENDANTS: case FOCUS_BLOCK_DESCENDANTS: break; default: throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, " + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS"); } mGroupFlags &= ~FLAG_MASK_FOCUSABILITY; mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY); }
可以看到,只有这三个常量可以设置,不是这三个常量会抛出异常的。
设置后,会在requestFocus(int direction, Rect previouslyFocusedRect) 方法里根据设置进行相应的处理。来看下实现
public boolean requestFocus(int direction, Rect previouslyFocusedRect) { if (DBG) { System.out.println(this + " ViewGroup.requestFocus direction=" + direction); } int descendantFocusability = getDescendantFocusability(); switch (descendantFocusability) { case FOCUS_BLOCK_DESCENDANTS: return super.requestFocus(direction, previouslyFocusedRect); case FOCUS_BEFORE_DESCENDANTS: { final boolean took = super.requestFocus(direction, previouslyFocusedRect); return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect); } case FOCUS_AFTER_DESCENDANTS: { final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect); return took ? took : super.requestFocus(direction, previouslyFocusedRect); } default: throw new IllegalStateException("descendant focusability must be " + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " + "but is " + descendantFocusability); } }
通过这里的实现可以看到上面定义的三个常量设置的意思。。
已有 0 人发表留言,猛击->>这里<<-参与讨论
ITeye推荐
- —软件人才免语言低担保 赴美带薪读研!—