Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 24.02.2023, 10:04   #11
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,695
По умолчанию

А еще char где-то знаковый, где-то нет... Да, именно для этого и повводили типы фиксированной размерности и иже с ними.
p51x на форуме Ответить с цитированием
Старый 26.02.2023, 05:52   #12
Пётр Седов
Форумчанин
 
Регистрация: 26.10.2022
Сообщений: 119
По умолчанию

Цитата:
Сообщение от p51x Посмотреть сообщение
И получить скрытые проблемы, т.к. это зависит от реализации.
Какие проблемы? Попробуйте в 3ds Max загрузить mesh, в котором > 2 миллиарда вершин. Он тупо crash-нется. Он crash-ится даже когда миллион вершин. Blender кстати нормально грузит.
Цитата:
Сообщение от p51x Посмотреть сообщение
1. Только стоит учитывать, что вполне возможны доп. расходы на расширение/обнуление остальной части регистра.
Речь скорее не об отдельных переменных, а о массивах. Как раз недавно был пример из прошлой жизни:
https://www.programmersforum.ru/showthread.php?t=345144
Палитра -- массив из 256 цветов. Каждый пиксел картинки -- ровно один байт, индекс цвета в палитре. Вот разумный пример, когда индекс элемента массива хранится в unsigned char, а не size_t. В API для 3D-графики (OpenGL, Direct3D) есть index buffer-ы, в них хранятся индексы вершин, обычно там unsigned short.
Цитата:
Сообщение от p51x Посмотреть сообщение
2. Если использовать, то сразу обкладывать ассертами, чтобы сюрпризов не было.
Да, assert -- полезная вещь, независимо от используемых типов. В Delphi и Java изначально не было assert, но потом добавили, потому что полезно.
Цитата:
Сообщение от p51x Посмотреть сообщение
Ну-ну
На движке Doom 3 действительно сделано немного игр (в Википедии написано, что 5). Давайте глянем Unreal Engine 4, на котором сделано много игр, которые потребляют гигабайты памяти.
Класс FString:
Цитата:
Код:
class FString
...
Код:
int32 Len()
Get the length of the string, excluding terminating character
...
Код:
TCHAR& operator[]
(
    int32 Index
)
Return specific character from this string
Код:
const TCHAR& operator[]
(
    int32 Index
)
Return specific const character from this string
Класс TArray:
Цитата:
Код:
template<typename InElementType, typename InAllocator>
class TArray
...
Код:
bool Find
(
    const ElementType& Item,
    SizeType& Index
)
Finds element within the array.
...
Код:
SizeType Num()
Returns number of elements in array.
...
Код:
const ElementType& operator[]
(
    SizeType Index
)
Array bracket operator. Returns reference to element at give index. Const version of the above.
Код:
ElementType& operator[]
(
    SizeType Index
)
Array bracket operator. Returns reference to element at give index.
Тип SizeType берётся из allocator-а. В allocator-е по умолчанию, SizeType -- это int32. Пример использования класса TArray с allocator-ом по умолчанию:
Цитата:
Код:
TArray<FString> StrArr;
...
...
Код:
for (int32 Index = 0; Index != StrArr.Num(); ++Index)
{
    JoinedStr += StrArr[Index];
    JoinedStr += TEXT(" ");
}
...
Код:
int32 Index;
if (StrArr.Find(TEXT("Hello"), Index))
{
    // Index == 3
}
Цитата:
Сообщение от p51x Посмотреть сообщение
А вы мотивационную часть по std::ssize читали?
Нет, не читал. По-моему, понятно, что добавление функции std::ssize -- это признание того, что использовать по умолчанию беззнаковый тип для метода std::vector::size оказалось неудачным решением, и вот теперь запоздалая попытка это хоть как-то исправить.
Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
Пётр Седов, не надо вводить новичков в заблуждение
Это скорее вы вводите людей в заблуждение, потому что вот это ваше утверждение:
Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
Использование int для размеров и индексов - это некорректно
не соответствует действительности. Я привёл примеры серьёзных проектов на C++ (Doom 3, Unreal Engine 4), в которых строковый класс и обобщённый динамический массив используют int для индексов и количества элементов. Если теоретики в книгах советуют использовать size_t, ну так на то они и теоретики. У реальных программистов нет времени книги писать. Разве что сообщения на форуме .
Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
Есть тип size_t, им надо пользоваться.
В редких исключительных случаях, например когда делаем свой heap (распределитель памяти).
Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
я ещё добавлю, что переполнение беззнаковой переменной - это нормальная ситуация с точки зрения языка. А переполнение знаковой переменой - это UB
Это как раз жирный довод использовать int. Раз знаковое переполнение -- undefined behavior, значит компилятор имеет право относиться к нему так же, как к целочисленному делению на ноль, и например при debug-настройках можно вставлять x86-инструкцию into (interrupt if overflow) для аварийного завершения программы или для передачи управления отладчику. Другое дело, что широко используемые компиляторы почему-то так не делают.

Кстати, есть новый язык Zig, C-шного уровня, и там беззнаковое переполнение тоже считается undefined behavior:
Цитата:
* Carefully chosen undefined behavior. For example, in Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C. This facilitates optimizations that are not available in C.
То есть беззнаковое переполнение тоже считается runtime-ошибкой, и это разумно. Если кому-то действительно нужна беззнаковая арифметика по модулю pow(2, 32) (например для вычисления hash-функции), то это лучше делать специальными функциями, встроенными в язык.
Пётр Седов вне форума Ответить с цитированием
Старый 26.02.2023, 09:21   #13
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 951
По умолчанию

Пётр Седов, серьёзность проекта (а перечисленные поделки - это именно поделки, ошибка в них ничего не значит) не исключает неточностей и ошибок в программах. Индекс в массивах - он только беззнаковый. К этому можно больше не возвращаться в обсуждении.

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Это как раз жирный довод использовать int
нет, не повод. Наличие UB в программе делает эту программу неработоспособной. На этом вопрос тоже можно закрыть.

Когда есть шанс получить переполнение для беззнакового значения, это должен учитывать программист. И должен сам обработать эти случаи. Это и есть - серьёзный подход

Единственный случай, когда для размера и индекса используется тип по размеру меньший, чем size_t - это когда нужно экономить место в памяти. Такие случаи очень редки

Последний раз редактировалось Алексей1153; 26.02.2023 в 09:57.
Алексей1153 вне форума Ответить с цитированием
Старый 26.02.2023, 10:15   #14
macomics
Участник клуба
 
Регистрация: 17.04.2022
Сообщений: 1,833
По умолчанию

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Палитра -- массив из 256 цветов. Каждый пиксел картинки -- ровно один байт, индекс цвета в палитре. Вот разумный пример, когда индекс элемента массива хранится в unsigned char, а не size_t. В API для 3D-графики (OpenGL, Direct3D) есть index buffer-ы, в них хранятся индексы вершин, обычно там unsigned short.
1) Вообще то в примере палитра хранится в видеоадаптере, а индексы все равно имеют размерности соответствующие текущей адресации. Т.е. пример составлен для 16-битного режима и индексы рассчитаны в переменной типа word.
Код:
    cmp cx,320 ; координаты выходят за границы экрана ?
    jnc pl1
    cmp dx,200
    jnc pl1
    push ax
    mov ax,320 ; di=320*y+x
    mul dx
    add ax,cx
т.к. длина видеобуфера = 64 кб, то используется без знаковый индекс для этого массива пикселей.

Но если интересно, тогда разработчики команд процессора закладывают механизмы применения знаковых индексов для адресации в памяти. Это наглядно будет видно в 64-битном режиме, где используется 64-битные канонические адреса при составлении PLM5, PLM4, PDPTR, PDE и PTB таблиц и формировании виртуального адресного пространства.

Вывод. При индексации в массивах для процессоров Intel всегда стоит брать переменную той же размерности, что и режим адресация в памяти. А в C++ для индексации отлично подходит int.
С другой стороны в массивах обычно нету отрицательных индексов и количество элементов не может быть отрицательным. И для хранения количества элементов массива так и хочется использовать тип unsigned int.
Но лучше использовать одинаковый тип для хранения длины и индекса в массиве. Так компилятор не станет перекладывать значение из одного регистра в другой лишний раз.

ADD: К тому же в примере настройка палитры не осуществляется, а в нее загружены только первые 16 цветов.

Последний раз редактировалось macomics; 26.02.2023 в 10:34.
macomics вне форума Ответить с цитированием
Старый 26.02.2023, 10:27   #15
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,695
По умолчанию

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Какие проблемы?
Может стать MAX_INT, может стать отрицательным, может стать 0, компилятор, увидев приведение может везде считать int...

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Речь скорее не об отдельных переменных, а о массивах.
Без разницы. Если вы в регистр грузите байт, то компилятор/процессор обязан обнулить остальную часть регистра.

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
В API для 3D-графики (OpenGL, Direct3D) есть index buffer-ы, в них хранятся индексы вершин, обычно там unsigned short.
Обычно там то,что поддерживает видеокарта.

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
На движке Doom 3 действительно сделано немного игр (в Википедии написано, что 5). Давайте глянем Unreal Engine 4, на котором сделано много игр, которые потребляют гигабайты памяти.
А давйте что-то более свежее, без легаси и техдолга?..

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Нет, не читал. По-моему, понятно, что добавление функции std::ssize -- это признание того, что использовать по умолчанию беззнаковый тип для метода std::vector::size оказалось неудачным решением, и вот теперь запоздалая попытка это хоть как-то исправить.
Так и думал...

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
У реальных программистов нет времени книги писать. Разве что сообщения на форуме .
Мда.. Т.е. себя вы тоже программистом не считаете?

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
В редких исключительных случаях, например когда делаем свой heap (распределитель памяти).
Угу, и в ЮД они не нужны, и в файлах, и т.д.

Цитата:
Сообщение от Пётр Седов Посмотреть сообщение
Раз знаковое переполнение -- undefined behavior, значит компилятор имеет право относиться к нему так же, как к целочисленному делению на ноль
Он имеет право делать, что угодно. Например, считать, что оно никогда не случается и оптимизировать.
p51x на форуме Ответить с цитированием
Старый 05.03.2023, 07:42   #16
Пётр Седов
Форумчанин
 
Регистрация: 26.10.2022
Сообщений: 119
По умолчанию

Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
Пётр Седов, серьёзность проекта (а перечисленные поделки - это именно поделки, ошибка в них ничего не значит)
В смысле, Doom 3, Unreal Engine 4 -- поделки? А вы участвовали в каких-то более серьёзных проектах на C++? Типа там, курсовая для Маши?
Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
нет, не повод. Наличие UB в программе делает эту программу неработоспособной. На этом вопрос тоже можно закрыть.

Когда есть шанс получить переполнение для беззнакового значения, это должен учитывать программист. И должен сам обработать эти случаи. Это и есть - серьёзный подход
В хорошем языке C-шного уровня должно быть примерно так:
1. Знаковое переполнение -- undefined behavior, то есть считается runtime-ошибкой. Это даёт компилятору право при debug-настройках генерировать машинный код, который такие ситуации обнаруживает, и как-то об этом сообщает (interrupt, assert, исключение, ...).
2. Беззнаковое переполнение -- undefined behavior, то есть считается runtime-ошибкой. Это даёт компилятору право при debug-настройках генерировать машинный код, который такие ситуации обнаруживает, и как-то об этом сообщает (interrupt, assert, исключение, ...).
3. Если программисту реально нужна беззнаковая арифметика по модулю pow(2, n) (для вычисления hash-функций, генерации случайных чисел, и других редких задач), то для этого в языке должны быть специальные функции вроде:
Код:
// беззнаковая арифметика по модулю pow(2, sizeof(unsigned int) * CHAR_BIT)
unsigned int add_m(unsigned int a, unsigned int b);
unsigned int sub_m(unsigned int a, unsigned int b);
unsigned int mul_m(unsigned int a, unsigned int b);

// беззнаковая арифметика по модулю pow(2, sizeof(unsigned long long) * CHAR_BIT)
unsigned long long add_m(unsigned long long a, unsigned long long b);
unsigned long long sub_m(unsigned long long a, unsigned long long b);
unsigned long long mul_m(unsigned long long a, unsigned long long b);
Понятно, что вызов таких функций должен компилироваться в одну инструкцию на x86-64 (как с функциями sin, cos).

То, что в C беззнаковое переполнение является легальной ситуацией (не undefined behavior, то есть не считается runtime-ошибкой) -- это дефект C, унаследованный C++, а вы предлагаете этим дефектом пользоваться, как будто это что-то хорошее. Я уже привёл пример, что в новых языках C-шного уровня (Zig) этот дефект исправляют. Заодно это позволяет делать оптимизации, невозможные в C.
Цитата:
Сообщение от macomics Посмотреть сообщение
1) Вообще то в примере палитра хранится в видеоадаптере, а индексы все равно имеют размерности соответствующие текущей адресации. Т.е. пример составлен для 16-битного режима и индексы рассчитаны в переменной типа word.
Код:
    cmp cx,320 ; координаты выходят за границы экрана ?
    jnc pl1
    cmp dx,200
    jnc pl1
    push ax
    mov ax,320 ; di=320*y+x
    mul dx
    add ax,cx
т.к. длина видеобуфера = 64 кб, то используется без знаковый индекс для этого массива пикселей.
Я писал про индекс цвета в палитре (0 .. 255), а не про индекс пиксела в видеопамяти (0 .. 200 * 320 - 1).
Цитата:
Сообщение от macomics Посмотреть сообщение
А в C++ для индексации отлично подходит int.
Да.
Цитата:
Сообщение от p51x Посмотреть сообщение
А давйте что-то более свежее, без легаси и техдолга?..
Два серьёзных успешных проекта (Doom 3, Unreal Engine 4) от двух разных компаний -- вполне достаточно, чтобы опровергнуть сомнительное утверждение «Использование int для размеров и индексов - это некорректно». Да даже одного проекта было бы достаточно.
Цитата:
Сообщение от p51x Посмотреть сообщение
Мда.. Т.е. себя вы тоже программистом не считаете?
Я может выразился не очень понятно, мысль была такая:
* У реальных программистов нет времени писать книги.
* У реальных программистов может быть время писать сообщения на форумах (или заметки в blog-ах, комментарии).
Цитата:
Сообщение от p51x Посмотреть сообщение
Угу, и в ЮД они не нужны, и в файлах, и т.д.
Не знаю, что такое «ЮД», а для работы с файлами мне хватило бы чего-нибудь такого:
Код:
class file_t {
public:
  ...
  long long size() const; // 64-битное число
  long long pos() const;
  void set_pos(long long pos);
  void read(void* buf, int size);
  void write(const void* buf, int size);
  ...
};
Без size_t. Если например хочется прочитать данные в огромный буфер размером 5 гб, то придётся разбивать на несколько вызовов. Но даже если использовать size_t, в WinAPI это всё равно одним вызовом не сделать, потому что у WinAPI-шной функции ReadFile параметр размера -- типа DWORD (а не DWORD_PTR). То же самое с WinAPI-шной функцией WriteFile.

Когда в C++-коде мешанина из целочисленных типов, в одних местах int, в других местах unsigned int (или DWORD, привет WinAPI), в третьих местах size_t (привет STL) -- это плодородная почва для bug-ов (особенно учитывая, как в C++ сделана беззнаковая арифметика, а это грабли, унаследованные от C). Удобнее, когда по всему коду используется один default-ный «стандартный» целочисленный тип, и 32-битный int на эту роль вполне нормально подходит, поэтому некоторые серьёзные проекты идут по такому пути.
Пётр Седов вне форума Ответить с цитированием
Старый 15.03.2023, 03:15   #17
Steelcraft
Форумчанин
 
Регистрация: 13.03.2023
Сообщений: 106
По умолчанию

Есть готовое простое решение.

Макрос size_t offsetof(type, member), определенный в <stddef.h>, возвращает смещение поля member от начала структуры type. Если member не выровнен по границе байта (т.е., если это битовое поле), то компилятор вернёт ошибку.

См. стандарт ISO/IEC 9899:2018 (E), подраздел 7.19 Common defnitions <stddef.h>, п. 3, стр. 211.

Разность двух смещений найдете самостоятельно?
Steelcraft вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Передача структуры из программы на c# в dll на c++ и получение структуры из dll iukash Общие вопросы .NET 0 07.05.2013 16:03
Динамические структуры данных, списковые структуры (надо разобраться что делает программа) _4Alex4_ Помощь студентам 1 14.11.2012 07:39
Написание программ (древовидные структуры, структуры неспециального вида и т.д.) Green Gin Фриланс 2 27.04.2012 15:26
Управляющие структуры. Программирование алгоритмов разветвляющейся структуры. Лёнка Компоненты Delphi 1 23.04.2012 15:03
структуры в С++ серг Общие вопросы C/C++ 2 13.12.2009 15:08