Распечатать / сохранить n байтов указателя u_char в c? - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь создать функцию для печати / сохранения первых n байтов указателя u_char.Это то, что у меня сейчас есть (просто я пытаюсь напечатать первое значение), но оно не работает.

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 голосов
/ 04 марта 2019

В соответствии с вашими правками вы печатаете пакеты

Ах, ха!Это имеет больше смысла.Когда вы создаете указатель unsigned char на значение unsigned, у вас есть указатель на начало значения в памяти, но то, как оно будет сохранено, будет зависеть от порядкового номера машины и порядка байтов 1008* байтов в пакете.

Простое сохранение / распечатка байтов, поскольку они в данный момент хранятся в памяти, не представляет трудностей, равно как и сохранение / печать каждого двухбайтовых.Каждое из них может быть выполнено с помощью чего-то похожего на:

/* 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_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);
}

( note: некоторые системыиспользуйте заголовок netinet/in.h вместо arpa/inet.h для преобразования метеорологов, как указано на странице руководства)

Полный пример использования / Вывод

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

dead
beef

efbe
adde

Вы можетехранить значения вместо печати - но это вам остается.Посмотрите вещи и дайте мне знать, если у вас есть вопросы.

0 голосов
/ 03 марта 2019

Прежде всего, несколько замечаний о вашем коде:

  • 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) не является неопределенным поведением, в отличие от случая со вторым фрагментом.*p равно const unsigned char (указатель был разыменован), а любой тип, более узкий, чем int, повышается до int в списках переменных параметров (printf определяется как int printf(const char *, ...); , ... вend указывает на то, что он принимает любое количество аргументов любого типа, и по этой причине его часто называют variadic), так что это действительно.

И на самом деле это работает.Давайте попробуем использовать полную программу:

#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 это число (называемое codepoint ), которое внутренне представляет заглавную букву H в ASCII.Помните, как я сказал, что 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 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;
}

И мы идем!Hello world! будет напечатано.Конечно, puts("Hello world!"); сделал бы то же самое, но это уже не так весело, правда?

...