如何仅使用 C 中的 write() 函数输出字符的十六进制?

How can I output hex of characters using only write() function in C?

是否有可能将“%X”放入 write() 函数中以获得十六进制数的 ascii 字符?

如 printf("十六进制数 .. : %X").

提前致谢!

Are there any possibilities to put "%X" in write() function to get hex number of ascii characters?

没有。 %Xprintf 系列 格式化 输出函数的一个特定特征。 ('f' 是“已格式化”的助记符。)write() 执行原始输出——正是您指定的字节。如果你想使用 write() 那么你将不得不自己执行格式化,我认为这是作业的很大一部分。

您需要自己编写函数。

char toHexDigit(int x)
{
    char result;
    if(x > 15) x = 0;
    if((x >= 0 && x < 10)) result = '0' + x;
    else result = 'A' + x - 10;

    return result;
}

char *tohex(unsigned x)
{
    static char buff[sizeof(x)*2];

    for(ssize_t i = 0; i < sizeof(x) * 2; i++)
    {
        int shift = (sizeof(x) * 2 - i - 1) * 4;
        buff[i] = toHexDigit((x & (0xf << shift)) >> shift);
    }
    return buff;
}

int main(void)
{
    write(1, tohex(0xD2A45678), sizeof(unsigned)*2);
}

https://godbolt.org/z/zveYazchj

简答

由于 write 不采用格式字符串和其他参数,您必须使用 printf,或者实现您自己的函数,该函数采用整数并将其转换为十六进制表示形式。

下面我将介绍如何编写您自己的 %X 类输出及其工作原理。

将字符转换为十六进制字符

您可以将整数(以及字符的十六进制代码)转换为十六进制字符串,方法是将数字除以 16 并重复取模。

例如,让我们考虑 'A' 的值,十六进制为 41。该数字除以 16 的余数等于其最后一位的值 -- 1。然后,如果我们将值 0x41 除以 16,我们将得到没有最后一位的相同数字 -- 0x4.

可以想到这样迭代的一个步骤:

int valueToConvert = 'A';
int lastHexDigit = valueToConvert % 16; // get the value of the last hex digit
valueToConvert = valueToConvert / 16;   // save the remainder of the number

这让我们得到了最后一个十六进制数字的值(作为整数)。

现在我们要将这个数字的整数值转换为char,即将值0转换为'0'。由于 ASCII 中的字符是连续存储的,我们可以通过将 '0' 添加到我们要转换的整数值来获得数字的值。

char charDigit = '0' + digitValue;

上面的代码片段仅适用于 digitValue 小于或等于 9。对于数字 AF 我们需要添加一个条件,如果 digitValue 大于 9 我们将(使用相同的逻辑)减去 10 来自 digitValue,然后向其添加 'A'。由于字母字符在 ASCII 码中也是连续的,我们可以自由地使用 digitValue-10 作为 'A' 字符的偏移量。

char charDigit;
if (digitValue < 10) {
  charDigit = '0' + digitValue;
}
else {
  charDigit = 'A' + (digitValue-10);
}

所以如果 digitValue 是 0xA (10),那么 digitValue-10 就是 0,并且将 0 添加到 'A' 只会让我们得到 'A'.

将整数转换为十六进制字符串表示形式

除以 16 取模,我们得到最低有效(最右边)的十六进制数字。但是打印字符串是从左到右完成的,而不是从右到左。

因此,为了打印字符串表示形式,我们需要某种缓冲区来存储所有最右边的字符。请记住这一点——没有 int 值会有超过 8 个十六进制数字,因此我们可以自由分配一个 8 个字符的数组。

char digits[8];
int digitIndex = 8;

中的digitIndex会一直记录我们写入的是哪个array slot,注意这里设置的是8,而不是7,这是有原因的我后面会解释.

现在让我们放回之前的代码——我们将使用 do/while 来“将最后一位数字放入数组的最后一个位置,直到余数为零”。

int valueToConvert = ...; // leaving the declaration here for clarity

do {
  // Get the value of the last digit
  int lastDigit = valueToConvert % 16;

  // Convert the last digit to char
  char charDigit;
  if (lastDigit < 10) {
    charDigit = '0' + lastDigit;
  }
  else {
    charDigit = 'A' + (lastDigit-10);
  }

  // Put the last digit into the array
  digitIndex -= 1;
  digits[digitIndex] = charDigit;

  valueToConvert /= 16;
} while(valueToConvert != 0);

假设 valueToConvert 不是负数,这应该会产生对应于给定 int 的十六进制值。

现在让我解释一下为什么我要先减去索引:我们将使用 digits+digitIndex 的值(这是一个 char *)作为我们数字数组的起点。如果我先减去,然后把数字放在那个地方,那保证当循环退出时,digitIndex结束第一个数字,这很方便。

现在让我们得到数组的长度:

unsigned count = 8 - digitIndex;

这就是我们缓冲区的大小减去数组中数字第一位的索引。

终于

将值转换为我们的数字数组后,我们只需调用 write:

write(handle, digits+digitIndex, count);

全图:

void write_hex_of_int(int handle, int number)
{
  char digits[8];
  int digitIndex = 8;

  // Fill the `digits` array with the digits of the number
  int valueToConvert = number;
  do {
    // Get the value of the last digit
    int lastDigit = valueToConvert % 16;

    // Convert the last digit to char
    char charDigit;
    if (lastDigit < 10) {
      charDigit = '0' + lastDigit;
    }
    else {
      charDigit = 'A' + (lastDigit-10);
    }

    // Put the last digit into the array
    digitIndex -= 1;
    digits[digitIndex] = charDigit;

    valueToConvert /= 16;
  } while(valueToConvert != 0);

  // Get the actual width of the number and print it.
  unsigned count = 8 - digitIndex;
  write(handle, digits+digitIndex, count);
}