我正在尝试创建一个函数来打印/存储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
,然后是下两个,依此类推。
答案 0 :(得分:2)
首先,关于您的代码的一些注意事项:
u_char
不是标准类型。 unsigned char
是拼写此类型的标准方法。虽然您可能在代码库中使用typedef
(例如typedef unsigned char u_char;
),但最好使用标准类型,尤其是在使用不带{{1 }}本身。typedef
和typedef
在C中的含义完全相同,而与&p[0]
的值无关(假设它是一个指针)。出于相同的原因,p
和p
的含义也相同。在进一步的示例中,我将仅使用p[0]
和*p
,但要记住等效性。p
是整数类型。这意味着其值为整数。该值也可以解释为字符的事实是偶然的。这将很快变得非常重要。现在,关于您的片段。让我们以相反的顺序进行。如您所知,最后一个只是打印字符串。
第二个是未定义的行为。 *p
(unsigned char
= printf("%d", p)
,记得吗?)将指针作为参数传递(&p[0]
类型为p
),但是p
需要一个const unsigned char *
。参数必须与格式说明符指示的类型匹配。否则是错误的。它可能可能“起作用”(例如,不会崩溃),但是您绝对不应该这样做。这不是有效的C。
第一个是最有趣的。首先,与第二个片段的情况不同,%d
不是未定义的行为。 int
是printf("%d", *p)
(指针已被取消引用),并且比*p
窄的任何类型在可变参量列表上都提升为const unsigned char
(int
被定义为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_t
和uint32_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
您可以存储值而不是打印-但这留给您。仔细检查一下,如果您有任何问题,请告诉我。