【汇编 C++】C++this指针、this指针长什么样?

目录

前言

一、什么是this指针?

        this指针长什么样?

        简单总结

二、this指针的使用

        this指针是否有用?

        this指针其他的特点

总结

结语


前言

        本教程可能需要一点汇编的底子,不过不懂汇编的也可以放心食用,我都会解释汇编代码的大致意思。使用的工具是vs2010。

        在此之前先复习一下一些琐碎的知识吧,在VC编译器中大多数函数调用之后,函数的内部堆栈中:

        ebp存放原ebp的值

        ebp+4返回地址

        ebp+8第一个参数...

        ebp-4往上是缓冲区,局部变量一般存放在这里

一、什么是this指针?

        this指针是“C++封装后的结构体或类中成员函数”的一个默认参数,用于存放当前结构体变量(类的对象)的首地址,它是一个隐藏的参数。
        概念听起来很模糊,不急,代码测试之后就能明白。

        测试程序demo01.cpp:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

};

// 结构体的外部定义函数
int ReturnAdd(int a,int b,int c)
{
    return a+b+c;
}


int main()
{
	struct st_info stinfo = {1,2,3};

	int x = ReturnAdd(stinfo.a,stinfo.b,stinfo.c);    // 此处下断点,观察反汇编

	printf("%d\n",x);

	system("pause");
    return 0;
}

        我们先把函数定义在结构体的外部,现在的结构体就是个简单的结构体,没有进行封装,函数也是个简单的函数,没有this指针。

        vs2010 F7编译、F5调试、ALT+8反汇编:

【汇编 C++】C++this指针、this指针长什么样?_第1张图片

        通过反汇编我们观察到,当函数在结构体外部(没有封装)时,函数传递的参数是三个。

        this指针长什么样?

        我们也说了,函数没有被封装到结构体或类的内部时是没有this指针的

        那么下面我们把函数放到结构体内部试试,代码稍微改动:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

    
    // 结构体的外部定义函数
    int ReturnAdd(int a,int b,int c)
    {
        return a+b+c;
    }

};


int main()
{
	struct st_info stinfo = {1,2,3};

	int x = stinfo.ReturnAdd(stinfo.a,stinfo.b,stinfo.c);    // 注意此处也有改动;依旧下断点在这里

	printf("%d\n",x);

	system("pause");
    return 0;
}

        首先我们可以发现,我们对函数是没有任何改动的,只是把它封装到结构体里了,对于函数的参数和实现,是没有任何修改的。

        那么接下来依旧转到反汇编查看,如下:

【汇编 C++】C++this指针、this指针长什么样?_第2张图片

        红色框的内容就是this指针的样貌,很独特,但又不独特

        独特是因为他与其他参数不同,它是用寄存器来传参的

        不独特是因为他也就是将【ebp-14】(结构体stinfo对应的首地址)存入了寄存器ecx 

        那么如果我们不使用this指针,不使用任何参数,不返回任何内容的话,this指针还在吗?

        测试如下:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

    
    // 结构体的外部定义函数
    void ReturnAdd()        // 删除参数和返回地址
    {
        
    }

};


int main()
{
	struct st_info stinfo = {1,2,3};
    
    stinfo.ReturnAdd();    // 注意此处也有改动;依旧下断点在这里

	system("pause");
    return 0;
}

        运行一下,转到反汇编:

【汇编 C++】C++this指针、this指针长什么样?_第3张图片

        依旧存在。 

        简单总结

        1、this指针是编译器默认传入的,通常会使用ecx进行参数的传递。

        2、你用或者不用它,他都在。

二、this指针的使用

        测试程序demo01.cpp:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

    
    void initStruct()
    {
        this->a = 1;
        this->b = 2;
        this->c = 3;
    }

};


int main()
{
	struct st_info stinfo;
    stinfo.initStruct();
    
    printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);

	system("pause");
    return 0;
}

        像这样,this指针一般都是用与初始化结构体成员,当然还有很多用法,这里就不提了。

        但是,可能有人会问,这样好像变得更麻烦了,直接在定义结构体变量的时候初始化不就行了吗?那么this指针的到底有没有用呢?

        有的。

        this指针是否有用?

        当我们定义的结构体成员与函数的形参名字一样的时候,this指针的作用就来了。

        测试代码如下:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

    
    void initStruct(int a, int b, int c)
    {
        a = a;
        b = b;
        c = c;
    }

};


int main()
{
	struct st_info stinfo;
    stinfo.initStruct(1,2,3);    // 断点
    
    printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);

	system("pause");
    return 0;
}

        如果我们直接写a=a的话,结构体成员真的初始化了吗?

        运行如下:

【汇编 C++】C++this指针、this指针长什么样?_第4张图片

        发现结构体变量成员并没有初始化。

        下断点、转到反汇编查看:

【汇编 C++】C++this指针、this指针长什么样?_第5张图片

        传入三个值加一个指针

        单步步入F11进入函数内部:

【汇编 C++】C++this指针、this指针长什么样?_第6张图片 

         可以看到我们直接a = a在底层汇编是没有做任何有意义的事的。

        那么大家现在应该能想到,我们如何使用this进行成员初始化,大家可以自己试着做做。

        代码如下:

#include 
#include 

struct st_info
{
    int a;
    int b;
    int c;

    
    void initStruct(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

};


int main()
{
	struct st_info stinfo;        // 断点
    stinfo.initStruct(1,2,3);
    
    printf("%d %d %d\n",stinfo.a,stinfo.b,stinfo.c);

	system("pause");
    return 0;
}

        我们说过,this是指向结构体变量首地址的一个指针,那么this->a肯定指的是这个结构体变量的成员啊。

        所以通过this指针,我们就能区别成员变量与参数。

        运行如下:

【汇编 C++】C++this指针、this指针长什么样?_第7张图片

        断点、转到反汇编看一下: 

【汇编 C++】C++this指针、this指针长什么样?_第8张图片

        三个参数一个指针。

        进入函数内部:

        vs2010是这样的:

【汇编 C++】C++this指针、this指针长什么样?_第9张图片 

        可能不太清晰,掩盖了局部变量的地址。

        VC++6是这样的:

【汇编 C++】C++this指针、this指针长什么样?_第10张图片 

        分析:

        我们知道ecx里存放的是this指针,那么我们先做的是mov dword ptr[ebp-4],ecx

        第一行不用看,那个是提升堆栈的时候使用到了。 

        mov dword ptr[ebp-4],ecx:

        这一步将this指针放到了【ebp-4】(局部变量里)

        mov eax,dword ptr[ebp-4]:

        这一步将this指针取出,放到了eax寄存器里

        mov ecx,dword ptr[ebp+8]:

        我们上面说了,ebp+8是第一个参数,所以这里将1取出放到了ecx寄存器里

        mov dword ptr[eax],ecx:

        最后一步将ecx中的1取出,放到了eax(this指针)对应的地址里

        总结,上面几步就是将第一个参数1取出放到this指针指向的结构体变量首地址(this->a)中

        VC++6代码,继续往下看:

【汇编 C++】C++this指针、this指针长什么样?_第11张图片

        ebp-4:this指针

        ebp+c:第二个参数(2)

        大家可以自己试着分析下。

        总结:

        这三步是将第二个参数(2)取出放到this指针指向的结构体变量首地址+4(this->b)的位置上;并且将第三个参数(3)取出放到this指向的结构体变量首地址+8(this->c)的位置上

        通过以上测试证明了this指针还是有用的。

        this指针其他的特点

        1、this指针不能做++--运算,不能重新被赋值。

        2、this指针不占用结构体的宽度。

        先来验证观点二吧:

#include 
#include 

struct st_info
{
	int x;
	int y;
	int z;
};

int main()
{
	struct st_info stinfo;

	printf("%d \n",sizeof(stinfo));

	system("pause");
	return 0;
}

        运行:
【汇编 C++】C++this指针、this指针长什么样?_第12张图片

        加上this(函数):

 

#include 
#include 

struct st_info
{
	int x;
	int y;
	int z;
    void myprint()
    {
        return ;
    }
};

int main()
{
	struct st_info stinfo;

    stinfo.myprint();

	printf("%d \n",sizeof(stinfo));

	system("pause");
	return 0;
}

        运行:

【汇编 C++】C++this指针、this指针长什么样?_第13张图片

        观点2证明成功!

        观点1:

        【汇编 C++】C++this指针、this指针长什么样?_第14张图片 

        无法++;

        不要强转,强转也没有什么意义,如果想用指针偏移取值,大可以自己定义一个指针。

        那么this是否可以+1,+2呢? 

        可以,代码如下:

        

#include 
#include 

struct st_info
{
	int x;
	int y;
	int z;

	void init(int x, int y, int z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}
    void myprint()
    {
        printf("%d \n",this+1);
    }
};

int main()
{
	struct st_info stinfo;

	stinfo.init(1,2,3);

	stinfo.myprint();

	system("pause");
	return 0;
}

        编译是没有任何错误的。

【汇编 C++】C++this指针、this指针长什么样?_第15张图片

        那么我们是不是可以通过+1,+2来进行指针偏移取值呢?

        不建议!

        首先,我们不偏移取一下值:

【汇编 C++】C++this指针、this指针长什么样?_第16张图片 

        取到1,没有任何毛病

        可能有的人会想,那么偏移4个字节不就能取到2了吗?

【汇编 C++】C++this指针、this指针长什么样?_第17张图片 

         取到的是一个垃圾值,为什么呢?

        因为我们不知道this这个指针的类型啊,我们知道指针的偏移量除了二级及以上的指针,对于一级指针来说,偏移量是和类型字节大小挂钩的。我们这里根本不知道this指针的类型,怎么可能知道偏移量呢?那又怎么可能正确的取到值呢?

        可能有些人想到了百度,搜索this指针类型,然后计算。

        还是那句话,没有什么意义,如果想用指针偏移取值,大可以自己定义一个指针。

        this指针我们就平常的指向成员就行了,没必要搞些花里胡哨的。

        所以观点1:不能++,--是正确的;可以+1,-1但是不建议!

        如果真想靠偏移取值,我们大不了自己定义一个,因为这里我们的测试代码中,结构体的成员是int类型,那么我们就定义一个int类型嘛,如下:

#include 
#include 

struct st_info
{
	int x;
	int y;
	int z;

	void init(int x, int y, int z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}
    void myprint(int* usr)
	{
		printf("%d \n",*(usr+1));		// int*+1偏移四个字节
	}
};

int main()
{
	struct st_info stinfo;

	int* pst = (int*)&stinfo;			// 定义一个int类型指向结构体
										// 不要那么死板,int不一定非要指向int
	stinfo.init(1,2,3);

	stinfo.myprint(pst);				// 传入int指针

	system("pause");
	return 0;
}

        int指针不一定指向int,这里int强转还是有意义的,最起码比强转this有意义。

        运行如下:

【汇编 C++】C++this指针、this指针长什么样?_第18张图片

        我们通过自己定义指针进行偏移,成功取出了结构体变量的成员,与直接操作this指针相比,好太多了。

总结

this指针总结
1、this指针是编译器默认传入的,不需要手动传入,通常使用ecx进行传递this指针。
2、成员函数都有this指针,无论使用与否。
3、this指针不能做++ --运算,不能重新被赋值;但是可以做+1,-1运算(不建议)。
4、this指针不占用结构体的宽度。

结语

        如果有错误,请指出;如果有听不懂的地方,可以私信或者评论,我都会进行修改。

 

你可能感兴趣的:(汇编,C,C++,c++,安全)