LINUX.ORG.RU

cast unsigned int * to unsigned short *

 , , ,


0

2

в общем, пишу так

buf = htonh(*((uint16_t *)p + i));

но это не очень читабельно, хочу как-то так:

buf = htonh((uint16_t[])p[i]);

подскажите, как правильно написать? не могу ничего нагуглить

★★★

Последнее исправление: IvanR (всего исправлений: 1)

как правильно написать?

Оба варианта неправильны. Размер указателя может не совпадать. Плюс проблемы alignment. Вы должны скопировать память в type *, разыменовать её в значение и присвоить его наружу.

PPP328 ★★★★★
()
Последнее исправление: PPP328 (всего исправлений: 1)
Ответ на: комментарий от PPP328

Размер указателя может не совпадать.

Что, почему? Размер указателя разве не фиксированный для любой платформы?

x86-
()
Ответ на: комментарий от Keltir

Ну, исключения редки, вроде 8051. Но здесь нет смысла это учитывать, кмк

x86-
()

Не так чтобы сильно читабельнее:

buf = htonh(((uint16_t *)p)[i]);

Может уже

uint16_t *pp = (void *)p;
buf = htonh(pp[i]);
xaizek ★★★★★
()

макру напишите, типа

#define cast(type, value) (type) (value)
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от x86-

В С вообще ничего не фиксировано. «Не более чем», «не меньше» - вот самые частые формулировки в стандарте относительно размеров.

LongLiveUbuntu ★★★★★
()

сишечник пишет тупой перебор bgp fv второй день, уже два тредика на лорчике

anonymous
()
Ответ на: комментарий от LongLiveUbuntu

Я об указателях, размер которых должен зависеть только от целевой платформы. Следовательно, при запуске программы на том же x64, размер указателей в этом инстансе должен быть фиксированным.

x86-
()

Зачем так тупить? Преобразовывать надо к char* p, затем два байта *p++ и *p++ меняешь местами;

Djanik
()
Ответ на: комментарий от LongLiveUbuntu

Размеры указателей вроде по стандарту могут различаться только для указателей на функции от указателей на что-угодно остальное. Размеры указателей на примитивы и структуры одинаковы для как минимум конкретного экземпляра программы, а то и всего бинарника для конкретной архитектуры и конкретных опций компиляции.

То есть C не даёт никаких гарантий, что указатель будет 16 или 32 или 64 бита, скажем. Но если в одном месте одной и той же программы указатель 32 бита, то во всех других местах все указатели на всё, кроме функций, тоже должны иметь такой же размер.

Разумеется, не учитываем какие-нибудь нестандартные расширения языка типа __attribute__ (те кто их используют, читают документацию к ним отдельно), а только базовый синтаксис (то есть программист не использует ничего нестандартного, у чего в документации к конкретному компилятору указано какое-то влияние на размер указателей).

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

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

где у ТС про «размер указателей»? он пытается привести некий p к указателю на int16. потом брать от него с индексом как будто это массив.

alysnix ★★★
()
Ответ на: комментарий от Djanik

из файла копируется в буфер по 2 байта, бефер пересылкается по modbus, принимается и записывается в файл (по 2 байта, чтобы избежать проблем с modbus, там тоже все пересылается по 2 байта), чтобы избежать проблем с разрядностью и little-big endian, использую htons при отправке и ntohs при получении. Может, можно проще, но как-то потом не охота отлавливать неожиданные проблемы. и потом, по 2 байта надежнее, modbus паботает с uint16_t

IvanR ★★★
() автор топика
Ответ на: комментарий от IvanR

чтобы избежать проблем с разрядностью и little-big endian

а у вас на концах этого модбаса могут быть разные места младшего байта?

alysnix ★★★
()
Ответ на: комментарий от IvanR

да не должно быть, вроде щас все процессоры big-endian, но на всякий случай

тогда зачем использовать «сетевой порядок», если надо использовать «антисетевой порядок»? зачем переставлять байты при посылке , чтобы потом их обратно переставить? вам это придется делать всегда.

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

alysnix ★★★
()
Ответ на: комментарий от untitl3d

Спасибо, стало намного читабельней и ясней.

x86-
()

как правильно написать?

Так вообще писать нельзя, формально у тебя УБ здесь должно быть. Надо через union. Или, как уже отметили - кастуй к char*.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)

подскажите, как правильно написать?

Как «правильно» или как «красиво»?

buf = htons( ((uint16_t *)p)[i] );

Чтобы поменьше вложенных скобок, можно сделать отдельную переменную:

uint16_t * myarray = (uint16_t *)p;
buf = htons( myarray[i] );

anonymous
()
Ответ на: комментарий от Djanik

Вижу вызов htonh который тоже не совсем бесплатный.

Практически бесплатный. Современные компилеры его инлайнят.

Demo: https://godbolt.org/z/PrKoazP1E

anonymous
()
Ответ на: комментарий от alysnix

я с arm работаю, не понятно, какой там порядок

например, ARM (по умолчанию — little endian),

фиг поймешь

IvanR ★★★
() автор топика
Ответ на: комментарий от IvanR

Можешь проверить.

#include <stdio.h>
#include <stdint.h>

int main ()
{
  uint16_t x = 0x0001;
  printf("%s-endian\n", *((uint8_t *) &x) ? "little" : "big");
}
Djanik
()
Ответ на: комментарий от KivApple

А если вспомнить 16-битный режим x86 с его сегментами и соответственно короткими и длинными указателями?

cobold ★★★★★
()
Ответ на: комментарий от cobold

И где сейчас такой компилятор найдёшь? Разве что open watcom, в котором чёрт ногу сломит.

luke ★★★★★
()

Тебе всё это не надо, испльзуй функцию для long https://www.opennet.ru/man.shtml?topic=htons&category=3&russian=0

А так, ты своими кастами получишь белиберду. Если уж приводишь то приводи значения друг другу, а не указатели. Ну вот у тебя массив по 4 байта ты его кастуешь к 2 байтам и делаешь забор значения получаешь первые два из 4х байт значнение и у тебя мусор вместо данных. Сначала из массива получай значение по индексу , затем проверй нет ли переполнения типа и твой unt32_t не больше твоего uint16_t по USHRT_MAX. Но это всё тебе не надо просто используй htonl вместо htonh

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от x86-

Тебе должно быть чхать на размер указателя. Ты либо сохраняешь его в другом указателе соотвецтвующего типа или void *. Или используешь адресную арифметику по соотвецтвующему типу или кастуешь к char* для смещения на заданный размер. Единственное когда тебе надо знать размер указателя это при расчёте памяти или сохранении адреса на который указывает указатель как значения переменной что-бы не вызвать переполнения. Но для этого тебе нужно сравнить sizeof(void*) c sizeof(твоя переменная) и всё, либо влезло, либо нет. Ибо даже на 8битной платформе указатель может быть 16 бит, а может и 4 бита (я таких не знаю, но сути это не меняет).

А так в си есть правило для всего char<=short<=int<=long<=long long Вот и всё всегда есть эта цепочка, а уж какого размера каждое из них как повезёт. Размер указателя же всегда sizeof(void*) а не что-то фиксированное.

И не путай со всякими uint64_t ptrdiff_t они просто означают то что мне нужен тип не менее 64 бит и мне нужен тип не менееsizeof(void*)` для данной платформы.

В пределах платформы ВСЁ ФИКСИРОВАННОЕ. В языке фиксирования нет, есть только чар меньше или равно шорт который меньше или равен инт который и так далее. Указатель тут всегда сбоку.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Я и не говорю о спецификации языка, речь шла о рантайме на большинстве платформ. Да, в каком-нибудь 8051 размер указателя может быть различным в пределах исполнения программы, но это очень уж редкие исключения на данный момент, чтобы их учитывать.

x86-
()
Ответ на: комментарий от x86-

Ну ок. Вот у тебя камень 64битный на руках. Ты пишешь для него загрузчик угадай какого размера должны быть твои указатели в нём? Даю наводку,64 битные процы стартуют в реальном режиме и в нём они работают в 16 битном режиме прям как Sega Mega Drive =) Это только потом они в защищённый режим переходят.

Ну и 32битные машины кто отменял? Тонны 32bit ARM которых больше чем всех ПК в мире. Не надо ломать голову где какой там размер указателя он всегда size_t size = sizeof(void*) и всё. Если так писать и так подходить к коду то твоя программа без изменений будет работать одинаково на 8/16/32/64/128/256 битных системах и даже на системах с троичной логикой вместо двоичной.

Твоя правда только в том случае если твоя платформа x86_64 и ты пишешь софт такой который прибит гвоздями, например игра. Нет смысла задумываться о том стартанёт ли код на 32bit ARM если код твой дёргает функции OpenGL4 попутно крутя финты с указателями, финты вроде тех что возвращают NULL в случае ошибки, но в этом NULL ещё и код возврата будет, твоё имя и год рождения твоей кошки. Ну или ты пишешь просто оптимизированный код для конкретной платформы или вообще просто для себя или вообще просто!!!11 =). Если уж так то всё просто вот тебе 8 байт и пихай туда всё нафиг не ошибёшься =)

Но ты меня не слушай я домохозяйка прост мимокрокодил.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 3)
Ответ на: комментарий от cobold

Разумеется, не учитываем какие-нибудь нестандартные расширения языка типа __attribute__ (те кто их используют, читают документацию к ним отдельно), а только базовый синтаксис (то есть программист не использует ничего нестандартного, у чего в документации к конкретному компилятору указано какое-то влияние на размер указателей).

Как я понимаю, штуки с near и far указателями это нестандартное расширение. И если у указателей нет никаких лишних нестандартных ключевых слов рядом, то всё ок.

KivApple ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Даю наводку,64 битные процы стартуют в реальном режиме

Знаю, но ТС явно не с загрузчиком работает.

cast unsigned int * to unsigned short * (комментарий)

Ну и 32битные машины кто отменял? Тонны 32bit ARM которых больше чем всех ПК в мире

Так там размер указателя хоть и 4б, но тоже ведь фиксированный в пределе рантайма платформы.

x86-
()
Ответ на: комментарий от x86-

Ну в пределах целевой платформы всё фиксированное (мимо всяких режимов до основного). Какой прок от того что ты знаешь что на твоей платформе указатель имеет какой то там размер в точности до бита? Что-бы хаки с битами указателей делать? Ну ладно почему бы и нет. Но завтра выйдет 128битный камень, а после завтра станет стандартом и всё приехала на сеновал ипца со своим кодом =)

Ты права когда нужно делать узкую конкретную весчь для платформы как таковой. Или твой софт и так уже прибит изначально и парится что оно не будет работать вне её нет смысла.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Ну я всё это к тому, что излишние действия и проверки, предложенные в этом комментарии, ненужны, когда речь идет о столь простой операции как uint16_t buf = htonh(*((uint16_t *)p + i));. Вот что в этой строке может пойти не так? На практически любой современной платформе.

x86-
()
Ответ на: комментарий от PPP328

Оба варианта неправильны. Размер указателя может не совпадать. Плюс проблемы alignment. Вы должны скопировать память в type *, разыменовать её в значение и присвоить его наружу.

+1.

Это по крайней мере точно будет работать.

fsb4000 ★★★★★
()
Ответ на: комментарий от x86-

Указатель на массив uint32_t, очевидно же

Ну тогда нужно использовать memcpy, очевидно же..

fsb4000 ★★★★★
()
Ответ на: комментарий от x86-

Здрасти девушка, как дила!

Смотри что может тут пойти не так и почему тигр прав.

Вот у тебя unsigned long = uint32_t значение равное 70536 (этому зачению нужно больше 2х байт) лежит по указателю *p теперь ты АХТУНГ сначала кастуешь указатель к uint16_t тем самым отрезаешь 2 байта от арифметики затем плюсуешь i значение коророго равно например 10. И теперь вместо прохождения по массиву на (sizeof(uint32_t) * i = 40 ) байт ты пройдёшь (sizeof(uint16_t) * i = 20) байт. Тоесть ты тупо во первых не на тот индекс данных попадёшь, во вторых лежащее там значение будет вообще мусором ибо обрежется и втретьих там вообщзе изначально может лежать значение которое в uint16_t не влезет и будет его кольцевое переполнение и ты опять получишь белиберду вместо ожидаемого числа. И тут вообще дело не в размере указателя.

Нужно так

uint16_t buff = 0;

unsigned int value = 0;
/*
Сначала выясним у нас индекс в космос не улетел
*/
if(i <= P_ARRAY_SIZE)
{
   value = *(p+i);
}else{

  ругаемся
}

/*или просто value = *(p + (i % P_ARRAY_SIZE)) если допустимо*/

/*
 сначала узнаём влезет ли вообще значение int в short.
 для этого сначала переходим на нужный индекс указателя
 разыменовываем его и сравниваем с лимитом типа в который будем писать
*/
if(value <= USHRT_MAX) 
{
  /*и только теперь как белые люди спокойно кастуем и то каст что-бы не ругался компилёр*/
  htonh((uint16_t)value);
}else{
  fprintf(stderr,"overflow uint16_t %s:%d:%s\n"__FILE__,__LINE__,__func__);
  ....
}

Или короче, если ты уверена в i уверена в значени массива p

uint16_t buff = *(p+i);
htonh(buff);

ну или сосем htonh((uint16_t)*(p+i)) коротко. Сначала двигаешь, адрес с учётом арифметики инта а не шорта, затем разыменовываешь значние и кастуешь его к шорту. Если А значение влезает 100% Б спещение по индексу p проверено выше.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

теперь ты АХТУНГ сначала кастуешь указатель к uint16_t тем самым отрезаешь 2 байта от арифметики затем плюсуешь i значение коророго равно например 10. И теперь вместо прохождения по массиву на (sizeof(uint32_t) * i = 320 ) байт ты пройдёшь (sizeof(uint16_t) * i = 160) байт.

Не-а, логическая ошибка. Он проходится циклом по элементам массива uint32_t p[]. Теперь смотрим пример:

p[0] = начинается с адреса 0xf72da630f72da630
p[1] = начинается с адреса 0xf72da630f72da634
p[2] = начинается с адреса 0xf72da630f72da638

Но вся фишка здесь в том, что ТС НЕ просто конвертирует массив uint32_t в массив uint16_t. Каждый элемент из p[] он передает на обработку в htonh(), и присваевает buf значение, возвращаемое этой функцией. То есть все должно отлично работать.

x86-
()
Ответ на: комментарий от x86-

То есть все должно отлично работать.

По какому пункту стандарта разрешёно разыменование unsigned short, если объект был unsigned int?

fsb4000 ★★★★★
()
Ответ на: комментарий от x86-

#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[])
{

  int i = 2;
  int p[5] = {1111,2222,3333,4444}; /*эти значения  вмещаются в uin16_t */
  uint16_t buff = *(uint16_t*)p+i;  /*делаем как ТС*/
  printf("%d\n",buff);//получаем белиберду


  /* А почему? А потому что размер указателя 
   * и смещение по индексу типа это разные вещи
   * мы смещаемся по памяти p + i не по размеру указалетя
   * а по размеру типа. тоесть p + (sizeof(p) * i)
   * размер указателя тут совсем не причём и знать его не надо
   * ты забыла про адресную арифметику и что она всегда в контексте типа
   * с которым мы работаем.
   * */
    return 0;
}
LINUX-ORG-RU ★★★★★
()

Кстати, тут есть небольшая ошибка, вместо

uint16_t buf = htonh(*((uint16_t *)p + i));

нужно

 uint16_t buf = htonh(*((uint16_t *)(p + i)));
x86-
()
Ответ на: комментарий от fsb4000

Стандарты не доводилось читать, но по практическому опыту все отлично работает, нуль варнингов. Gcc 9.3.0-r2.

#include <stdio.h>
#include <inttypes.h>

uint16_t htonh(uint16_t a) {
    return a + 2;
}

int main() {
    uint32_t p[] = { 1,2,3 };
    
    for (size_t i = 0; i < 3; i++) { 
        uint16_t buf = htonh(*((uint16_t *)(p + i)));
        printf("%d\n", buf);
    }

}

Выводит:

3
4
5
x86-
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.