О размерности данных в программах для микроконтроллеров

Для начала предисловие. Я не программист, и все знания приобрел в процессе решения технических задач. Поэтому для меня до сих пор банальные для других вещи становятся как открытие. В институте изучал Fortran 77, после появления персонального компьютера начал осваивать Си для AVR. Сейчас осваиваю STM32. Программы под Windows пробовал писать( пару примитивных по нужде, подгрядывая в интернете как это делается). В результате я понял, что мне больше подходит низкоуровневое программирование (когда понимаешь процессы, которые происходят). Для большего утоления информационного голода не хватает знаний как работают компиляторы. В сети практически нет информации об этом (или я не правильно поисковый запрос формировал). Теперь про суть.
Просматривая большинство примеров для STM32 можно обратить внимание, что есть типы переменных uint8_t, uint16_t, uint32_t (необходимо использовать), а есть unsigned char, unsigned int, unsigned long. Я использую второй тип записи потому что привык. Поэтому наступил на грабли, и сделал вывод, что для 8-битных микроконтроллеров char — 8бит, int 16бит, long 32бита. Для 32-х битных char — 8бит, short 16бит, int 32бита, long 64 бита. При написании программ для микроконтроллеров количество используемой памяти все таки ограничено. Поэтому используя примеры из интернета обращайте на это внимание. Это может проявиться при объявлении массивов, использовании операций сдвигов, масок и других логических операций, а также при преобразовании типов.

Комментарии (56)

RSS свернуть / развернуть
Настоятельно советую прочитать Кернигана и Ричи и руководство к вашему компилятору.
0
Вот не зря придумали типы данных с явным указанием размера, как то uint16_t итп.
+5
… советую почитать правила «MISRA C» (с хорошими примерами что будет если не следовать этим правилам), а еще лучше использовать статический анализатор кода (есть в IAR, CCS и т.д.)
0
и сделал вывод, что для 8-битных микроконтроллеров char — 8бит, int 16бит, long 32бита. Для 32-х битных char — 8бит, short 16бит, int 32бита, long 64 бита.
Так и есть. Продолжу: Для 8-битных: float — 32 бита, double — 32 бита. Для 32-х битных: float — 32 бита, double — 64 бита.
0
Для 16-ти битных DSP от TI вообще все еще веселее:

Взято отсюда
+2
Как правильно сказал coredumped, удобно использовать:
uint32_t, int32_t
uint16_t, int16_t
uint8_t, int8_t
и им подобные.
0
обратите внимание, что и это не панацея:
в TI при написании uint8_t резервируется память в 16 бит! Потому как минимальная ячейка памяти там — 16-ти битная. Поэтому если хочется компоновать данные — по 8 бит — придется попотеть масками и сдвигами.
+1
соответственно
uint8_t a = 0x80;
if(a<<1){}else{}

даст разный результат при сравнении
0
Если я правильно понял, то для данного семейства МК компилятор определяет char как 8-битный, хотя фактически на C2X, будет для хранения использоваться 16 бит.

Результат будет одинаков – 0, т. к. это оговорено стандартом. Компилятор для данной платформы будет создавать дополнительный код (например, наложит маску 0xFF после операции сдвига) чтобы соответствовать стандарту и скрыть особенности архитектуры. Аналогично, например, в ARM все регистры 32битные, и компилятору иногда приходится генерировать дополнительный код чтобы, например, «эмулировать» переполнение 8-битного uint8_t при сложении (хотя сама операция сложения выполняется в 32-битном регистре, и никакого «реального» переполнения не происходит).

Теоретически, может быть ситуация, когда для какого-то МК какой-то компилятор определяет char как 16 бит (я такого никогда не встречал, но теоретически размерность char может быть любой ). Тогда (насколько я помню стандарт) в stdint.h должно отсутствовать определение uint8_t.

Другими словами – стандарт требует, чтобы uint8_t, если он определен, вел себя как беззнаковая 8-битная переменная (не важно как она будет обрабатываться и хранится в памяти). Если это невозможно (особенности платформы/компилятора) – то данный тип не должен быть определен в stdint.h Это гарантирует что приведенный вами код будет работать везде одинаково, либо компилятор выдаст ошибку на этапе компиляции.
+1
улыбнуло) Вы когда-нибудь реально писали для 16-ти битных архитектур или у вас только в теории? На практике все совсем не так, к сожалению.
0
все дело в том, что переменная «a» — действительно принимается как 8-ми битная, а вот выражение (a<<1) восьмибитным быть не обязано и обычно соответствует разрядности архитектуры по умолчанию. Поэтому отсюда и разный результат.
видал я как в stdint.h от TI описан тип uint8_t:
#define uint8_t uint16_t
0
Вы когда-нибудь реально писали для 16-ти битных архитектур или у вас только в теории?

Если под 16-бит архитектурой вы имеете ввиду, что минимальный размер ячейки памяти 16-бит то не писал.

На практике все совсем не так, к сожалению.

а вот выражение (a<<1) восьмибитным быть не обязано и обычно соответствует разрядности архитектуры по умолчанию.

Данная ситуация четко описана в стандарте:

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type.

Если uint8_t определен как беззнаковый 8-бит, то результат должен быть по модулю 0x100, исключение там есть только для знаковых типов.

#define uint8_t uint16_t

А вот это очень странно… Такое определение есть в стандартном stdint.h который поставляется с компилятором?
+1
видал я как в stdint.h от TI описан тип uint8_t:

Специально нашел в стандарте место где это описано:

Conversely, for each type described herein that the implementation does not provide, <stdint.h> shall not declare that typedef name nor shall it define the associated macros. An implementation shall provide those types described as ‘‘required’’, but need not provide any of the others (described as ‘‘optional’’).
Тип uint8_t не является обязательным (в отличии например от uint_least8_t), и если компилятор не может реализовать его корректную поддержку то декларация должна отсутствовать.
+1
#define uint8_t uint16_t
Напомнило это

// Just before switching jobs:
// Add one of these.
// Preferably into the same commit where you do a large merge.
//
// This started as a tweet with a joke of "C++ pro-tip: #define private public",
// and then it quickly escalated into more and more evil suggestions.
// I've tried to capture interesting suggestions here.
//
// Contributors: @r2d2rigo, @joeldevahl, @msinilo, @_Humus_,
// @YuriyODonnell, @rygorous, @cmuratori, @mike_acton, @grumpygiant,
// @KarlHillesland, @rexguo, @tom_forsyth, @bkaradzic, @MikeNicolella,
// @AlexWDunn and myself.


// Easy keyword replacement. Too easy to detect I think!
#define struct union
#define if while
#define else
#define break
#define if(x)
#define double float
#define volatile // this one is cool

// I heard you like math
#define M_PI 3.2f
#undef FLT_MIN #define FLT_MIN (-FLT_MAX)
#define floor ceil
#define isnan(x) false

// Randomness based; "works" most of the time.
#define true ((__LINE__&15)!=15)
#define true ((rand()&15)!=15)
#define if(x) if ((x) && (rand() < RAND_MAX * 0.99))

// String/memory handling, probably can live undetected quite long!
#define strcpy(a,b) memmove(a,b,strlen(b)+2)
#define strcpy(a,b) (((a & 0xFF) == (b & 0xFF)) ? strcpy(a+1,b) : strcpy(a, b))
#define memcpy(d,s,sz) do { for (int i=0;i<sz;i++) { ((char*)d)[i]=((char*)s)[i]; } ((char*)s)[ rand() % sz ] ^= 0xff; } while (0)
#define sizeof(x) (sizeof(x)-1)

// Let's have some fun with threads & atomics.
#define pthread_mutex_lock(m) 0
#define InterlockedAdd(x,y) (*x+=y)

// What's wrong with you people?!
#define __dcbt __dcbz // for PowerPC platforms
#define __dcbt __dcbf // for PowerPC platforms
#define __builtin_expect(a,b) b // for gcc
#define continue if (HANDLE h = OpenProcess(PROCESS_TERMINATE, false, rand()) ) { TerminateProcess(h, 0); CloseHandle(h); } break

// Some for HLSL shaders:
#define row_major column_major
#define nointerpolation
#define branch flatten
#define any all
+1
Это я видел.

Мне запомнилась байка когда true был определён как-то типа (rand() % 50) и стоял комментарий
//меня уволили с работы, счастливой отладки, суки

:)
Это даже веселее чем
#define true ((__LINE__&15)!=15)


Вот по этому я и не верю, что ТІ позволяет себе определения
#define uint8_t uint16_t
Ибо это не просто несоответствие стандарту – это сознательная «подстава» которую будет очень тяжело выявить. Ведь сама идея введения подобных uint8_t типов — это именно гарантировать размерность, а не просто создать еще один синоним для типа с неопределенной размерностью. И приведенное в оригинале выражение не является undefined behavior.
+1
Вы имеете ввиду невыровненные данные или действительно 32-х битные ячейки. Или же речь идет только о регистрах, а не об оперативной памяти.
0
Я имел виду именно регистры процессора, выравнивание доступа – это другая история.

Хотя это тоже показательный пример и при автоматическом расположении переменных в памяти компилятор (линкер) должен учитывать особенности архитектуры касательно выравнивания доступа (правда, естественно, он не может гарантировать корректную работу, если программист явно манипулирует приведением указателей разных типов, хотя даже в таком случае он старается предупредить об опасности, выдав соответствующей warning).

Есть вещи, которые гарантируются стандартом, ну, например, что будет работать операция деления (хотя конкретный процессор может не иметь аппаратной поддержки деления). Есть вещи, когда стандарт нечетко определят требования (sizeof(char) <= sizeof(short) …), иногда стандарт вообще говорит, что компилятор может поступать на свое усмотрении (например сдвиг восьмибайтной переменной влево на 8 бит, это undefined behavior, и результат может быть любым).

Дык вот стандарт говорит, что если uint8_t определен – то это именно беззнаковый восьмибайтный тип. Не «около восьми» бит, а именно 8. Есть другие типы, например uint_least8_t – гарантирует, что он как минимум восьмибайтный, хотя его реальная размерность может быть любой, но не менее 8 бит.
+1
Реальная размерность данных зависит от конкретной архитектуры и может быть какой угодно, теоретически — даже некратной байту. С этим ничего не сделаешь. Но и именно с учётом этого спроектирована система типов в Си. Если хочешь написать гипотетический абсолютно переносимый код — можешь рассчитывать лишь на то, что, как указано в стандарте, sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long), и что типы будут иметь известную минимальную разрядность. Если же пишешь для конкретных архитектур — можешь опираться уже на конкретные размеры, указанных в описании компиляторов. Такой подход обеспечивает максимальную универсальность.
А всякие int16_t — дурацкое новомодное изобретение тех, кто не способен понять сишную идеологию.
0
У меня есть драйвер, который используется на компе под Linux x64, а также в проектах на ARM и AVR. Ну, расскажите мне, как используя стандартные типы и «сишную идеологию» собрать один и тот же исходник разными компиляторами под разные архитектуры.
0
Использовать typedef или stdint.h (что по сути одно и тоже). Иметь при этом в виду, что это всё равно решение для частных случаев, построенное поверх стандартных типов с учётом их реализации в конкретных компиляторах, а не универсальное.
0
А чем тогда uint16_t не устраивает? Это тоже typedef.
0
Тем, что его запихнули в стандарт.
0
прикол в том, что в 16-ти битной архитектуре sizeof() работает тоже по-другому. Например, sizeof(uint16_t) даст результат 1, sizeof(uint32_t) = 2, sizeof(float) = 2. Поскольку минимальная ячейка памяти там — 16 бит. Покажите мне, как вы универсально решите задачу с упакованной структурой?
typedef __packed struct
{
    uint8_t a;
    uint8_t b;
    uint16_t c;
    uint32_t d;
}Struct_Typedef
+1
Только структура с битовыми полями поможет.
0
это да… представляю какой это гемор.
0
особенно в том случае, когда уже имеется набор бинарных данных, который требуется распарсить.
0
Ну, если хотите унивесальности, то без гемора никак :( Особенно доставляет радость перенос кода с системы с другим байт-ордером.
0
да уж, мне это тоже знакомо)
0
прикол в том, что в 16-ти битной архитектуре sizeof() работает тоже по-другому.
Не надо обобщать. В компиляторе PICC для dsPIC33 sizeof возвращает размер в байтах.
0
вот и как с этим жить? Треш и угар…
0
Очень просто. Нужно сначала почитать документацию к компилятору и сделать соответствующие настройки, а не вводить людей в заблуждение.
+1
Никто никого в заблуждение не вводит. Да, я рассмотрел частный случай. Это как раз и говорит о том, что прежде чем писать тип переменной, не плохо бы разобраться что именно компилятор имеет ввиду.
0
А зачем? Кривые архитектуры со всякими 19-битными словами и биг эндианами нафиг не нужны. А универсальное решение — упаковщик/распаковщик данных типа перловского.
0
С sizeof не все так просто. Согласно стандарту:

When sizeof is applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1. When applied to an operand that has array type, the result is the total number of bytes in the array) When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding
0
Не нравится uint8_t — уберите #include <stdint.h> и пользуйтесь unsigned char.
И для остального тоже самое.
Надуманная проблема.
0
  • avatar
  • x893
  • 18 сентября 2015, 13:41
Можно и свои типы объявить. Чтоб не истерлись пальцы удобно определить типы u8,u16,s8,s16 итд. Идея «посмотрела» в линуксовых драйверах (с).
0
как раз uint8_t более соответствует размерности, чем char. Это собственно здесь и обсуждается. Однако универсального решения нет и быть не может. Причина в различии архитектуры. и если имеем к примеру архитектуру 16 бит, где минимальная ячейка памяти — 16 бит — то никак не получится иметь полную совместимость с типом char и uint8_t
Наверное это самый сильный косяк из всех, поскольку даже memcpy будет работать иначе, чем в 8-ми битном проце.
0
Не так давно тоже попался на том что тип Uint8, а занимает 16-ть бит в архитектуре серии C28xx, хотя знал об этой фишке давно. Использовал готовую функцию подсчёта CRC8 от TI, и обнаружился спустя какое-то время баг, что CRC8 неправильно считается при передаче ей колличество элементов более одного, в итоге начал смотреть тело функции, а там всё на указателях сделанно, соответственно корректно работает только там где Uint8 физически занимает 8-мь бит.
0
  • avatar
  • CHIP
  • 21 сентября 2015, 19:01
Я позволю себе высказать свое мнение касательно вопроса использования <stdint.h>

1. Зачем это нужно? Есть много задач, в которых нам нужно точно знать размерность переменной в битах. Нам ничего не мешает (зная особенности компилятора) создать свой синоним для типа. Но без стандартизации, мы получим, что каждый программист будет придумывать свое название для, например, без знаковой 16-битной переменной. В итоге получим, что в одном модуле этот тип называют u16, в другом — WORD, в третьем Uint16 (… uInt16, uSHORT …). Стандарт свел весь этот зоопарк названий к uint16_t

2. ОК, но ведь мы можем определить синоним uint16_t сами. Можем, но не должны. Если мы определим uint16_t как синоним int в каком-то «MyType.h», то это будет чудесно работать для AVR, но при попытке перенести код на ARM он скомпилируется, но будет работать не так как Вы ожидаете. Стандарт требует, чтобы компилятор (вернее окружение компилятора) сам корректно предоставлял (если это возможно) соответствующе синонимы типов.

3. Помимо типов с фиксированной размерностью <stdint.h> предоставляет другие синонимы. Например «быстрые типы» int_fastN_t. Это отельная тема, но этим стоит пользоваться в тех местах, где важна переносимость и, одновременно, производительность. Банальный пример – итераторы цикла.

Ну, и не следует забывать, что эти «типы» не являются серебряной пулей, их использование упрощает задачу переносимости кода, но только в рамках тех «гарантий» которые озвучены в стандарте. Это всего лишь синонимы, не стоит ждать от этого чего-то большего (например, при манипуляции с приведением указателей к другому типу).
+2
  • avatar
  • e_mc2
  • 21 сентября 2015, 23:19
А всё равно в каждой библиотеке свои типы — uLongf, GLuint, и т.д. Большая часть библиотек не юзает size_t, хотя он добавлен давным-давно.

Стдинт-овые типы не дают нужной универсальности на байтовых архитектурах (скажем, на МК для счётчика цикла используется байт, на компе — size_t и нужно вручную это обрабатывать), а небайтовые (которые вообще-то Си изначально прекрасно поддерживает) — вообще игнорит.
0
А всё равно в каждой библиотеке свои типы — uLongf, GLuint, и т.д

Да, такая проблема действительно есть, и ее практически нельзя решить ибо существует огромное множество legacy code.

Но я не пойму, Вшу позицию, что Вы предлагаете?

Не использовать синонимы вообще? Плохая идея. Наличие синонимов при портировании позволяет в одном месте заменить определение, а не лазить по всему коду заменяя (где это нужно!) например «unsigned int» на «unsigned short». Да и с точки самодокумектации выражение
uint16_t counter;

выглядит лучше чем
unsigned int counter; //Реализация рассчитывает, что это 16-битная переменная, на забывайте об этом при портировании


Далее, можно игнорировать стандарт и придумать свой синоним, но зачем? Чтобы потом другие гадали что я подразумеваю под UnSh и в каком файле объявлена декларация данного типа?

(скажем, на МК для счётчика цикла используется байт, на компе — size_t и нужно вручную это обрабатывать)
Здесь я не понял, что вы хотели сказать. Если вы знаете, что максимальное значение итератора укладывается в 8 бит, но не хотите явно задавать размерность т. к. это может быть не оптимально по скорости для другой архитектуры можете использовать тот-же uint_fast8_t, он розвернетеся в соответствующий тип для данной архитектуры.

а небайтовые (которые вообще-то Си изначально прекрасно поддерживает) — вообще игнорит.
Не совсем игнорит, но какие другие варианты Вы можете предложить, чтобы сохранить одновременно универсальность и производительность?
+1
Тайпдефы нужно использовать при необходимости. Сделать в проекте тип mycoollib_smallindex_t, который будет 8 бит на межке и size_t на компе.
А предлагать ничего и не нужно. Родная система базовых типов — лучшее что есть. Всё остальное менее универсально и менее производительно.
0
Тайпдефы нужно использовать при необходимости
С этим никто не спорит.
Сделать в проекте тип mycoollib_smallindex_t, который будет 8 бит на межке и size_t на компе.

Да, и каждый модуль будет определять свои синонимы для каждого типа, с непонятыми названиями, которые определены непонятно где, через множество #ifdef для разных архитектур. При этом нет четких стандартов и договоренностей по именованию.

Просто отличная идея…

Всяких там u8, uint8, BYTE, tByte, min_data_type_8bit, нам мало, давайте пусть каждый программист придумает свое название, каким-то образом его где-то декларирует… Зато каждый программист сможет проявить свою творческую сторону, придумав свое, оригинальное название синониму. А всякие там стандартизации и правила именований – это для слабаков :)
+3
Да обычная идея. Конечно, не u8 и не BYTE, а имя, состоящее из названия модуля и назначения типа. Так и делается в прилично написанных библиотеках.
Я понимаю, что во всяких модных оопах принято всё захардкодить. Отсюда и эти дурацкие и непригодные типы с фиксированной длиной. Также, как, скажем, в новомодных линупсах дают репу с набором хрен знает каких бинарников, хрен знает какой версии и конфигурации. И это считается лучше, чем просто брать в удобном месте нужную версию программы и запускать как удобно. По сути, источник всей этой гадости — капиталистическое желание приучить общество жрать что дают, а также привить злобу к любой возможности выбора.
0
Библиотеки часто вводят свои синонимы, чтобы повысить читаемость кода. Например, какая-то реализация файловой системы объявляет тип myfs_file_handle_t и это нормально, т. к. повышает читабельность, название типа подразумевает, что переменная содержит именно внутренний идентификатор файла.

Но мы с вами говорим о синонимах для типов с фиксированной (или минимальной) размерностью, которые есть в стандарте. Дык вот, если вместо использования стандартизованных названий каждая библиотека будет определять свои, например myfs_u8_t, то это плохо, т. к. мы утонем в куче разных синонимов, при том, что такой синоним никакой дополнительной полезной информации не несет по сравнению с общепринятым uint8_t …

Я понимаю, что во всяких модных оопах принято всё захардкодить…

Вы как всегда. Мы вроде обсуждаем типы из <stdint.h> и область их применения… Вот к чему Вы приплели сюда ООП, линуксовые репозитории и капиталистов?
+1
Минимальные размеры обеспечивает родная система типов, а фиксированные — только конкретная реализация. Я уже в который раз это повторяю.
Но главное не это. Гибкость — один из ключевых принципов языка. Система типов спроектирована гибкой и предполагалось, что такими же гибкими будут написанные программы. Тайпдеф также нужен не для уродований языка (вроде сокрытия различия между объявлениями скаляров, структур и массивов или бестолкового перименования типов), а для гибкости.
По идеалогии Си подразумевается отсутствие любого захардкоженного барахла. Язык подобен чистому листу, на котором можно изобразить то что нужно, не запинаясь ежеминутно о всякий понапиханный туда мусор (потому что его нет). Так вот, мало того, что использовали название для кучи говёных языков (С++ и пр.), которые ничего общего с основными принципами Си не имеют, а лишь манипулируют известным именем. Так ещё и тащат мусор из них, вроде эти захардкоженных типов.
0
Минимальные размеры обеспечивает родная система типов

Я имел ввиду семейство синонимов подобных int_leastN_t, а система встроенных типов как раз ничего кроме выполнения условий sizeof(char) <= sizeof(int) <= … не обеспечивает.

А на практике, мы в большинстве случаев имеем дело с величинами, которые имеют определенную размерность (диапазон значений) и нам нужно обеспечить корректную работу в этом диапазоне.

Давайте предположим, что нам нужно написать программу, которая считает котиков. Из ТЗ нам известно, что количество котиков лежит в диапазоне 0 … 100000. Теперь нам нужно выбрать тип для внутреннего счетчика котиков. Если забить на переносимость кода и сконцентрироваться на, например, х86, то можно объявить тип как unsigned и забить на переносимость. Но мы не хотим на переносимость забивать мыж эмбеддеры, и точно знаем что в unsigned на AVR все наши котики не поместятся.

Мое предложение: давайте объявим тип счетчика как uint32_t, и это нам гарантирует, что на любой (даже неизвестной нам на данный момент платформе) нам хватит размерности счетчика. (для ценителей оптимизации можно предложить тип uint_fast32_t). При этом наш код будет понятен другим программистам, ибо данный тип общеизвестен. Более того нам не нужно самостоятельно его объявлять, окружение компилятора само подставит нужный для данной платформы встроенный тип.

Ваше предложение (если я Вас правильно понял): давайте объявим синоним catlib_cat_caunter_t, потом сами через директивы условной компиляции будем определять соответствующий встроенный тип для конкретной платформы. Зачем это делать самому если существуют специально стандартизированные для этого средства? Более того, потом мы захотим измерять массу котика (а она, как известно лежит в диапазоне 1… 1000) и что опять вводим новый тип catlib_cat_mass_t? А если нам нужно будет потом посчитать суммарную массу всех котиков – опять новый тип? Как-то дофига типов получается Вам не кажется?

Итого: если мы не используем синонимы мы не можем писать переносимый код т. к. не можем гарантировать нужную нам по условиям размерность (я надеюсь вы не рассматриваете всерьез идею делать все переменные unsigned long long ?). Писать свой тип для каждого конкретного случая – мы утоним в куче нафиг не нужных деклараций, которые нам еще к тому-же придётся самим адаптировать под каждую платформу.

Так вот, мало того, что использовали название для кучи говёных языков (С++ и пр.

Опять, при чем здесь С++ и другие «говеные» языки, мы ведь вроде конкретный вопрос обсуждаем?
0
а система встроенных типов как раз ничего кроме выполнения условий sizeof(char) <= sizeof(int) <= … не обеспечивает.
Ну, строго говоря, она еще гарантирует минимальную разрядность типов (точнее, диапазон значений). Так что котики с гарантией влезут в long int.
Но, разумеется, типы фиксированной разрядности все равно нужны. Как в тех случаях, когда важна именно она (например, взаимодействие с другими системами), так и в тех случаях, когда счетчик лезет в байт, но хочется иметь максимум быстродействия и на 8-битке, и на не работающей с байтами 32-битке.
+1
  • avatar
  • Vga
  • 23 сентября 2015, 20:42
Да, тут я был неправ, забыв про гарантии диапазона и перестарался с аналогиями, пытаясь показать несовместимость типов на примере счетчика без переполнения.
0
Котикам абсолютно без надобности иметь фиксированный размер переменных. Стандартные гибкие типы unsigned long и unsigned int подойдут лучше всего. Зачем нам строго заданная разрядность? Нафига мучать компилятор дурацкими ограничениями, попутно создавая ненужные сущности? Кроме того, приведённые примеры — это прикладные типы, которые необходимо выносить в конфиг, и это будет соответствовать изначальной идеологии языка. Если понадобится переконфигурировать библиотеку, например, для работы на МК с ограниченным числом котиков — только поправить тайпдеф в хедере.

В блоках, работающих с буфером (строки, crc, шифрование, etc.) длина данных может быть вообще любым типом. На маленьком процессоре — байт или ворд. На компе — ворд (да, кто-то ещё пишет под дос), дворд или даже кворд. По умолчанию можно вписать unsigned int. Отлично подойдёт для большинства случаев — специально так спроектировано. Но если библиотека используется только для win32-проги — можно поправить на size_t, а если в МК для маленьких данных — то на unsigned char…

Авторы жабьего языка, например, спёрли названия типов, но не поняли смысла этих названий. В Си имена типов хорошо описывали гибкость типов, в жабьем же языке типы фиксированы и названия не к месту. На то он и жабий, впрочем.

Единственное место, где фиксированные типы необходимы — работа с внешними бинарными данными. Но тут если процессор и компилятор нормальные, никаких алиасов не требуется. А общем случае стдинт всё равно не поможет.
0
Касательно котиков – признаю свою ошибку, пример неправильный, я не подумав хотел показать несовместимость на конкретном типе (unsigned), хотя действительно можно все решить через базовый типы.

Но тут если процессор и компилятор нормальные, никаких алиасов не требуется. А общем случае стдинт всё равно не поможет.

Что значит «нормальные»?
0
Котикам абсолютно без надобности иметь фиксированный размер переменных. Стандартные гибкие типы unsigned long и unsigned int подойдут лучше всего.
Ничего не понял, а можно расшифровать для дебилоидов, и почему unsigned long и unsigned int гибкие?

P.S. Что-то я не замечал типов вида mycoollib_smallindex_t в твоем TCP/IP стеке — там все типы а-ля C99 stdint, ну и плюс char, где конкретно US-ASCII текстовые строки/массивы.
0
По компиляторам стоит почитать что-нибудь типа такого www.proklondike.com/books/thobshee/compiler2.html
+3
  • avatar
  • evsi
  • 22 сентября 2015, 10:32
Спасибо, это как раз то, что я хотел почитать.
0
КО на связи.
0
  • avatar
  • esp
  • 22 сентября 2015, 14:53
Хехх, завидую.
Вам предстоит еще много открытий.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.