繁体   English   中英

在 c 中打印/存储 n 个字节的 u_char 指针?

[英]Print/store n bytes of u_char pointer in c?

我正在尝试创建一个函数来打印/存储 u_char 指针的前 n 个字节。 这是我目前拥有的(只是试图打印第一个值),但它不起作用。

void print_first_value(const u_char * p) {
    printf("%d", p[0]);
}

我也试过这个:

void print_first_value(const u_char * p) {
    printf("%d", &p[0]);
}

我将如何使这项工作? 最后,我想遍历*p的各个值,但我只能通过此代码在p指向的地址处打印整个字符串。

void print_first_value(const u_char * p) {
    printf("%s", p);
}

所以我打印的是数据包,抱歉我没有提到这一点。 最后一段代码以十六进制打印数据包,因此类似于0050 5686 7654 0000...我想打印/存储某些索引处的值。 所以我想要前两个块00505686 ,然后是接下来的两个块,依此类推。

首先,关于您的代码的一些注意事项:

  • u_char不是标准类型。 unsigned char是拼写这种类型的标准方式。 虽然您可能在代码库中使用typedef (例如typedef unsigned char u_char; ),但最好使用标准类型,尤其是在发布使用该typedef而没有typedef本身的代码时。
  • &p[0]p在 C 中的含义完全相同,无论p的值如何(假设它是一个指针)。 同理, p[0]*p也是同一个意思。 我将在其他示例中专门使用p*p ,但请记住它们的等效性。
  • unsigned char是一个整数类型 这意味着它的值是一个整数。 这个值也可以解释为一个字符的事实是偶然的。 这很快就会非常重要。

现在,至于你的片段。 让我们以相反的顺序进行。 如您所知,最后一个只是打印字符串。

第二个是未定义的行为。 printf("%d", p)&p[0] = p ,还记得吗?)正在传递一个指针作为参数( p的类型是const unsigned char * ),但%d需要一个int 参数必须与格式说明符指示的类型相匹配; 否则是错误的。 可能会“工作”(例如,不会崩溃),但这是您绝对不应该做的事情。 无效 C.

第一个是最有趣的。 首先, printf("%d", *p)不是未定义的行为,与第二个片段的情况不同。 *pconst unsigned char (指针已被取消引用),任何比int窄的类型都会在可变参数列表上提升为intprintf定义为int printf(const char *, ...), ...最后表示它接受任何数量的任何类型的参数,并且由于这个原因它通常被称为可变参数),所以这是有效的。

事实上,它有效。 让我们尝试使用它的完整程序:

#include <stdio.h>

void print_first_value (const unsigned char * p) {
  printf("%d", *p);
}

int main (void) {
  char str[] = "Hello world!";
  print_first_value(str);
  return 0;
}

假设您没有使用特别奇怪的计算机或操作系统,您将通过这种方式打印72 这没有错! 72恰好是在 ASCII 中内部表示大写字母 H 的数字(称为代码点)。 还记得我说过unsigned char是整数类型吗? 这就是它的意思:它的值实际上是一个数字。 你让你的电脑打印这个数字,它确实做到了。

但是,如果你想打印这个数字代表的字符,你有两种选择:在printf使用%c作为格式说明符(告诉它打印字符)或使用putchar / putc函数(它采用单个数字并打印它们所代表的字符)。 让我们来看看后者:

#include <stdio.h>

void print_first_character (const char * p) {
  // it doesn't matter if it is unsigned or signed,
  // because we're just printing the character
  putchar(*p);
}

int main (void) {
  char str[] = "Hello world!";
  print_first_character(str);
  return 0;
}

现在你会得到H 到达某个地方! 现在,要打印字符串中的所有字符,我们需要知道一个额外的细节:在字符串中所有有意义的字符之后,最后一个总是零。 就像数字零,而不是字符'0' (这通常写为'\\0' ,但这与零相同。)所以,我们开始:

#include <stdio.h>

void print_first_character (const char * p) {
  putchar(*p);
}

int main (void) {
  char message[] = "Hello world!";
  const char * str = message; // initialize the pointer to the beginning of the string
  while (*str) { // while *str isn't zero
    print_first_character(str); // print the character...
    str ++; // ...and advance to the next one
  }
  putchar('\n'); // let's print a newline too, so the output looks nicer
  return 0;
}

现在我们开始! Hello world! 将被打印。 当然, puts("Hello world!"); 会做同样的事情,但这不那么有趣,现在是吗?

根据您的编辑,您正在打印数据包

啊哈! 这更有意义。 当您创建一个指向unsigned值的unsigned char指针时,您有一个指向内存中该值开头的指针,但该值的存储方式将取决于机器的字节序和数据包中字节的字节顺序

简单地存储/打印出当前存储在内存中的字节并不困难,存储/打印每两个字节也不困难。 每个都可以用类似的东西来完成:

/* all bytes stored in memory */
void prn_all (const unsigned char *p, size_t nbytes)
{
    while (nbytes--)
        printf ("0x%02x\n", p[nbytes]);
}

/* each 2-bytes stored in memory */
void prn_two (const unsigned char *p, size_t nbytes)
{
    while (nbytes--) {
        printf ("%02x", p[nbytes]);
        if (nbytes % 2 == 0)
            putchar ('\n');
    }
}
...
    unsigned u = 0xdeadbeef;
    unsigned char *p = (unsigned char *)&u;

    prn_all (p, sizeof u);
    putchar ('\n');
    prn_two (p, sizeof u);

会导致:

$ /bin/prn_uchar_byte
0xde
0xad
0xbe
0xef

dead
beef

现在警告。 由于您提到了"packet" ,根据数据包是按网络字节顺序还是主机字节顺序,您可能需要进行转换(或简单的位移)以按您需要的顺序获取字节。 C 提供了在网络字节顺序主机字节顺序之间进行转换的函数,反之亦然,使用man 3 byteorder htonl, htons, ntohl, ntohs 需要,因为网络字节顺序是 Big Endian 而正常的 x86 和 x86_64 是 Little Endian。 如果您的包是网络字节顺序并且您需要主机字节顺序,您可以简单地调用ntohs (网络到主机short )将每个两字节值转换为主机顺序,例如

/* each 2-bytes converted to host byte order from network byte order */
void prn_two_host_order (const unsigned char *p, size_t nbytes)
{
    for (size_t i = 0; i < nbytes; i+=2) {
        uint16_t hostorder = ntohs (*(uint16_t*)(p+i));
        printf ("%04" PRIx16 "\n", hostorder);
    }
}
...
    prn_two_host_order (p, sizeof u);

结果是:

efbe
adde

注意: ntohs (和所有字节顺序转换)的原型使用精确宽度类型uint16_tuint32_t相关的打印宏在inttypes.h ——它也自动包含stdint.h

您将确定"packets"的顺序,以了解是否需要字节顺序转换。 这将取决于您如何获取数据。

将其放在一个简短的示例中,您可以执行以下操作:

#include <stdio.h>
#include <inttypes.h>
#include <arpa/inet.h>

/* all bytes stored in memory */
void prn_all (const unsigned char *p, size_t nbytes)
{
    while (nbytes--)
        printf ("0x%02x\n", p[nbytes]);
}

/* each 2-bytes stored in memory */
void prn_two (const unsigned char *p, size_t nbytes)
{
    while (nbytes--) {
        printf ("%02x", p[nbytes]);
        if (nbytes % 2 == 0)
            putchar ('\n');
    }
}

/* each 2-bytes converted to host byte order from network byte order */
void prn_two_host_order (const unsigned char *p, size_t nbytes)
{
    for (size_t i = 0; i < nbytes; i+=2) {
        uint16_t hostorder = ntohs (*(uint16_t*)(p+i));
        printf ("%04" PRIx16 "\n", hostorder);
    }
}

int main (void) {

    unsigned u = 0xdeadbeef;
    unsigned char *p = (unsigned char *)&u;

    prn_all (p, sizeof u);
    putchar ('\n');
    prn_two (p, sizeof u);
    putchar ('\n');
    prn_two_host_order (p, sizeof u);
}

注意:有些系统使用头文件netinet/in.h而不是arpa/inet.h来进行手册页中列出的字节顺序转换)

完整示例使用/输出

$ /bin/prn_uchar_byte
0xde
0xad
0xbe
0xef

dead
beef

efbe
adde

您可以存储这些值而不是打印——但这取决于您。 仔细检查一下,如果您有任何问题,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM