Как наложить указатель sockaddr на sockaddr_in на Arm [duplicate]

Математическая математика с плавающей запятой такова. В большинстве языков программирования он основан на стандарте IEEE 754 . JavaScript использует 64-битное представление с плавающей запятой, которое совпадает с Java double. Суть проблемы состоит в том, что числа представлены в этом формате как целое число раз в два раза; рациональные числа (такие как 0.1, который является 1/10), знаменатель которого не является степенью двух, не могут быть точно представлены.

Для 0.1 в стандартном формате binary64 представление может записывается в точности как

  • 0.1000000000000000055511151231257827021181583404541015625 в десятичной форме или
  • 0x1.999999999999ap-4 в нотации C99 hexfloat .

Напротив, рациональное число 0.1, которое является 1/10, может быть записано точно как

  • 0.1 в десятичной форме или
  • 0x1.99999999999999...p-4 в аналоге обозначения гексафлоата C99, где ... представляет собой бесконечную последовательность 9.

Константы 0.2 и 0.3 в вашей программе также будут приближенными к их истинные ценности. Бывает, что ближайший double до 0.2 больше, чем рациональное число 0.2, но ближайший double до 0.3 меньше, чем рациональное число 0.3. Сумма 0.1 и 0.2 заканчивается выше, чем рациональное число 0.3 и, следовательно, не согласуется с константой в вашем коде.

Достаточно полное рассмотрение арифметических вопросов с плавающей запятой Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой . Для более простого объяснения см. floating-point-gui.de .

4
задан Abhinav Gauniyal 22 February 2016 в 13:21
поделиться

3 ответа

TLDR: Это предупреждение не указывает на ошибку в вашем коде, но вы можете избежать этого, используя poper c ++ reinterpret_cast (благодаря @Kurt Stutsman).


Объяснение:

Причина предупреждения:

  • sockaddr состоит из unsigned short (обычно 16 бит) и массива char, поэтому его требование к выравниванию равно 2.
  • sockaddr_in содержит (среди прочего) a struct in_addr, который имеет требование выравнивания 4, которое, в свою очередь, означает sockaddr_in, также должно быть выровнено с границей 4 байта.

По этой причине приведение произвольного sockaddr* в sockaddr_in* изменяет требование выравнивания, и доступ к объекту с помощью нового указателя будет даже нарушать правила псевдонимов и приводить к неопределенному поведению.

Почему вы можете игнорировать его:

В вашем случае объект p->ai_addr указывает на, скорее всего, это sockaddr_in или sockaddr_in6 объект в любом случае (как определено проверив ai_family), и поэтому операция безопасна. Однако компилятор не знает этого и выдает предупреждение.

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

Решение. Я не знаю, как это сделать (кроме предупреждения), что необычно для предупреждений, разрешенных -Weverything. Вы могли бы скопировать объект, на который указывает p->ai_addr byte by byte, на объект соответствующего типа, но тогда вы, скорее всего, больше не будете использовать addr так же, как раньше, поскольку теперь он указывает на другой (например, локальной) переменной. -Weverything - это не то, что я бы использовал для своих обычных сборок, потому что он добавляет слишком много шума, но если вы хотите его сохранить, @Kurt Stutsman упомянул хорошее решение в комментариях:

clang ++ (g ++ не выдает предупреждения в любом случае) не выдает предупреждения, если вы используете вместо [c] f17 вместо c style cast (который вы не должны использовать в любом случае), хотя оба имеют (в данном случае ) точно такая же функциональность. Возможно, потому что reinterpret_cast явно сообщает компилятору: «Поверьте мне, я знаю, что я делаю» .


На стороне. Примечание: в коде c ++ вы не нужны ключевые слова struct.

7
ответ дан MikeMB 25 August 2018 в 07:23
поделиться

Хорошо -Weverything позволяет довольно много предупреждений, некоторые из которых, как известно, бросают нежелательные предупреждения.

Здесь ваш код запускает предупреждение cast-align, которое явно говорит

cast from ... to ... увеличивает необходимое выравнивание от ... до ...

И это имеет место здесь, потому что выравнивание для struct addr - только 2 тогда как для struct addr_in оно равно 4.

Но вы (и программист для getaddrinfo ...) знаете, что указатель p->ai_addr уже указывает на фактический struct addr_in, поэтому актерский состав Действительный.

Вы можете либо:

  • , чтобы предупредить огонь и проигнорировать его - ведь это всего лишь предупреждение ...
  • отключить его с -Wno-cast-align после -Weverything

Должен признать, что я редко использую -Weverything по этой причине и использую только -Wall


Альтернативно , если вы знаете, что используете только CLang, вы можете использовать pragmas , чтобы развернуть предупреждение только в этих строках:

for(p = res; p != NULL; p = p->ai_next) {
    void *addr;
    std::string ipVer = "IPv0";

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-align"

    if(p->ai_family == AF_INET) {
        ipVer                    = "IPv4";
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
        addr                     = &(ipv4->sin_addr);
    }

    else {
        ipVer                     = "IPv6";
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
        addr                      = &(ipv6->sin6_addr);
    }
#pragma clang diagnostic pop

....
}
6
ответ дан Serge Ballesta 25 August 2018 в 07:23
поделиться

Чтобы уточнить версию memcpy. I thnk это необходимо для ARM, который не может иметь неверные данные.

Я создал структуру, содержащую только первые два поля (мне нужен только порт)

struct sockaddr_in_header {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
};

Затем, чтобы получить порт, я использовал memcpy для перемещения данных в стек

struct sockaddr_in_header   sinh;
unsigned short              sin_port;

memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header));

И вернул порт

sin_port = ntohs(sinh.sin_port);

. Этот ответ действительно связан с получением порта на руке

Как мне наложить указатель sockaddr на sockaddr_in на Arm

Силы, которые будут думать, что это тот же вопрос, что и этот, однако я не хочу игнорировать предупреждения. Опыт научил меня, что это плохая идея.

1
ответ дан teknopaul 25 August 2018 в 07:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: