Jump to content

Pop quiz...


Valik
 Share

Recommended Posts

I got bored and did an experiment, this is the result. Does anybody else know why this works (This is obviously C++)? Should this win an award for obfuscation (Not my intent, but its pretty hard to read)? Does anybody besides me even know what the hell this does...?

#include <iostream>

class CTest
{
public:
    int m_n;
    CTest(int _n) : m_n(_n) { }
    virtual int method1() { std::cout<<"method1"<<std::endl;    return 1; }
    virtual int method2() { std::cout<<"method2"<<std::endl;    return 2; }
};

int main()
{
    CTest t(4);
    int **array = (int**)&t;

    std::cout<<"Ret: "<<(t.*(*(int (CTest::**)(void))(*array)))()<<std::endl;
    std::cout<<"Ret: "<<(t.*(*(int (CTest::**)(void))(++(*array))))()<<std::endl;
    std::cout<<"Val: "<<*(int*)++array<<std::endl;

    std::cout<<"\nPress {ENTER} to continue"<<std::endl;
    std::cin.get();
    return 0;
}

Edit: I neglected to mention that this was done using VS 7.1 (.NET 2003). The results should be the same on any of Microsoft's compilers, the results may or may not be the same on other compilers (It could even be different between versions of VS, but I doubt it).

Edited by Valik
Link to comment
Share on other sites

  • Administrators

I got bored and did an experiment, this is the result.  Does anybody else know why this works (This is obviously C++)?  Should this win an award for obfuscation (Not my intent, but its pretty hard to read)?  Does anybody besides me even know what the hell this does...?

No idea, mate. :)
Link to comment
Share on other sites

method1
Ret: 1
method2
Ret: 2
Val: 4

Press {ENTER} to continue

The above is output from VC++ 6. Let's not tell the others how the code works.

May I use your code tne next time I have to write a C++ coding standard? It will go in the 'don't do this' section. :)

Ignorance is strength.

Link to comment
Share on other sites

I expected Microsoft compilers would produce the same results.

Jon (and others), when an object is layed out in memory (under MSVC), if the class contains virtual members, then the very first block of memory will be an array of pointers to the functions, otherwise known as the vtable. After that 4 byte block, the data members are stored. So the first two std::cout lines are accessing the vtable directly through the array of pointers. Instead of specifying the name to call a member through pointer (As AutoIt does internally), I'm skipping that step and just giving the address. Since the vtable is a pointer to an array of pointers, in the second line, I tell it to increment ot the second element in the array and then use that address to call the second function. The 3rd line prints the value in the data member (m_n). Since the members are layed out after the vtable, skipping over the vtable puts me to the first member.

And, as Henrik said, this should not be done. Specifically because this is compiler dependant. This is the way Microsoft lays out an object in memory. Another compiler will likely be different and the code will break. This was just a fun experiment for me to see if I really did understand how an object was layed out in memory.

However, in doing this, I thought up something applicable to AutoIt (Although it relates nothing to this code). Jon, the goal for doing function lookups is to use a binary search correct (Can't remember if this is in there yet or not...)? Well, what about taking that one step further. Why not cache functions in a second binary tree which is searched first? The benefit to this would be that in a long running script, all the functions could get cached and AutoIt would have to do even less work to find them (Again, I don't think this is implemented, but my memory is shoddy at times...)

Link to comment
Share on other sites

  • Administrators

I expected Microsoft compilers would produce the same results.

Jon (and others), when an object is layed out in memory (under MSVC), if the class contains virtual members, then the very first block of memory will be an array of pointers to the functions, otherwise known as the vtable.  After that 4 byte block, the data members are stored.  So the first two std::cout lines are accessing the vtable directly through the array of pointers.  Instead of specifying the name to call a member through pointer (As AutoIt does internally), I'm skipping that step and just giving the address.  Since the vtable is a pointer to an array of pointers, in the second line, I tell it to increment ot the second element in the array and then use that address to call the second function.  The 3rd line prints the value in the data member (m_n).  Since the members are layed out after the vtable, skipping over the vtable puts me to the first member.

And, as Henrik said, this should not be done.  Specifically because this is compiler dependant.  This is the way Microsoft lays out an object in memory.  Another compiler will likely be different and the code will break.  This was just a fun experiment for me to see if I really did understand how an object was layed out in memory.

However, in doing this, I thought up something applicable to AutoIt (Although it relates nothing to this code).  Jon, the goal for doing function lookups is to use a binary search correct (Can't remember if this is in there yet or not...)?  Well, what about taking that one step further.  Why not cache functions in a second binary tree which is searched first?  The benefit to this would be that in a long running script, all the functions could get cached and AutoIt would have to do even less work to find them (Again, I don't think this is implemented, but my memory is shoddy at times...)

Binary searches are currently in for builtin and user functions. Even for the 300 built in functions you only do about 7 string compares - it's lightning fast. :)
Link to comment
Share on other sites

I converted a binary search function I found.

Func BinarySearch($Arr, $target)
  ;Original Source: http://www.dcs.warwick.ac.uk/~zabin/dis.html
   Dim $low = 0
   Dim $high = UBound($Arr) - 1
   Dim $mid
   While $low <= $high
      $mid = ($low + $high) / 2
      $mid = Int($mid)
      If $target = $Arr[$mid] Then
        ;//match
         Return $mid
      ElseIf $target < $Arr[$mid] Then
        ;//search low end of array
         $high = $mid - 1;
      Else
        ;//search high end of array
         $low = $mid + 1;
      EndIf
   WEnd
   Return $mid
EndFunc
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...