将类指针转换为字符指针(不安全)



我正在制作一种编程语言。我以前做过一些次,但这次我想做得更好,在开发它时使用更多的技术。其中之一是我使用了两个定义的预处理器,_DEBUG(我自己制作,我使用_,这样它就不会与Visual Studio、DEBUG已经定义的混在一起)和_DEBUG_VARS。当我有_DEBUG = 1时,我想做一些调试工作,当我做_DEBUG_VARS = 1时,我想要做Var转储等。其中一个是十六进制转储。我打赌标准库里已经有了,但我想要我自己的。我希望它的工作方式是,我传递一个指向任何类的指针(我使用模板<class T_HEX>使其工作。因此,它会将我放入的T_HEX*转换为char*,然后获得T_HEX的大小,从char*开始循环所有字节(记住char*是RAM中char所在的位置)。然后我将用2个十六进制数字写出这个字节。我知道这真的很不安全,我编码的方式是,当我执行_DEBUG_VARS = 1时,它会创建这些函数,而当我执行_DEBUG_VARS = 0时,这些函数会被空的定义所取代,如果使用了空的定义,在编译时将被什么都不取代。因为它不安全,我只会在开发过程中使用它。发布版本不会有这个。

所以,对于代码来说。为了尝试这一点,我制作了一个名为Test:的课程

class Test
{
public:
    char* Allapoto = "AAaaAAaA";
    int Banana = 1025;
    bool Apple = true;
};

注意,我这里没有函数,这是因为我希望在使HexDump工作时它简单。然后HexDump自行运行:

int DEBUG_HexLineCounter = 0;
#define H_D_CUT 10
#define H_D_L() (DEBUG_HexLineCounter % H_D_CUT > 0)
#define H_D_NL() ((++DEBUG_HexLineCounter % H_D_CUT) == 0)
template <class T_HEX>
void HexDump(T_HEX* var)
{
    char* ptr = reinterpret_cast<char*>(var);
    char* off_ptr = NULL;
    int size = sizeof(*var);
    for (int i = 0; i < size; i++)
    {
        off_ptr = (ptr + i);
        char c = *off_ptr;
        HexDump(&c);
    }
    if (H_D_L())
        std::cout << std::endl;
}
void HexDump(char* c)
{
    char _char = *c;
    char ch = _char / 0x10;
    char cl = _char % 0x10;
    std::cout << std::hex << static_cast<int>(ch) << std::hex << static_cast<int>(cl) << " ";
    if (H_D_NL())
        std::cout << std::endl;
}

我省略了_DEBUG和_DEBUG_VARS的部分,因为我知道它们有效,所以它们在这里并不重要。我运行这个,我想从整个类中的值中获得以字节为单位的十六进制值。我使用以下代码在程序的主要功能(这是一个Win32控制台应用程序)中运行:

Test test;
HexDump<Test>(&test);

这导致了输出:

18 5e 17 01 01 04 00 00 01 00

对我来说,这并没有我想要的那么清楚。所以我添加了这个代码:

HexDump<char>(test.Allapoto);
HexDump<int>(&test.Banana);
HexDump<bool>(&test.Apple);

所以它现在看起来像这样:

Test test;
HexDump<Test>(&test);
HexDump<char>(test.Allapoto);
HexDump<int>(&test.Banana);
HexDump<bool>(&test.Apple);

我确实运行了这个,这次我得到了更有趣的东西:

18 5e 17 01 01 04 00 00 01 00
00 00
41
01 04 00 00
01

所以在这之后,我想,嗯,一些熟悉的数字,还有一些我以前见过的。第一件事01 04 00 00和一个01,我以前在更大的测试中见过它们(第一次测试)。如果我查看类结构,我会发现我在最后加了一个bool,在那之前加了一个子int。所以01必须是我的bool,因为bool是1个字节,它也被设置为true,然后在那之前,我声明了一个int。一个int是32位或4个字节,在这里我看到01 04 00 00。然后我们有一个char,它在int之前。我一直做这个变量,并传递一个指向它的指针。在这种情况下,第一个字符是"A"。当我们看到控制台输出时,我们可以看到它显示41,你可能会问为什么它是41?它是十六进制的,十六进制中的41是十进制中的65,这是A.的ascii值

但现在我的问题。

  1. 如果我们看看char值之前的那个:00 00。为什么这些不在第一个输出中?

  2. 如果我们看第一个输出,想想问题1,为什么没有在那里写char,在本例中是41?

  3. 既然它不在41,我们还可以看到char*(字符串)的其余部分也不在。也许那18 5e 17 01是一个指向字符串的指针,对吗?

  4. 有没有其他方法可以做一个六角哑?我想要两个自定义代码,如果有的话,可能还想要标准库中的函数。

感谢

似乎把这件事搞砸了的是,对HexDump的一次调用可能会导致多行。如果您将逻辑更改为始终在通用HexDump的末尾输出换行符,而从不在专用HexDump中输出,则每次调用HexDump之前都会有一行。

这可能会澄清你的一些问题。

没有这些修改,我得到的输出:

--- &test:
6f 10 40 00 00 00 00 00 01 04 
00 00 01 00 00 00 
--- test.Allapoto:
41 
--- &test.Banana:
01 04 00 
00 
--- &test.Apple:
01 

通过我修改的断线处理,我得到:

--- &test:
6f 10 40 00 00 00 00 00 01 04 00 00 01 00 00 00 
--- test.Allapoto:
41 
--- &test.Banana:
01 04 00 00 
--- &test.Apple:
01 
  1. 如果我们看看char值之前的那个:00 00。为什么这些不在第一个输出中

00是第一行的一部分,这里一切都好。

  1. 如果我们看第一个输出,想想问题1,为什么没有在那里写char,在本例中是41

41是该行的第一个字符,而在存储指针的结构中。

  1. 既然它不在41,我们还可以看到char*(字符串)的其余部分也不在。也许那18 5e 17 01是一个指向字符串的指针,对吗

是的,你是对的。

  1. 有没有其他方法可以做一个六角哑?我想要两个自定义代码,如果有的话,可能还想要标准库中的函数

您必须意识到,执行十六进制转储的任何方式都将跨越标准中定义的边界。您将不得不依赖一些实现定义的行为,并且在某种程度上,未定义的行为不会导致鼻恶魔。大多数编译器可能会在这里正常运行,以允许十六进制转储。

你可以做一些改进。首先,你可能应该使用unsigned char而不是char,以确保在将字节转换为十六进制时不会得到符号扩展。

其次,您应该改进新行逻辑。您可能应该将其限制在通用HexDump函数中,并使计数器成为局部变量。例如:

template <class T_HEX>
void HexDump(T_HEX* var)
{
    unsigned char* ptr = reinterpret_cast<unsigned char*>(var);
    unsigned char* off_ptr = NULL;
    int size = sizeof(*var);
    for (int i = 0; i < size; i++)
    {
        off_ptr = (ptr + i);
        unsigned char c = *off_ptr;
        if( i && i%8 == 0 )
            std::cout << std::endl;
        HexDump(&c);
    }
    std::cout << std::endl;
}
void HexDump(unsigned char* c)
{
    unsigned char _char = *c;
    unsigned char ch = _char / 0x10;
    unsigned char cl = _char % 0x10;
    std::cout << std::hex << static_cast<int>(ch) << std::hex <<     static_cast<int>(cl) << " ";
}

最新更新