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

时间:2019-03-03 03:59:01

标签: c pointers unsigned-char

我正在尝试创建一个函数来打印/存储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,然后是下两个,依此类推。

2 个答案:

答案 0 :(得分:2)

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

  • u_char不是标准类型。 unsigned char是拼写此类型的标准方法。虽然您可能在代码库中使用typedef(例如typedef unsigned char u_char;),但最好使用标准类型,尤其是在使用不带{{1 }}本身。
  • typedeftypedef在C中的含义完全相同,而与&p[0]的值无关(假设它是一个指针)。出于相同的原因,pp的含义也相同。在进一步的示例中,我将仅使用p[0]*p,但要记住等效性。
  • p整数类型。这意味着其值为整数。该值也可以解释为字符的事实是偶然的。这将很快变得非常重要。

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

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

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

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

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; } 恰好是内部表示ASCII大写字母H的数字(称为 codepoint )。还记得我曾经说过72是整数吗?这就是它的意思:它的值实际上是一个数字。您要求计算机打印该号码,并且确实如此。

但是,如果要打印此数字表示的字符,则有两种选择:在72中使用unsigned char作为格式说明符(告诉它打印字符)或使用%c / printf函数(采用一个数字并打印它们代表的字符)。让我们去看看后者:

putchar

现在您将获得putc。到某个地方!现在,要打印 all 字符串中的字符,我们需要知道一个额外的细节:在字符串中所有有意义的字符之后,最后一个始终为零。如中所示,数字为零,而不是字符#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 str[] = "Hello world!"; 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; } 会做同样的事情,但这还不那么有趣,是吗?

答案 1 :(得分:0)

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

啊哈!这更有意义。当创建指向unsigned char值的unsigned指针时,您将具有指向该值在内存中的开头的指针,但是该值的存储方式将取决于机器的字节序和 byte分组中字节的顺序

简单地存储/打印当前存储在内存中的字节并不困难,也不必存储/打印每个两个字节。每种操作都可以通过类似以下方式完成:

/* 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提供了在网络字节顺序主机字节顺序之间进行转换的功能,反之亦然,可以使用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

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