PDA

Просмотр полной версии : stop := (size - SIZEOF(pt)) / SIZEOF(pt); как это работает?



Schneider
17.04.2023, 19:03
в oscat_basic есть множество функций работы с массивами.

у большинства из них(может и у всех, не проверял) в начале есть строчка как в заголовке темы
весь текст ниже


FUNCTION ARRAY_MIN : REAL
VAR_INPUT
pt : POINTER TO ARRAY[0..32000] OF REAL;
size : UINT;
END_VAR
VAR
i: UINT;
stop: UINT;
END_VAR
-------------------------
stop := (size - SIZEOF(pt)) / SIZEOF(pt);
array_min := pt^[0];
FOR i := 1 TO stop DO
IF pt^[i] < array_min THEN array_min := pt^[i]; END_IF;
END_FOR;

вызов её выглядет так
X:=oscat_basic.ARRAY_MIN(ADR(some),SIZEOFF(some))

не понимаю как работает первая строчка,
к примеру some массив из 10 значений REAL
тогда на вход фунции попадет указатель на массив и число 40 (размер массива в байтах)
в коде функции получается
stop:=(40-40)/40

иии какой в этом смысл? но это работает же. в переменной stop в итоге размер массива(количество значений real)
что я не так понимаю?:eek:

Евгений Кислов
17.04.2023, 19:11
к примеру some массив из 10 значений REAL
тогда на вход функции попадет указатель на массив и число 40 (размер массива в байтах)


Тут вы правы.


в коде функции получается
stop:=(40-40)/40

А вот здесь нет.

SIZEOF(pt) в данном случае возвращает размер указателя. В реализации CODESYS - размер указателя всегда равен 32 бита (4 байта) на 32-битных рантаймах.
На 64-битных рантаймах он равен 64 бита, но во времена разработки OSCAT поддержки таких рантаймов еще не было.

В итоге: (40-4)/4 = 9

Логично - у вас массив из 10 элементов, индексация элементов ведется с 0 - значит, индекс последнего элемента и правда равен 9.

Schneider
17.04.2023, 20:50
SIZEOF(pt) в данном случае возвращает размер указателя. В реализации CODESYS - размер указателя всегда равен 32 бита (4 байта) на 32-битных рантаймах.
На 64-битных рантаймах он равен 64 бита, но во времена разработки OSCAT поддержки таких рантаймов еще не было.

В итоге: (40-4)/4 = 9

Спасибо. Я догадывался, о чем то подобном. Да, ведь через ADDR мы получаем только адрес начала массива безотносительно его размеров . Только непонятно, что задумал автор? Ведь в других системах sizeoff(адрес) будет зависеть от количества адресуемой памяти видимо. А размер real так и останется 4 байта. Или 8 байт байт и это универсальный код получается?

Евгений Кислов
18.04.2023, 06:42
Только непонятно, что задумал автор? Ведь в других системах sizeoff(адрес) будет зависеть от количества адресуемой памяти видимо.

Библиотека разрабатывалась для конкретного набора систем (CoDeSys V2.3, PCWORX и т.д.), про которые было известно, что размер указателя там равен 4 байта.
При портировании библиотеки на другие системы - отвественность за подобные исправления лежит на авторе порта.

Schneider
18.04.2023, 07:09
...про которые было известно, что размер указателя там равен 4 байта....
в таком случае это излишнее усложнение и строку


stop := (size - SIZEOF(pt)) / SIZEOF(pt);

можно было написать проще и понятнее для человека:


stop := (size - 4) / 4;

хотя, конечно для компилятора по итогу все равно, так как SIZEOF(pt) на начальном этапе компиляции заменяется на константу 4.

PS. кстати, я задался этим вопросом потому, что переделывал под себя пару ФБ из oscat_basic.
все блоки работающие с массивами в этой библиотеке предназначены для массивов REAL.
А мне нужны были для INT массивов.
Я, не долго думая, взял исходники и переделал их под INT, но допустил такую вот ошибку (выше)

PPS. вот в другом месте ребята этот вопрос решили по другому:


stop := SHR(size,2)-1;

но это мне было мне понято. побитовый сдвиг вправо на два знака, равносилен делению на 4,
и для работы с INT достаточно было сдвигать на 1, чтобы получить деление на 2.

Cs-Cs
18.04.2023, 08:53
и строку

stop := (size - SIZEOF(pt)) / SIZEOF(pt);
можно было написать проще и понятнее для человека:

stop := (size - 4) / 4;
Так не принято делать во всех языках программирования. То, когда подставляют непонятные числа, называется "Магические числа (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D 0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_(%D0%BF% D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1 %80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)#%D0%9F%D0 %BB%D0%BE%D1%85%D0%B0%D1%8F_%D0%BF%D1%80%D0%B0%D0% BA%D1%82%D0%B8%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B 3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2% D0%B0%D0%BD%D0%B8%D1%8F)" и за это во многих компаниях серьёзно наказывают.
Поэтому здесь они и применили SIZEOF, и это правильно, хоть даже и будет известно что при 32-битных указателях это всегда равно 4.

Schneider
18.04.2023, 10:24
Спасибо, интересная статья! Не знал, но на практике в своем коде сталкивался с таким,
и интуитивно сам старался избавлятся от таких вещей, кстати. Теперь буду пользоваться осознано.

Тогда в продолжение её логики могу добавить, что выражение с числами:


stop := (size - 4) / 4;

вполне понятно, и ничего "магического" в цифре 4 нет, поскольку REAL он же FLOAT по смыслу во всех системах занимает 4 байта.
а брать размер адреса в качестве размера REAL - это ли не запутает еще больше?

а пример из другого блока той же oscat_basic так же работающей с массивами REAL и написаный ровно в таком же месте с такой же целью:


stop := SHR(size,2)-1;

вот вам классический пример "магических чисел" в таком случае.

Понятней тогда написать было типа:


SizeOfReal:USINT:=4;
stop := (size/SizeOfReal)-1;

Schneider
18.04.2023, 10:26
Поэтому здесь они и применили SIZEOF, и это правильно, хоть даже и будет известно что при 32-битных указателях это всегда равно 4.
Вот по моему, не совсем правильно, поскольку в 64 битной системе адрес будет 8ми байтный а REAL останется 4 байта, вроде, и тогда не переносимость кода.

Alexander S
19.04.2023, 09:35
Вот по моему, не совсем правильно, поскольку в 64 битной системе адрес будет 8ми байтный а REAL останется 4 байта, вроде, и тогда не переносимость кода.
>что задумал автор?
>как это работает?


Автор хотел написать переносимый код, написал неправильно, но получил работающий код, потому-что SIZEOF(POINTER) и SIZEOF(REAL) оба равны 4.

Переносимый вариант должен выглядеть как-то так:

stop := (size - SIZEOF(pt^[0])) / SIZEOF(pt^[0]);

Schneider
19.04.2023, 09:50
Да, так лучше всего, точно.