【问题标题】:Recoding printf %p with write function用 write 函数重新编码 printf %p
【发布时间】:2021-07-09 10:21:05
【问题描述】:

我目前正在处理一项需要打印变量地址的任务。使用 printf %p 很容易,但我只能使用 unistd 中的 write。

我尝试将指针转换为无符号整数和 uintptr_t,然后将其转换为十六进制数。使用 uintptr_t 它可以工作,但使用无符号整数它只打印地址的一半。也许有人可以解释一下为什么会这样?

我还看到了一些使用“>>”和“>”的解决方案,那就太好了,因为我不确定是否允许我使用 uintptr_t。

这是我用来将其转换为 unsigned int / unitptr_t / unsigned long long 的代码(我知道 ft_rec_hex 缺少前导 0):

void ft_rec_hex(unsigned long long nbr)
{
    char tmp;

    if (nbr != 0)
    {
        ft_rec_hex(nbr / 16);
        if (nbr % 16 < 10)
            tmp = nbr % 16 + '0';
        else
            tmp = (nbr % 16) - 10 + 'a';
        write(1, &tmp, 1);
    }
}

int     main(void)
{
    char c = 'd';
    unsigned  long long ui = (unsigned long long)&c;
    ft_rec_hex(ui);
}

【问题讨论】:

  • 因为你使用的“无符号整数”只有uintptr_t的一半大小? (uintptr_t 是一个无符号整数,所以看起来很废话)
  • jsiller,“不确定我是否可以使用 uintptr_t。” --> uintptr_t 是可选类型。替代方案:使用最广泛的可用:uintmax_t, unsigned long long, ...
  • "我尝试将指针转换为无符号整数和 uintptr_t,然后将其转换为十六进制数。" --> 最好发布该代码。
  • unsigned long long 的输出与 unsigned int 相同
  • 没关系,我 2 天前试过这个,显然做错了,因为长时间工作

标签: c pointers printf write


【解决方案1】:

看起来只打印了一半的地址,因为您使用的“无符号整数”只有uintptr_t 的一半大小。 (注意uintptr_t是无符号整数类型)

您可以使用unsigned char 的数组将数据存储在指针变量中并打印它以打印带有uintptr_t 的完整指针。

根据strict aliasing rule允许使用字符类型读取其他类型的对象。

#include <stdio.h>
#include <unistd.h>

void printOne(unsigned char v) {
    const char* chars = "0123456789ABCDEF";
    char data[2];
    data[0] = chars[(v >> 4) & 0xf];
    data[1] = chars[v & 0xf];
    write(1, data, 2);
}

int main(void) {
    int a;
    int* p = &a;
    /* to make sure the value is correct */
    printf("p = %p\n", (void*)p);
    fflush(stdout);

    unsigned char ptrData[sizeof(int*)];
    for(size_t i = 0; i < sizeof(int*); i++) {
        ptrData[i] = ((unsigned char*)&p)[i];
    }
    /* print in reversed order, assuming little endian */
    for (size_t i = sizeof(int*); i > 0; i--) {
        printOne(ptrData[i - 1]);
    }

    return 0;
}

或者将指针变量中的数据读取为unsigned char数组而不复制:

#include <stdio.h>
#include <unistd.h>

void printOne(unsigned char v) {
    const char* chars = "0123456789ABCDEF";
    char data[2];
    data[0] = chars[(v >> 4) & 0xf];
    data[1] = chars[v & 0xf];
    write(1, data, 2);
}

int main(void) {
    int a;
    int* p = &a;
    /* to make sure the value is correct */
    printf("p = %p\n", (void*)p);
    fflush(stdout);

    /* print in reversed order, assuming little endian */
    for (size_t i = sizeof(int*); i > 0; i--) {
        printOne(((unsigned char*)&p)[i - 1]);
    }

    return 0;
}

【讨论】:

  • 好的,我试着理解这一点,但很困惑:D。在第二个解决方案中,您将指向指针 p 的指针转换为无符号字符 *。 (像这样:“unsigned char d = (unsigned char)&p;”)。那究竟是什么? f.e. 的价值是多少? d[0] 或 d[10] ?
  • 它使d指向分配给p的内存,从而可以将p对应的数据作为unsigned char的数组查看。 d[0] 将是指针的最小字节(如果它是小端序),d[10] 将超出范围(如果指针的大小小于 11 个字节)。
【解决方案2】:

使用 printf %p 很容易,但我只能使用 unistd 中的 write。

然后形成一个字符串并打印出来。

int n = snprintf(NULL, 0, "%p", (void *) p);
char buf[n+1];
snprintf(buf, sizeof buf, "%p", (void *) p);
write(1, buf, n);

使用转换为整数的指针会略微降低可移植性,并且肯定不会形成指针的最佳文本表示 - 依赖于实现。


使用 uintptr_t 它可以工作,但使用无符号整数它只打印地址的一半。

unsigned 未指定足够宽以包含指针中的所有信息。

uintptr_t,如果可用(非常常见),可以为void 指针保留大部分信息。足以往返到一个 等效 指针,即使是另一种形式。

【讨论】:

  • 我只允许使用write,不允许使用其他功能。
  • @jsiller 没有main() 很难编写程序。祝你好运。
  • 我实际上不应该写一个主要的。我只使用 main 来测试它,但该任务只要求一个仅使用 write 打印内存地址的函数。我可以自己编写其他函数并使用它们,但是编写 snprintf 不会那么容易编写。
猜你喜欢
  • 2017-06-26
  • 2013-01-16
  • 1970-01-01
  • 2016-05-01
  • 2011-06-19
  • 2021-05-16
  • 2014-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多