【问题标题】:How to get and store binary values for operations?如何获取和存储操作的二进制值?
【发布时间】:2019-07-31 17:23:35
【问题描述】:

我正在尝试将字符数组转换为二进制。我已经花了一个小时阅读 SO 帖子,但很少有人能完全满足我的需要,而且没有一个对我有用。

void formatCallSign(unsigned char *callsign) {
   unsigned char new[7];
   printf("original is %s",callsign);
   for (int j = 0; j < 6; j++) {
      printf("Hex of this is %x", callsign[j]); // this correctly prints out the original hex value
      char binary[9];
      binary[0] = ( (callsign[j] & (1 << 7)) ? '1' : '0' );
      binary[1] = ( (callsign[j] & (1 << 6)) ? '1' : '0' );
      binary[2] = ( (callsign[j] & (1 << 5)) ? '1' : '0' );
      binary[3] = ( (callsign[j] & (1 << 4)) ? '1' : '0' );
      binary[4] = ( (callsign[j] & (1 << 3)) ? '1' : '0' );
      binary[5] = ( (callsign[j] & (1 << 2)) ? '1' : '0' );
      binary[6] = ( (callsign[j] & (1 << 1)) ? '1' : '0' );
      binary[7] = ( (callsign[j] & (1 << 0)) ? '1' : '0' );
      printf("Binary of %c is: %x\r\n", callsign[j], binary); // print hex representation of character
   }
   }

本质上,这个函数的输入是一个 7 个字符的字符串(char 数组)。我也使用了嵌套的for循环,但是编译器生成了没有意义的无限循环错误,所以我这样重写了。

然后我需要获取该字符的二进制表示,然后按 1 向左移位(并非总是如此,但在 95% 的情况下,我可以稍后添加逻辑)。移位后,我需要十六进制值。

获得二进制值已被证明是难以捉摸的。在之前的项目中,我永远无法正确完成,只能获得十六进制值。在这种情况下,我也可以在第二个 printf 语句中获取 hax 值,但是我不能对 hex 值进行按位移位,因为我需要移动各个位。

然而,当我运行程序时,它总是显示Binary of [char] is: 9A。总是 9A,这不是十六进制代表。我传递给它的字符串中的任何个字符。

我的想法是使用sprintf 并传入数组并使用%x 来获取它的十六进制表示。现在,它打印到控制台,但我实际上会将它存储在新数组中,该数组现在在这个 sn-p 中未使用。有可能以某种方式做到这一点吗?获取二进制文件的代码似乎不起作用(基于this answer 的第二部分)。

(澄清一下,我在这个 sn-p 中的第二个和第三个 printf 语句应该输出相同的东西。只有第二个有效。这样画出来的原因是我可以进行按位运算,然后使用 sprintf 将其放回连贯的单个值并从中获取十六进制。)

许多问题都解决了这个主题,但很少有帮助,因为我对将二进制文件打印到控制台不感兴趣。相反,我需要将二进制文件存储在一个变量中,对其进行按位运算,然后获得 final 二进制文件的十六进制表示。现在,我可以成功获得 original 字符的十六进制表示,但这没有帮助。

我唯一的另一个想法是manually define the binary values of every hex number,然后将其用作“查找表”,但这似乎效率低下。

要对此进行测试,所需的所有其他内容都在 main 中:

unsigned char dest[8]   = "CQ     ";
formatCallSign(&dest);

【问题讨论】:

  • 最后一个printf 语句是错误的。您不应该为 %x 格式规范传递数组 (binary)。要查看数组中的内容,请分别打印每个数组元素。 (因为它是char的数组,你可以滥用C语义并用%s打印它。首先,将最后一个元素设置为0(binary[8] = 0;),然后像现在一样将它传递给printf,但将%x 更改为%s。另外,删除\r。)
  • 以后,当您询问代码有什么问题时,请包含minimal reproducible example,以便其他人可以轻松编译和使用您的代码。
  • 在编译器中打开警告。它应该警告你 binary%x 的错误参数。
  • @EricPostpischil 我想我关注了,现在正在修改。但是为什么要删除\r?否则有新行但没有回车,导致输出混乱。回车需要\r
  • @EricPostpischil 我现在也处于类似的困境中,而不是永久的9A,而是永久的a3。您说您不能将数组传递给 sprintf - 那么您如何构建一个字符串(不可避免地是一个 char[])然后将其传递进去?我跟着这个答案:stackoverflow.com/a/2674333

标签: c binary


【解决方案1】:

这里有两个问题。我将首先介绍简单、肤浅的内容。您的代码几乎正确地将字符转换为其二进制表示。它将该表示创建为字符串。 (这可能不是您想要的;稍后会详细介绍。)但随后它会尝试使用 printf 的 %x 格式打印字符串。现在,%x 用于打印 数字,而不是字符串,并且由于 C 处理数组和指针的方式有时令人惊讶,如果您有一个数组 char binary[9] 并尝试使用 @ 打印它987654324@,你得到的是数组的地址,而不是数组的内容。我想这就是为什么你每次都得到相同的价值。

您还忘记对构造的binary 字符串进行空终止。

这是您的 formatCallSign 函数的更正版本。 (我同时清理了其他一些东西。)

void formatCallSign(unsigned char *callsign) {
   printf("original is %s\n",callsign);
   for (int j = 0; j < strlen(callsign); j++) {
      printf("Hex of %c is %x\n", callsign[j], callsign[j]);
      char binary[9];
      binary[0] = ( (callsign[j] & (1 << 7)) ? '1' : '0' );
      binary[1] = ( (callsign[j] & (1 << 6)) ? '1' : '0' );
      binary[2] = ( (callsign[j] & (1 << 5)) ? '1' : '0' );
      binary[3] = ( (callsign[j] & (1 << 4)) ? '1' : '0' );
      binary[4] = ( (callsign[j] & (1 << 3)) ? '1' : '0' );
      binary[5] = ( (callsign[j] & (1 << 2)) ? '1' : '0' );
      binary[6] = ( (callsign[j] & (1 << 1)) ? '1' : '0' );
      binary[7] = ( (callsign[j] & (1 << 0)) ? '1' : '0' );
      binary[8] = '\0';
      printf("Binary of %c is: %s\r\n", callsign[j], binary);
   }
}

有了这个函数,当我调用formatCallSign("CQ")时,它会打印出来

original is CQ
Hex of C is 43
Binary of C is: 01000011
Hex of Q is 51
Binary of Q is: 01010001

没关系,这是有道理的,这些是CQ 在 ASCII 中的正确二进制表示。

但它也比它的价值要麻烦得多——事实上它是毫无用处的。您说您的最终目标是移动这些字符中的位,但是将所有内容转换为字符串数组binary 会使这个问题变得更加困难。 (我们甚至还没有解决将二进制字符串转换回真正的 8 位字符的问题。)

您可能知道,几乎所有您在 C 程序中操作的东西——字符、整数、浮点数、字符串——在内部深处,已经是二进制的。例如,字符 C 已在内部表示为 01000011。如果您使用 %c 将其打印出来,它只会看起来像字符 C,而且它只是 看起来 em> 就像十六进制数 43 如果您使用 %x 打印出来。在内部深处,它仍然是 01000011,当您以其他形式打印出来时,它并没有改变。

由于字符已经在内部表示为二进制数,如果您想将字符的二进制表示左移 1,您可以通过简单、直接的 &lt;&lt; 运算符应用来做到这一点,无需事先明确转换为二进制必需的。这是一个 encryptCallSign 函数,它可以满足我的想法:

void encryptCallSign(unsigned char *callsign) {
    for(int j = 0; j < strlen(callsign); j++) {
        unsigned char newchar = callsign[j] << 1;
        printf("%X -> %X\n", callsign[j], newchar);
        callsign[j] = newchar;
    }
}

我尝试从一个小测试 main 函数中调用它,如下所示:

main()
{
    char callSign[] = "CQ";
    formatCallSign(callSign);
    printf("encrypting...\n");
    encryptCallSign(callSign);
    printf("encrypted:\n");
    formatCallSign(callSign);
}

这是我得到的输出:

original is CQ
Hex of C is 43
Binary of C is: 01000011
Hex of Q is 51
Binary of Q is: 01010001
encrypting...
43 -> 86
51 -> A2
encrypted:
original is ¢
Hex of  is 86
Binary of  is: 10000110
Hex of ¢ is a2
Binary of ¢ is: 10100010

这看起来有点滑稽,因为值为A2 的字符是¢ 符号(无论如何在ISO-8601-1“Latin1”中),而值为86 的字符没有打印表示。

虽然很简单,但我展示的encryptCallSign 函数的长度大约是所需长度的两倍。如果它没有打印出它的工作,我们可以将其简化为

void encryptCallSign(unsigned char *callsign) {
    for(int j = 0; j < strlen(callsign); j++)
        callsign[j] = callsign[j] << 1;
}

(您也可以使用 C 的“op =”快捷方式callsign[j] &lt;&lt;= 1。)

这里还有一些其他问题 - 例如,我的编译器正确地抱怨 char *unsigned char * 类型混合存在一些可疑的情况 - 但希望这能帮助您找到正确的方向。

【讨论】:

  • 很奇怪。当我使用您的 encryptCallSign 时(尽管我没有加密任何东西,只是移动位),我确实得到了正确的翻译per character。但是,当我在main 中有以下代码时:printf("before %x", dest); formatCallSign(&amp;dest); printf("after %x", dest); - 我得到“88 之前......正确的单个字符...... 88 之后”。我认为它引用了您所说的内存位置。我应该将您的答案与另一个答案结合起来并使用 strtol 或 sprintf 将字符串显式转换为二进制吗?
  • @InterLinked 同样,%x 不是打印字符串的正确方法。
  • 我现在无法发布测试过的代码,但是在调用encryptCallSign 之前和之后尝试printf("%s\n", callSign),或者,如果你想查看十六进制表示字符(而不是原始字符),尝试类似for(i = 0; i &lt; strlen(callSign); i++) printf("%02x ", callSign[i]);。为了确保您了解这里发生了什么,请比较非常相似的循环 for(i = 0; i &lt; strlen(callSign); i++) printf("%c ", callSign[i]); 的行为。
  • 这已经足够接近我需要它做的足够了,稍后我实际上需要构建一个超级数组,其中包含我移动位的所有呼号的有序十六进制值,所以拥有它毕竟在数组中是一个优势。谢谢!
猜你喜欢
  • 1970-01-01
  • 2018-09-16
  • 2013-11-04
  • 2014-08-17
  • 1970-01-01
  • 2013-07-21
  • 2011-10-01
  • 1970-01-01
  • 2021-07-25
相关资源
最近更新 更多