如何获取一个结构体数据的十六进制转储

38
 ....
 finalize(char *hdrs, sendip_data *headers[], int index,
                    sendip_data *data, sendip_data *pack)
 {

 ........

出于调试目的,我想要一个sendip_data类型的datapack结构的十六进制转储。实际上它们包含了一些二进制信息,因此我不确定我的项目输出是否正确。因此,为了调试目的,我想将数据写入文件,以便我可以像下面这样使用hexdump-

$hexdump -C file.txt

由于这是一个运行时生成的网络数据包,所以我也不确定datapack结构的长度,我认为fread / fwrite需要知道..因此,请给我一些建议。


1
不确定从哪里开始。sizeof(sendip_data)将给出该结构的大小,但您是否在说它包含指向其他数据的指针,您也希望它们成为输出的一部分? - Vaughn Cato
请参见https://dev59.com/T3VD5IYBdhLWcg3wR5ko。 - Kristopher Johnson
2个回答

146

以下代码将在您的代码内部为您提供任意内存的十六进制转储。

#include <stdio.h>

// Usage:
//     hexDump(desc, addr, len, perLine);
//         desc:    if non-NULL, printed as a description before hex dump.
//         addr:    the address to start dumping from.
//         len:     the number of bytes to dump.
//         perLine: number of bytes on each output line.

void hexDump (
    const char * desc,
    const void * addr,
    const int len,
    int perLine
) {
    // Silently ignore silly per-line values.

    if (perLine < 4 || perLine > 64) perLine = 16;

    int i;
    unsigned char buff[perLine+1];
    const unsigned char * pc = (const unsigned char *)addr;

    // Output description if given.

    if (desc != NULL) printf ("%s:\n", desc);

    // Length checks.

    if (len == 0) {
        printf("  ZERO LENGTH\n");
        return;
    }
    if (len < 0) {
        printf("  NEGATIVE LENGTH: %d\n", len);
        return;
    }

    // Process every byte in the data.

    for (i = 0; i < len; i++) {
        // Multiple of perLine means new or first line (with line offset).

        if ((i % perLine) == 0) {
            // Only print previous-line ASCII buffer for lines beyond first.

            if (i != 0) printf ("  %s\n", buff);

            // Output the offset of current line.

            printf ("  %04x ", i);
        }

        // Now the hex code for the specific character.

        printf (" %02x", pc[i]);

        // And buffer a printable ASCII character for later.

        if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
            buff[i % perLine] = '.';
        else
            buff[i % perLine] = pc[i];
        buff[(i % perLine) + 1] = '\0';
    }

    // Pad out last line if not exactly perLine characters.

    while ((i % perLine) != 0) {
        printf ("   ");
        i++;
    }

    // And print the final ASCII buffer.

    printf ("  %s\n", buff);
}

// Very simple test harness.

int main (int argc, char *argv[]) {
    char my_str[] = "a char string greater than 16 chars";
    hexDump ("my_str", &my_str, sizeof (my_str), 16);
    return 0;
}

你将描述、内存地址、长度以及每行要显示多少个字节传递给了hexDump

它会输出一个十六进制转储(包括字符数据)供检查。当你使用包含的main运行它时,输出为:

my_str:
  0000  61 20 63 68 61 72 20 73 74 72 69 6e 67 20 67 72  a char string gr
  0010  65 61 74 65 72 20 74 68 61 6e 20 31 36 20 63 68  eater than 16 ch
  0020  61 72 73 00                                      ars.

很好,真的很有帮助 :) - Dev.K.
将 if ((pc[i] < 0x20) || (pc[i] > 0x7e)) 替换为 if (!isprint(pc[i])) - weberjn
谢谢你,善良的灵魂。我喜欢你的函数,我会把它包含在我的创作中...你的思想现在在我身上生根发芽了... - daparic
主函数中的示例看起来不对。第二个参数是&my_str,应该是my_str - Frak
@frakman1,在这种情况下,没有区别。生成的类型不同(指向char而不是指向char数组),但它们在这里都被视为相同(在函数中强制转换为void*/char*)。我更喜欢&something形式,因为如果您有一个非数组类型,例如:int my_int; hexDump("my_int", &my_int, sizeof(my_int));,则需要它。有关更多详细信息,请参见https://dev59.com/4nE85IYBdhLWcg3w9odJ。 - paxdiablo
这里提供了带有ANSI颜色的C++版本 - https://gist.github.com/shreyasbharath/32a8092666303a916e24a81b18af146b - thegreendroid

8

一个十六进制转储工具适用于Android平台,同样也适用于其他平台。

LOGD()DLOG()相同,扮演了printf()的角色,因为在Android中printf()无法使用。对于其他平台,可以使用#define DLOG printf

dlog.h:

// Android logging
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "~~~~~~", __VA_ARGS__)
#define DLOG(...) __android_log_print(ANDROID_LOG_DEBUG  , "~~~~~~", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "~~~~~~", __VA_ARGS__)
#define ELOG(...) __android_log_print(ANDROID_LOG_ERROR  , "~~~~~~", __VA_ARGS__)

#ifdef __cplusplus
extern "C" {
#endif

void log_dump(const void*addr,int len,int linelen);
void log_dumpf(const char*fmt,const void*addr,int len,int linelen);

#ifdef __cplusplus
}
#endif

dump.cpp:

#include <dlog.h>
//#include <alloca.h>

inline char hdigit(int n){return "0123456789abcdef"[n&0xf];};

#define LEN_LIMIT 8
#define SUBSTITUTE_CHAR '`'

static const char* dumpline(char*dest, int linelen, const char*src, const char*srcend)
{
    if(src>=srcend) {
        return 0;
    }
    int i;
    unsigned long s = (unsigned long)src;
    for(i=0; i<8; i++) {
        dest[i] = hdigit(s>>(28-i*4));
    }
    dest[8] = ' ';
    dest += 9;
    for(i=0; i<linelen/4 ; i++) {
        if(src+i<srcend) {
            dest[i*3] = hdigit(src[i]>>4);
            dest[i*3+1] = hdigit(src[i]);
            dest[i*3+2] = ' ';
            dest[linelen/4*3+i] = src[i] >= ' ' && src[i] < 0x7f ? src[i] : SUBSTITUTE_CHAR;
        }else{
            dest[i*3] = dest[i*3+1] = dest[i*3+2] = dest[linelen/4*3+i] = ' ';
        }
    }
    return src+i;
}

void log_dumpf(const char*fmt,const void*addr,int len,int linelen)
{
#if LEN_LIMIT
    if(len>linelen*LEN_LIMIT) {
        len=linelen*LEN_LIMIT;
    }
#endif
    linelen *= 4;
    static char _buf[4096];
    char*buf = _buf;//(char*)alloca(linelen+1); // alloca() causes the initialization to fail!!!!
    buf[linelen]=0;
    const char*start = (char*)addr;
    const char*cur = start;
    const char*end = start+len;
    while(!!(cur = dumpline(buf,linelen,cur,start+len))){DLOG(fmt,buf);}
}

void log_dump(const void*addr,int len,int linelen)
{
    log_dumpf("%s\n",addr,len,linelen);
}

使用示例:

log_dumpf("args: %s\n", &p, 0x20, 0x10);

输出:

args: 61efadc4 00 3c 17 01 6d bc 59 61 02 00 00 00 80 ae ef 61 `<``m`Ya```````a
args: 61efadd4 00 3c 17 01 00 00 00 00 31 a5 59 61 80 ae ef 61 `<``````1`Ya```a

更新: 请查看reDroid(github)中的dump.cppre_dump.h,其中包括一个递归式转储程序,可以检查指针是否有效。


很高兴有更新,但是仓库里没有说明文件,链接的文件引用了其中的其他文件,这并没有提供太多便利。 - fuzzyTew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接