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

Posted

技术标签:

【中文标题】在c中打印/存储n字节的u_char指针?【英文标题】:Print/store n bytes of u_char pointer in c? 【发布时间】:2019-03-03 03:59:01 【问题描述】:

我正在尝试创建一个函数来打印/存储 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);

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

【问题讨论】:

您能否更具体地说明什么不起作用?您期望的输出与使用每个功能得到的输出是什么?我尝试了你的第一个函数,它打印了第一个 u_char 作为数字。 第一个函数是正确的。可能是您使用了错误的转换字符串? "%d" 用于十进制转换,打印一个数值。 "%c" 用于字符:​​try, printf("%c", p[0]); 什么是'p'?它是指向unsigned 值的指针吗,例如unsigned u = 0xdeadbeef; unsigned char *p = (unsigned char *)&u;?或者它是指向unsigned char 数组的指针,例如char s[] = "somestring"; unsigned char *p = (unsigned char *)s; @DavidC.Rankin 看到我的编辑,抱歉我之前没有包含它。 @Citut - 我添加了一个答案来处理 "packet" 中的值,而不是字符串。您需要确定数据包中数据的 byteorder(在答案中解释)如果您有任何问题,请告诉我。 【参考方案1】:

首先,关于您的代码的几点说明:

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

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

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

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

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

#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 的数字(称为 codepoint)。还记得我说过unsigned char 是一个整数类型吗?这就是它的意思:它的值实际上是一个数字。您要求您的计算机打印该号码,它确实做到了。

如果你想打印这个数字代表的字符,你有两个选择:使用%c作为printf中的格式说明符(告诉它打印字符)或使用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!"); 也会做同样的事情,但这不是那么有趣,是吗?

【讨论】:

努力(而且格式很好),但可以肯定的是,我认为您必须找出p 指向什么?符号不同的字符数组?、无符号值?和无符号字符数组?您已经涵盖了第 1 部分,但请尝试确保 OP 不是在谈论案例 2 或 3。 @DavidC.Rankin 在将字符打印为字符时,字符的签名是无关紧要的。此外,我们知道他发布的第三个 sn-p 打印了 something,这意味着它必须指向一个以 null 结尾的字符数组——换句话说,一个字符串。 是的,我知道,一个字符无论如何都会转换为 unsigned char。不同之处在于回答他关于如何输出所有字节的最终问题。 (我猜他确实提到了string,因此可以将其范围缩小到您的情况)在这种情况下,打印所有内容的函数可能只是while (*p) putchar (*p++); @aaaaaa123456789 做得好,非常感谢。我做了一点修改,我不确定这是否重要,但我正在打印数据包(十六进制格式)。 @Citut 如果你想打印一个十六进制的unsigned char%02hhX 可以做到。 0 表示以前导零作为前缀是值太小,2 无法填充到长度 2,hh 是修饰符,表示该值是 char,而不是 int,并且X 表示打印十六进制数字,A-F 使用大写字母(x 使用小写)。【参考方案2】:

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

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

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

/* 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",根据数据包是network-byte-order还是host-byte-order,你可能需要一个转换(或者简单的位移) 以按您需要的顺序获取字节。 C 提供了在 network-byte-orderhost-byte-order 之间转换的函数,反之亦然,man 3 byteorder htonl, htons, ntohl, ntohs。需要,因为网络字节顺序是大端,而普通 x86 和 x86_64 是小端。如果你的包是网络字节序并且你需要主机字节序,你可以简单地调用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 的原型(以及所有字节顺序转换)使用 exact-width 类型 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

您可以存储值而不是打印 - 但这留给您。看看事情,如果你有问题,请告诉我。

【讨论】:

以上是关于在c中打印/存储n字节的u_char指针?的主要内容,如果未能解决你的问题,请参考以下文章

C多级指针与多维数组

《带你学C带你飞》---指针

C入门10指针

无法在 C 中打印返回数组指针的函数的输出

《彻底搞定C指针》文档整理

C语言打印输出指针数组