【问题标题】:Convert a void* to a C-Style string without sprintf or C library functions将 void* 转换为没有 sprintf 或 C 库函数的 C 样式字符串
【发布时间】:2019-12-20 01:27:28
【问题描述】:

我正在使用带有/NODEFAULTLIB 的msvc 编译程序,所以我不能使用任何C 库函数。如何创建一个 C 风格的字符串 (char*),其中包含像 printf("%p\r\n", voidPointer) 产生的指针 (void*) 的十六进制表示,所以我可以使用 WriteConsoleW 将其写入控制台?

WriteConsoleW 只接受一个指向缓冲区的指针,该缓冲区包含要输出的字符串,以及字符串中的字符数。如何在不使用 sprintf 等 C 运行时库函数的情况下进行转换?但是,我可以使用WinAPI 函数,例如lstrlenW

【问题讨论】:

  • 你想要十六进制吗?如果有,你知道算法吗?
  • @David Schwartz 是的,我需要十六进制。不幸的是,我不确定您所指的算法。
  • 所以您正在寻找指针值的字符串表示,就像您使用printf("%p", ptr) 得到的那样? (而不是将void* 视为指向字符串的指针?)您可能想在您的问题中澄清这一点。 “convert”这个词可能有歧义。
  • @KeithThompson 是的,你是对的。我已经编辑了我的帖子。
  • 与您的问题无关,但在写入标准输出时通常不需要\r\n。该实现会将单个 \n 字符转换为操作系统的行尾表示(\n 在类 Unix 系统上,\r\n 在 Windows 上)。与您的目的更不相关,但无论如何我都会提到它,十六进制是%p 最常见的格式,但标准没有指定指针如何表示为字符串。你特别想要十六进制,所以这不是问题。

标签: c string pointers winapi


【解决方案1】:

既然你说你可以使用 Win32 API,那么看看 API 提供的多个字符串格式化函数:

#include <windows.h>

WCHAR buf[25];
int len = wsprintfW(buf, L"%p\r\n", voidPointer);
DWORD written;
WriteConsole(hConsole, buf, len, &written, NULL);
#include <windows.h>
#include <strsafe.h>

WCHAR buf[25];
StringCbPrintfW(buf, sizeof(buf), L"%p\r\n", voidPointer);
DWORD written;
WriteConsole(hConsole, buf, lstrlenW(buf), &written, NULL);
#include <windows.h>
#include <strsafe.h>

WCHAR buf[25], *end;
StringCbPrintfExW(buf, sizeof(buf), &end, NULL, 0, L"%p\r\n", voidPointer);
DWORD written;
WriteConsole(hConsole, buf, end-buf, &written, NULL);
#include <windows.h>
#include <strsafe.h>

WCHAR buf[25];
StringCchPrintfW(buf, sizeof(buf)/sizeof(WCHAR), L"%p\r\n", voidPointer);
DWORD written;
WriteConsole(hConsole, buf, lstrlenW(buf), &written, NULL);

【讨论】:

【解决方案2】:
#include <stdio.h>
#include <stdint.h>

void hexDump(void *ptr, char *buf)
{
    static char hex[16] = {
        '0', '1', '2', '3', '4',
        '5', '6', '7', '8', '9',
        'A', 'B', 'C', 'D', 'E', 'F' };
    *buf++ = '0';
    *buf++ = 'x';

    uintptr_t ip = (uintptr_t) ptr;
    for (int nibble = (2 * sizeof(ptr) - 1); nibble >= 0; --nibble)
        *buf++=hex[(ip >> (4 * nibble)) & 0xf];

    *buf = 0;
    return;        
}

int main()
{
    void *ptr = (void *) 0x1234abcd567890ef;
    char buf[20];
    hexDump(ptr, buf);
    printf("\"%s\"\n", buf);
}

输出:

“0x1234ABCD567890EF”

这就是我可能会如何编写真正的内部代码:

    // To extract the first nibble, we shift right 4 bits less
    // than the size of the pointer. Each subsequent nibble requires
    // Shifting four fewer bits. The last output requires no shift
    for (int shift = sizeof(ptr) * 8 - 4; shift >= 0; shift -= 4)
        *buf++ = hex[(((uintptr_t) ptr) >> shift) & 0xf];

【讨论】:

  • +1,但我认为 char buf[16] 对于 x64 或 char buf[11] 对于 x86 至少应该是 char buf[11],而 for 循环中的 nibble &gt; 1 应该是 nibble &gt;= 0
  • @DrakeWu-MSFT 谢谢。固定。
【解决方案3】:

您可以在 Win32 库中找到类似 printf 的支持,而无需转到 C 运行时。只需在 NTDLL.DLLdepends.exe 中查看,我就看到了相当不错的 CRT 式函数选择。

参考:Is there a wsprintf()-type function from a low-level library such as kernel32.dll or ntdll.dll?

如果它已经加载到您的地址空间,则无需编写您自己的 :-)

【讨论】:

  • 那些是不受支持的私有实现。它们的行为可能与 CRT 对应物相似,也可能不同,尤其是在错误处理和报告方面。这个Q&A 提供了更多信息,包括您在删除编译器的支持库时必须考虑的其他事项。
猜你喜欢
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 2014-12-28
相关资源
最近更新 更多