我正在编写一门编程语言。我之前已经做了好几次了,但是这次我想更好地开发它并在开发时使用更多tecniques。其中之一就是我使用了2个已定义的预处理器_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\0";
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 last和一个int。因此01必须是我的bool,因为bool是1个字节并且它也设置为true,然后在此之前,我声明了一个int。一个int是32位或4个字节,在这里我看到01 04 00 00.然后我们有了char,它在int之前。我总是完成变量并传入一个指向它的指针。现在我想做一个char,然后传入一个char指针。在这种情况下,第一个字符是&#39; A&#39;。当我们查看控制台输出时,我们可以看到它显示41,你可能会问为什么它是41?以及它的十六进制,十六进制中的41是十进制的65,这是A的ascii值。
但现在问我的问题。
如果我们看一下char值之前的那个:00 00。为什么不是第一个输出中的那些?
如果我们查看第一个输出,并想到问题1,为什么不写在那里的字符,在这种情况下是41?
现在它不是41,我们也可以看到其余的char *(字符串)也不存在。也许那些18 5e 17 01是指向char *字符串的指针,我是对的吗?
还有另一种做十六进制哑的方法吗?我想要两个自定义代码,如果有的话,可能需要标准库中的一个函数。
由于
答案 0 :(得分:1)
似乎搞得一团糟的是,对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
<00> 00 00是第一行的一部分,一切都可以。
- 如果我们查看char值之前的那个:00 00。为什么不是第一个输出中的那个?
醇>
- 如果我们查看第一个输出,并想到问题1,为什么不写在那里的字符,在这种情况下41?
醇>
41是该行的第一个char,而在结构中存储指针。
- 现在它不是41,我们也可以看到其余的char *(字符串)也不存在。也许那些18 5e 17 01是指向char *字符串的指针,我是对的吗?
醇>
是的,你是对的。
- 还有另一种方法可以做一个十六进制的哑巴吗?我想要两个自定义代码,如果有的话,可能需要标准库中的一个函数。
醇>
您必须意识到,执行十六进制转储的任何方式都将跨越标准中定义的边界。您将不得不依赖某些实现定义的行为,并且在某种程度上,未定义的行为不会导致鼻子恶魔。大多数编译器在这里可能会正常运行以允许十六进制转储。
您可以做一些改进。首先,您应该使用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) << " ";
}