我正在制作一种编程语言。我以前做过一些次,但这次我想做得更好,在开发它时使用更多的技术。其中之一是我使用了两个定义的预处理器,_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值
但现在我的问题。
如果我们看看char值之前的那个:00 00。为什么这些不在第一个输出中?
如果我们看第一个输出,想想问题1,为什么没有在那里写char,在本例中是41?
既然它不在41,我们还可以看到char*(字符串)的其余部分也不在。也许那18 5e 17 01是一个指向字符串的指针,对吗?
有没有其他方法可以做一个六角哑?我想要两个自定义代码,如果有的话,可能还想要标准库中的函数。
感谢
似乎把这件事搞砸了的是,对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
- 如果我们看看char值之前的那个:00 00。为什么这些不在第一个输出中
00是第一行的一部分,这里一切都好。
- 如果我们看第一个输出,想想问题1,为什么没有在那里写char,在本例中是41
41是该行的第一个字符,而在存储指针的结构中。
- 既然它不在41,我们还可以看到char*(字符串)的其余部分也不在。也许那18 5e 17 01是一个指向字符串的指针,对吗
是的,你是对的。
- 有没有其他方法可以做一个六角哑?我想要两个自定义代码,如果有的话,可能还想要标准库中的函数
您必须意识到,执行十六进制转储的任何方式都将跨越标准中定义的边界。您将不得不依赖一些实现定义的行为,并且在某种程度上,未定义的行为不会导致鼻恶魔。大多数编译器可能会在这里正常运行,以允许十六进制转储。
你可以做一些改进。首先,你可能应该使用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) << " ";
}