Я делаю язык программирования. Я делал это несколько раз раньше, но на этот раз я хотел сделать это лучше и использовать больше техник при разработке. Одним из них является то, что я использую 2 определенных препроцессора, _DEBUG
(сделано мной, я использую _
так что это не перепутать с уже определенным Visual Studio, DEBUG) и _DEBUG_VARS
, Когда у меня есть _DEBUG = 1
, Я хочу сделать что-то с отладкой, и при выполнении _DEBUG_VARS = 1
Хочу делать вар вар и тому подобное. И один из них — шестнадцатеричная свалка. Могу поспорить, что в стандартной библиотеке уже есть одна, но я бы хотел свою собственную. То, как я хочу, чтобы это работало, я передаю указатель на любой класс (я сделал это, используя шаблон <class T_HEX>
, Так что тогда это T_HEX*
Я вставил в символ *, а затем получил размер T_HEX
Перебрать все байты из char*
вперед (помните 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 последним и int перед этим. Таким образом, 01 должен быть моим bool, потому что bool — это 1 байт, и он также установлен в true, затем перед этим я объявил int. int 32-битный или 4 байта, и здесь я вижу 01 04 00 00. тогда у нас есть символ, который находится перед int. Я всегда делал переменную и передавал указатель на нее. Теперь я хочу сделать символ, и передал указатель на символ. В этом случае первый символ — «А». Когда мы смотрим на вывод консоли, мы видим, что он показывает 41, вы можете спросить, почему это 41? ну это шестнадцатеричный, а 41 в шестнадцатеричном это 65 в десятичном, что является значением ASCII А.
Но теперь к моим вопросам.
Если мы посмотрим на тот, что перед значением char: 00 00. Почему их нет в первом выводе?
Если мы посмотрим в первом выводе и подумаем над вопросом 1, почему в этом случае не записан символ, в данном случае 41?
Теперь, когда его там нет 41, мы также можем видеть, что остальной части char * (string) тоже нет. Может быть, эти 18 5e 17 01 — указатель на строку char *, я прав?
Есть ли другой способ сделать немой хекс? Я хочу как пользовательский код, и, возможно, если есть, функция в стандартной библиотеке.
Спасибо
Похоже, что это испортило один вызов 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 00 является частью первой строки, здесь все в порядке.
- Если мы посмотрим в первом выводе и подумаем над вопросом 1, почему в этом случае не записан символ, в данном случае 41?
41 — первый символ строки, в то время как в структуре вы сохраняете указатель.
- Теперь, когда его там нет 41, мы также можем видеть, что остальной части char * (string) тоже нет. Может быть, эти 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) << " ";
}
Других решений пока нет …